let a = height * width;
let area = height * width;

动机

好的命名是整洁编程的核心。变量可以很好地解释一段程序在干什么——如果变量名起得好的话。但我经常会把名字起错——有时是因为想得不够仔细,有时是因为我对问题的理解加深了,还有时是因为程序的用途随着用户的需求改变了。

使用范围越广,名字的好坏就越重要。只在一行的 lambda 表达式中使用的变量,跟踪起来很容易——像这样的变量,我经常只用一个字母命名,因为变量的用途在这个上下文中很清晰。同理,短函数的参数名也常常很简单。不过在 JavaScript 这样的动态类型语言中,我喜欢把类型信息也放进名字里(于是变量名可能叫 aCustomer)。

对于作用域超出一次函数调用的字段,则需要更用心命名。这是我最花心思的地方。

机制

  • 如果变量被广泛使用,考虑运用封装变量(132)将其封装起来。
  • 找出所有使用该变量的代码,逐一修改。

Tip

如果在另一个代码库中使用了该变量,这就是一个“已发布变量”(published variable),此时不能进行这个重构。

如果变量值从不修改,可以将其复制到一个新名字之下,然后逐一修改使用代码,每次修改后执行测试。

  • 测试。

范例

如果要改名的变量只作用于一个函数(临时变量或者参数),对其改名是最简单的。这种情况太简单,根本不需要范例:找到变量的所有引用,修改过来就行。完成修改之后,我会执行测试,确保没有破坏什么东西。

如果变量的作用域不止于单个函数,问题就会出现。代码库的各处可能有很多地方使用它:

let tpHd = "untitled";

有些地方是在读取变量值:

result += `<h1>${tpHd}</h1>`;

另一些地方则更新它的值:

tpHd = obj["articleTitle"];

对于这种情况,我通常的反应是运用封装变量(132):

result += `<h1>${title()}</h1>`;
 
setTitle(obj["articleTitle"]);
 
function title() {
  return tpHd;
}
function setTitle(arg) {
  tpHd = arg;
}

现在就可以给变量改名:

let _title = "untitled";
 
function title() {
  return _title;
}
function setTitle(arg) {
  _title = arg;
}

我可以继续重构下去,将包装函数内联回去,这样所有的调用者就变回直接使用变量的状态。不过我很少这样做。如果这个变量被广泛使用,以至于我感到需要先做封装才敢改名,那就有必要保持这个状态,将变量封装在函数后面。

Tip

如果我确实想内联,在重构过程中,我就会将取值函数命名为 getTitle,并且其中的变量名也不会以下划线开头。

给常量改名

如果我想改名的是一个常量(或者在客户端看来就像是常量的元素),我可以复制这个常量,这样既不需要封装,又可以逐步完成改名。假如原来的变量声明是这样:

const cpyNm = "Acme Gooseberries";

改名的第一步是复制这个常量:

const companyName = "Acme Gooseberries";
const cpyNm = companyName;

有了这个副本,我就可以逐一修改引用旧常量的代码,使其引用新的常量。全部修改完成后,我会删掉旧的常量。我喜欢先声明新的常量名,然后把新常量复制给旧的名字。这样最后删除旧名字时会稍微容易一点,如果测试失败,再把旧常量放回来也稍微容易一点。

这个做法不仅适用于常量,也同样适用于客户端只能读取的变量(例如 JavaScript 模块中导出的变量)。