学而不思则罔,思而不学则殆。@孔子
过去的程序员忍者使用这些技巧来使代码维护者的头脑更加敏锐。
代码审查大师在测试任务中寻找它们。
一些新入门的开发者有时候甚至比忍者程序员更好的使用它们。
仔细阅读它们,找出你是谁 —— 一个忍者、一个新手、或者一个代码审查者?
检测到讽刺意味,许多人试图追随忍者的脚步。只有极少数成功了。
让代码尽可能地短一点。展示出你是多么的聪明啊。
让一些巧妙的语言特性来指导你。
例如,看一下这个三元运算符 '?':
// 从一个著名的 javascript 库中取到的代码
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
很酷,对吗?如果你这样写了,那些看到这一行代码并尝试去理解 i 的值是什么的开发者们就会有一个“快活的”的时光了。然后会来找你寻求答案。
告诉他短一点总是更好的。引导他进入忍者之路。
道隐无名。夫唯道善贷且成。@老子(道德经)
编码更快(也更糟糕)的另一种方式是到处使用单字母的变量名。像是 a、b 或 c。
短变量会像森林中真正的忍者一样在代码中消失不见。没有人能够通过编辑器的“搜索”找到它。即使有人做到了,他也不能“破译”出变量名 a 或 b 是什么意思。
…但是有一个例外情况。一个真正的忍者绝不会在 "for" 循环中使用 i 作为计数器。在任何地方都可以,但是这里不会用。看一下四周吧,还有很多不常用的字母呢。例如 x 或 y。
如果循环体能够达到 1-2 页(如果可以的话可以让它更长)那么长的话,使用一个不常用的变量作为循环的计数器就更酷了。如果某人看到循环内部的深处后,他就不能很快地找出变量 x 是循环计数器啦。
如果团队规则中禁止使用一个字母和模糊的名字,那就缩短他们,使用缩写吧。
像这样:
list → lst
userAgent → ua
browser → brsr
…等等
只有具有真正良好直觉的人才能够理解所有的这些名字。尽量缩短一切。只有一个有价值的人才能够维护这种代码的开发。
大方无隅,大器晚成,大音希声,大象无形。@老子(道德经)
当选择一个名字时尽可能尝试使用最抽象的词语。例如 obj、data、value、item、elem 等等。
一个变量的理想名称是 data。在任何能用的地方都使用它。的确,每个变量都持有 data,对吧?
…但是 data 已经用过了怎么办?可以尝试一下 value,它也很普遍呢。一个变量总会有一个 value,对吧?
根据变量的类型命名:str、num…
尝试一下吧。新手可能会诧异 —— 这些名字对于忍者来说真的有用吗?事实上,是的!
一方面,变量名仍然有着一些含义。它说明了变量内是什么:一个字符串、一个数字或是其他的东西。但是当一个局外人试图理解代码时,他会惊讶地发现实际上没有任何有效信息!最终无法改变你精心思考过的代码。
事实上,值的类型很容易就能通过调试看出来。但是变量名的含义呢?它存了哪一个字符串/数字?
如果不深思是没有办法找出来的!
…但是如果找不到更多这样的名字呢?可以加一个数字:item1、item2、elem5、data1…
只有一个真正细心的程序员才能理解代码。但是怎么检验呢?
方式之一 —— 使用相似的变量名,像 date 和 data。
尽你所能地将它们混合在一起。
快速阅读这些代码是不可能的。并且如果有一个错别字时… (⊙v⊙)嗯…我们卡在这里很久没有喝茶了。
最难的事情是在黑暗的房间里找到一只黑猫,特别是如果没有猫。@孔子
对于同样的事情使用相同的名字,可以使生活更有趣,并向公众展示出你的创意。
例如,函数前缀。如果一个函数是在屏幕上展示一个消息 —— 可以以 display… 开始,例如 displayMessage。如果另一个函数展示别的东西,比如一个用户名,可以以 show… 开始(例如 showName)。
暗示这些函数之间有微妙的差异,实际上并没有。
与团队中的其他忍者们达成一个协议:如果 John 在他的代码中以 display... 来开始一个"显示"函数,那么 Peter 可以用 render..,Ann 可以使用 paint...。你可以发现代码变得多么的有趣多样呀。
现在是帽子戏法!
对于有非常重要的差异的两个函数使用相同的前缀。
例如,printPage(page) 函数会使用一个打印机。printText(text)函数会将文字显示到屏幕上。让一个陌生的读者来思考一下:“printMessage(message)会将消息放到哪里呢?打印机还是屏幕上?”,为了使它真正耀眼,printMessage(message)` 应该将消息输出在新窗口中!
始制有名,名亦既有,夫亦将知止,知止可以不殆。@老子(道德经)
仅在绝对必要时才添加新变量。
否则,重用已经存在的名字。只需要将新值写进变量即可。
在一个函数中,尝试仅使用作为参数传递的变量。
这样就无法确定这个变量现在是什么了。也不知道它是从哪里来的。一个弱直觉的人必须逐行分析代码,并通过每个代码分支跟踪变化。
这个方法的一个进阶方案是在一个循环或函数中偷偷地替换掉它的值。
例如:
function ninjaFunction(elem) {
// 基于变量 elem 进行工作的 20 行
elem = clone(elem);
// 又 20 行,现在是使用 clone 后的 elem 变量。
}
想要在第二部分中使用 elem 的程序员会非常的诧异滴…只有在调试期间,在检查代码之后,他会发现他正在使用克隆过的变量!
经常看到这样的代码,即使对经验丰富的忍者来说也是致命的。
在变量名前使用 _ 和 _。例如 _name 和 _value。如果只有你知道他们的含义的话将会非常棒。或者,更棒的是,其实没有意义。
你一枪杀死了两只兔子。首先,代码变得更长降低了可读性;第二,你的开发者小伙伴可能会花费很长时间来弄清楚下划线是什么意思。
一个聪明的忍者会在代码的一个地方使用下划线然后在其他地方刻意避免使用它们。这会使得代码变得更加脆弱,并增加未来出现错误的可能性。
让大家看看你的实体是多么壮观!像 superElement、megaFrame 和 niceItem 这样的名字一定会启发读者。
事实上,从一方面来说,看似写了一些东西:super..、mega..、nice..,但是从另一方面来说 —— 并没有提供任何细节。读者可能要寻找一个隐藏的含义或深思一两个小时。
处明者不见暗中一物, 处暗者能见明中区事。@关尹子
对函数内外的变量使用相同的名称。很简单,一点也不费劲。
let user = authenticateUser();
function render() {
let user = anotherValue();
...
...many lines...
...
... // <-- 某个程序员想要在这里使用 user 变量...
...
}
跳过 render 的程序员可能不会注意到有一个本地 user 遮挡外部的 user 了。
然后他会假设 user 仍然是外部的变量然后使用它,authenticateUser() 的结果… 陷阱出来啦!你好呀,调试器…
有些函数看起来它们不会改变任何东西。例如 isReady()、checkPermission()、findTags()… 它们被假定为会执行计算,查找和返回数据,而不需要更改任何外部的数据。这被称为“无副作用”。
一个非常好的技巧 – 除了主要任务之外,还要向它们添加一个“有用的”动作
当你的同事看到被命名为 is..、check.. 或 find... 的函数改变了某些东西的时候,他的脸上肯定是一脸懵逼的状态。
另一种惊喜的方式是返回非标准的结果。
展示你原来的想法!让调用 checkPermission 时返回的不是 true/false,而是一个包含检查结果的复杂对象。
那些尝试写 if (checkPermission(..)) 的开发者会怀疑为什么它不能工作。告诉他们:“去读文档吧”。然后给出这篇文章。
大道泛兮,其左可右。@老子(道德经)
不要让函数受限于名字中写的那样。变得更宽泛一点吧。
例如,函数 validateEmail(email) 可以(除了检查邮件的正确性之外)显示一个错误消息并要求重新输入邮件。
额外的动作在函数名称中不应该很明显。一个真正的忍者会使它们在代码中也不明显。
将多个动作加入到一起可以保护您的代码避免重用。
想象一下,另一个开发者只想检查邮箱而不想输出任何信息。你的函数 validateEmail(email) 对他而言就不合适啦。所以他不会找你问一些关于这些函数的事情从而打断你的思考。
上面的所有“建议”都是从真实的代码中提炼而来… 有时,这些代码是由有经验的开发人员写的。也许比你更有经验 ;)
遵从其中的一丢丢,你的代码就会变得充满惊喜。
遵从其中的一大部分,你的代码将真正成为你的代码,没有人会想改变它。
遵从所有,你的代码将成为寻求启发的年轻开发人员的宝贵案例。
关于本文
原文:https://zh.javascript.info/ninja-code#jian-jie-shi-zhi-hui-de-ling-hun
好文 推 荐
☞
☞
☞