2020 年 9 月 5 日

忍者代码

学而不思则罔,思而不学则殆。

孔子(《论语》)

过去的程序员忍者使用这些技巧来磨练代码维护人员的头脑。

代码审查大师在测试任务中寻找它们。

新手开发者有时比程序员忍者使用得更好。

仔细阅读它们,找出你是谁——忍者、新手,还是代码审查员?

检测到讽刺

许多人试图遵循忍者之路。很少有人成功。

简洁是智慧的灵魂

尽可能缩短代码。展示你的聪明才智。

让微妙的语言特性指导你。

例如,看看这个三元运算符 '?'

// taken from a well-known javascript library
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

酷,对吧?如果你这样写,一个遇到这行代码并试图理解i的值的开发者会过得很开心。然后来找你,寻求答案。

告诉他们,越短越好。引导他们走上忍者的道路。

单字母变量

道藏于无言。只有道才开端良好,完成良好。

老子(道德经)

另一种缩短代码的方法是在所有地方使用单字母变量名。比如abc

一个短变量会像真正的忍者消失在代码中,就像森林中的忍者一样。没有人能够使用编辑器的“搜索”功能找到它。即使有人找到了,他们也无法“破译”ab名称的含义。

…但有一个例外。真正的忍者绝不会在"for"循环中使用i作为计数器。任何地方都可以,但这里不行。环顾四周,还有更多异域字母。例如,xy

如果循环体占用了 1-2 页(如果可以,可以更长),那么一个异域变量作为循环计数器会特别酷。然后,如果有人深入研究循环,他们将无法快速弄清楚名为x的变量是循环计数器。

使用缩写

如果团队规则禁止使用单字母和模糊名称,请缩短它们,创建缩写。

像这样

  • listlst
  • userAgentua
  • browserbrsr
  • …等等

只有真正具有良好直觉的人才能理解这样的名称。尝试缩短所有内容。只有值得的人才能维护你的代码开发。

高瞻远瞩。抽象化。

大方形无角
大器晚成
大音希声
大象无形

老子(道德经)

在选择名称时,尝试使用最抽象的单词。比如objdatavalueitemelem等。

  • 变量的理想名称是data在任何可能的地方使用它。事实上,每个变量都保存数据,对吧?

    …但是如果data已经被使用了怎么办?尝试value,它也是通用的。毕竟,变量最终会得到一个

  • 按类型命名变量:strnum...

    试一试。年轻的初学者可能会想——这样的名字对忍者来说真的有用吗?当然有用!

    当然,变量名仍然表示某种含义。它说明了变量内部的内容:字符串、数字或其他内容。但是当外人尝试理解代码时,他们会惊讶地发现实际上没有任何信息!最终无法更改你经过深思熟虑的代码。

    可以通过调试轻松找出值类型。但是变量的含义是什么?它存储哪个字符串/数字?

    如果没有良好的冥想,根本无法弄清楚!

  • ...但是如果不再有这样的名字呢?只需添加一个数字:data1、item2、elem5...

注意力测试

只有真正细心的程序员才能理解你的代码。但如何检查这一点?

其中一种方法——使用类似的变量名,例如datedata

在可以的地方混合它们。

快速阅读这样的代码变得不可能。当出现错别字时...嗯...我们卡住了,是时候喝茶了。

聪明的同义词

可言说的道,不是永恒的道。可名之名,不是永恒的名。

老子(道德经)

相同的事物使用类似的名称,让生活更有趣,向公众展示你的创造力。

例如,考虑函数前缀。如果函数在屏幕上显示消息,则以display...开头,例如displayMessage。然后,如果另一个函数在屏幕上显示其他内容,例如用户名,则以show...开头(例如showName)。

暗示此类函数之间存在细微差别,而实际上没有。

与团队中的忍者伙伴达成协议:如果 John 在他的代码中使用display...“显示”函数,那么 Peter 就可以使用render..,而 Ann 使用paint...。注意代码变得更加有趣和多样。

...现在是帽子戏法!

对于具有重要差异的两个函数——使用相同的前缀!

例如,函数printPage(page)将使用打印机。函数printText(text)会将文本放在屏幕上。让不熟悉的读者仔细考虑类似命名的函数printMessage:“它将消息放在哪里?是打印机还是屏幕上?”。为了使其真正闪耀,printMessage(message)应该在新的窗口中输出它!

重用名称

整体一旦被分割,部分
需要名称。
名称已经足够多。
必须知道何时停止。

老子(道德经)

仅在绝对必要时添加新变量。

相反,重复使用现有名称。只需向其中写入新值即可。

尝试在函数中仅使用作为参数传递的变量。

这将使识别变量现在中确切内容变得非常困难。而且还不知道它来自哪里。目的是培养阅读代码的人员的直觉和记忆力。直觉较弱的人员必须逐行分析代码并跟踪每个代码分支中的更改。

该方法的高级变体是在循环或函数的中间用类似的东西秘密地 (!) 替换该值。

例如

function ninjaFunction(elem) {
  // 20 lines of code working with elem

  elem = clone(elem);

  // 20 more lines, now working with the clone of the elem!
}

一位希望在函数的后半部分使用elem的程序员会感到惊讶……只有在调试期间,在检查代码后,他们才会发现他们正在使用克隆!

在代码中经常看到。即使是对经验丰富的忍者,也是致命有效的。

下划线用于娱乐

在变量名前面加上下划线___。如_name__value。如果您知道它们的含义,那就太好了。或者,更好的是,仅为了娱乐而添加它们,而没有任何特定含义。或者在不同的地方有不同的含义。

一石二鸟。首先,代码变得更长且更难读,其次,其他开发人员可能需要花费很长时间来弄清楚下划线是什么意思。

聪明的忍者在代码的一个位置放置下划线,而在其他位置避开它们。这使得代码更加脆弱,并增加了将来出错的可能性。

表达你的爱

让每个人都看到你的实体有多么宏伟!诸如superElementmegaFrameniceItem之类的名称肯定会启发读者。

确实,一方面,写着一些内容:super..mega..nice..但另一方面 - 这没有带来任何细节。读者可能会决定寻找隐藏的含义,并冥想一两个小时的带薪工作时间。

重叠外部变量

在光线下,在黑暗中什么也看不见。
在黑暗中,在光线下可以看到一切。

关尹子

在函数内部和外部使用变量的相同名称。很简单。无需努力发明新名称。

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ...many lines...
  ...
  ... // <-- a programmer wants to work with user here and...
  ...
}

一个跳进render的程序员可能会没有注意到有一个局部user遮蔽了外部user

然后他们会尝试使用user,假设它是外部变量,是authenticateUser()的结果……陷阱启动!你好,调试器……

到处都是副作用!

有一些函数看起来好像不会改变任何东西。比如isReady()checkPermission()findTags()……它们被假定执行计算,查找并返回数据,而不会改变它们之外的任何东西。换句话说,没有“副作用”。

一个非常漂亮的技巧是除了主任务之外,还向它们添加一个“有用的”动作。

当你的同事看到一个名为is..check..find...的函数改变了一些东西时,他们脸上茫然惊讶的表情——肯定会拓宽你的推理界限。

另一个惊喜的方法是返回一个非标准结果。

展示你的原创思维!让checkPermission的调用返回的不是true/false,而是包含检查结果的复杂对象。

那些尝试编写if (checkPermission(..))的开发人员会想知道为什么它不起作用。告诉他们:“阅读文档!”并提供这篇文章。

强大的函数!

伟大的道无处不在,
向左向右。

老子(道德经)

不要被函数名称中写的内容限制住。要更宽泛。

例如,一个函数validateEmail(email)可以(除了检查电子邮件是否正确)显示一条错误消息并要求重新输入电子邮件。

函数名称不应该明显看出有其他动作。真正的忍者编码员会让它们在代码中也不明显。

将几个动作合并到一个动作中可以保护你的代码不被重用。

想象一下,另一个开发人员只想检查电子邮件,而不想输出任何消息。你的函数validateEmail(email)同时执行这两个操作,不适合他们。因此,他们不会通过询问任何有关它的问题来打破你的冥想。

总结

以上所有“建议”都来自真实代码……有时,由经验丰富的开发人员编写。甚至可能比你更有经验 ;)

  • 遵循其中一些,你的代码将充满惊喜。
  • 遵循其中许多,你的代码将真正属于你,没人会想更改它。
  • 遵循所有,你的代码将成为寻求启迪的年轻开发者的宝贵课程。
教程地图

评论

在评论之前阅读此内容…
  • 如果你有改进建议 - 请 提交 GitHub 问题 或提交拉取请求,而不是评论。
  • 如果你无法理解文章中的某些内容 - 请详细说明。
  • 若要插入几行代码,请使用 <code> 标记,对于多行 - 将其包装在 <pre> 标记中,对于超过 10 行 - 使用沙盒 (plnkrjsbincodepen…)