本系列文中会有大量书中内容摘抄,都是个人认为很值得分享的内容。当然,也会有个人感悟,作为学习记录及简单分享。
认真对待每一个变量名。你当用为自己第一个孩子命名般的谨慎来给变量命名。 -- 序
名副其实
诚然选一个好名字是要花时间的,但是它省下来的时间会比花掉的多。注意命名,并且在有一个更好的名称的时候,就替换掉旧的,保持这样的习惯,读你代码的人(当然也包括自己)都会更开心。
通常,一个命名就应该包好了很多的含义,如果一个名称需要注释来补充,那这个命名就不是名副其实。
我们来看一个例子: let d; // elapsed time in days
变量名 d
没有什么含义,他没有引起读者对时间消逝的感觉,更别说以日计了。我们应该选择指明了计量对象和计量单位的名称。
let elapsedTimeInDays; let daysSinceCreation; let daysSinceMondification; let fileAgeIndays; 复制代码
选择体现本意的名称能让人更容易理解和修改代码,我们再来看一个例子:
function getThem(){ const list1 = []; for(let x of theList){ if(x[0] === 4) list1.push(x) } return list1; } 复制代码
上面这段代码并不复杂,代码格式也中规中矩,但是看完后一头雾水。
问题不在于代码的简洁度,而在于代码的模糊度:即上下文在代码中未被明确体现的程度。上述代码会给我们带来很多疑惑:
- theList 中的数据类型是什么样的?
- theList 下标 0 的意义是什么?
- 值 4 的含义是什么?
- 返回的列表有什么用?
问题的答案没有体现在代码段中,可是代码段就是它们该在的地方。
比如说我们开发一种扫雷游戏,我们发现盘面是名为 theList
的单元格列表,那就将其名称改为 gameBoard
。盘面上,每个单元格都用一个简单数组表示。我们还发现 0
下标条目是一种状态值,而该种状态值为 4
表示”已标记“。只要改为有意义的名称代码就会得到相应程度的改进:
function getFlaggedCells() { const flaggedCells = [] for (let cell of gameBoard) { if (cell['STATUS_VALUE'] === FLAGGED) flaggedCells.push(cell) } return flaggedCells } 复制代码
可以看到,代码的结构和逻辑并没有任何改变,但是通过更改命名,代码变得明确了很多。
我们还可以更进一步把 cell
封装成一个类,该类有一个函数 isFlagged
返回是否被标记,从而掩盖 FLAGGED
这个数字,修改后代码如下:
function getFlaggedCells() { const flaggedCells = [] for (let cell of gameBoard) { if (cell.isFlagged()) flaggedCells.push(cell) } return flaggedCells } 复制代码
可以看到,这个过程并不复杂,只是优化了变量的命名,抽离了一个类,但是代码的可读性增强了不止亿点点,这就是好的命名的力量。
做有意义的区分
话不多少,直接上例子:
function copyList(list1, list2) { for (let i = 0; i < list1.length; i++) { if (list1[i].checked) list2.push(list1[i]) } } 复制代码
其实在方法中这样命名还不是最恐怖的,我在项目中真的遇到过直接
list1,list2,list3
这样命名的,还没有注释~~~
接下来我们修改一下命名,这个函数就会像样很多。
function getCheckedList(source, destination) { for (let i = 0; i < source.length; i++) { if (source[i].checked) destination.push(source[i]) } } 复制代码
再比如下面的例子:
- name & nameString
- User & UserObject
- getAccount() & getAccounts() & getAccountInfo()
这种无意义的区分,别人根本不知道具体有什么区别,什么时候该用什么,所以命名的区分,要以读者能鉴别不同之处的方式来区分。
使用读的出来的名称
话不多少,直接上例子:
function genymdhms(){} function modymdhms(){} 复制代码
上面两个函数,单独看函数名不太可能明白它的功能是干什么,遇到这种情况,只能指望函数逻辑清晰,通过读函数逻辑了解这个函数的功能,但这显然是不对的,而如果修改为如下命名,则会清晰很多。
function generationTimestamp(){} function modificationTimestamp(){} 复制代码
使用可搜索的名称
现在的编辑器提供了很多功能,可以方便我们搜索指定的名称,还可以批量替换,但如果变量名称是 e,m,name
这种,我们空有这么强大的编辑器,依然对它们束手无策,因为可能会搜出来几十甚至几百个同名变量,所以命名应该能表示其具体含义,通常这样的命名,也便于搜索。
当然,短小的命名可以提高我们编码的速度,减少代码提交,但是名称的长短应与其作用域大小相对应。
在循环中定义变量 i
为循环下标固然没有问题;操作链表或者数组时,定义双指针名称为 pre,next
固然也没有问题。
有效的前缀
当我们在定义有动作的函数名时,加上对应动作的前缀会让函数名更清晰的表达其意图,例如:
function getName(){} function setAge(){} function isChecked(){} 复制代码
再比如我们可以给所有接口方法加上 api
前缀,这样就可以很容易区分普通函数和接口函数。
不要用双关语
我们要避免将同一个单词用于不同的目的,同一个术语用于不同的概念,就属于双关语了。
比如有一个方法 add
可以将传入的 Number
类型的参数进行求和,如果此时需要一个方法用于将某个满足条件的条目插入列表中,则不应该使用 add
,使用 insert
或者 append
会更好一些。
使用贴近业务的命名
如果不能用程序员熟悉的术语给手头的工作命名,那么就采用贴近业务的命名,因为通常负责维护代码的程序员也会去了解业务,哪怕他不知道具体的含义,也可以去请教产品经理和业务人员。而如果我们采用了其他的命名方式,他要去读懂我们的代码就会困难很多。
以上就是关于命名的分享了,认真对待每一次命名吧!😊