SIGN IN SIGN UP

现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。

0 0 0 HTML
# 忍者代码
2017-01-03 01:36:58 +03:00
2017-12-16 14:23:54 +03:00
2020-10-16 21:08:59 +08:00
```quote author="孔子《论语》"
学而不思则罔,思而不学则殆。
2017-12-16 14:23:54 +03:00
```
过去的程序员忍者使用这些技巧,来使代码维护者的头脑更加敏锐。
2017-11-24 18:42:33 +03:00
代码审查大师在测试任务中寻找它们。
2017-11-24 18:42:33 +03:00
一些新入门的开发者有时候甚至比忍者程序员能够更好地使用它们。
2017-01-03 01:36:58 +03:00
仔细阅读本文,找出你是谁 —— 一个忍者、一个新手、或者一个代码审查者?
2017-01-03 01:36:58 +03:00
2017-11-24 18:42:33 +03:00
```warn header="检测到讽刺意味"
许多人试图追随忍者的脚步。只有极少数成功了。
2017-03-24 17:28:37 +03:00
```
2017-11-24 18:42:33 +03:00
## 简洁是智慧的灵魂
2017-01-03 01:36:58 +03:00
把代码尽可能写得短。展示出你是多么的聪明啊。
2017-01-03 01:36:58 +03:00
在编程中,多使用一些巧妙的编程语言特性。
2017-01-03 01:36:58 +03:00
例如,看一下这个三元运算符 `'?'`
2017-01-03 01:36:58 +03:00
```js
// 从一个著名的 JavaScript 库中截取的代码
2017-01-03 01:36:58 +03:00
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
```
很酷,对吗?如果你这样写了,那些看到这一行代码并尝试去理解 `i` 的值是什么的开发者们,就会有一个“快活的”的时光了。然后会来找你寻求答案。
2017-01-03 01:36:58 +03:00
告诉他短一点总是更好的。引导他进入忍者之路。
2017-01-03 01:36:58 +03:00
## 一个字母的变量
2017-01-03 01:36:58 +03:00
```quote author="老子(道德经)"
道隐无名。夫唯道善贷且成。
2017-01-03 01:36:58 +03:00
```
另一个缩减代码量的方法是,到处使用单字母的变量名。例如 `a`、`b` 或 `c`。
2017-01-03 01:36:58 +03:00
短变量就像森林中真正的忍者一样,一下就找不到了。没有人能够通过编辑器的“搜索”功能找到它。即使有人做到了,他也不能“破译”出变量名 `a` 或 `b` 到底是什么意思。
2017-01-03 01:36:58 +03:00
……但是有一个例外情况。一个真正的忍者绝不会在 `"for"` 循环中使用 `i` 作为计数器。在任何地方都可以,但是这里不会用。你随便一找,就能找到很多不寻常的字母。例如 `x` 或 `y`。
使用一个不寻常的变量多酷啊,尤其是在长达 1-2 页(如果可以的话,你可以写得更长)的循环体中使用的时候。如果某人要研究循环内部实现的时候,他就很难很快地找出变量 `x` 其实是循环计数器啦。
2017-01-03 01:36:58 +03:00
## 使用缩写
2017-01-03 01:36:58 +03:00
如果团队规则中禁止使用一个字母和模糊的命名 — 那就缩短命名,使用缩写吧。
2017-01-03 01:36:58 +03:00
像这样:
2017-01-03 01:36:58 +03:00
- `list` -> `lst`
- `userAgent` -> `ua`
- `browser` -> `brsr`
- ……等
2017-01-03 01:36:58 +03:00
只有具有真正良好直觉的人,才能够理解这样的命名。尽可能缩短一切。只有真正有价值的人,才能够维护这种代码的开发。
2017-01-03 01:36:58 +03:00
## Soar high抽象化。
2017-01-03 01:36:58 +03:00
```quote author="老子(道德经)"
大方无隅,<br>
大器晚成,<br>
大音希声,<br>
大象无形。
2017-01-03 01:36:58 +03:00
```
当选择一个名字时,尽可能尝试使用最抽象的词语。例如 `obj`、`data`、`value`、`item` 和 `elem` 等。
2017-01-03 01:36:58 +03:00
- **一个变量的理想名称是 `data`。** 在任何能用的地方都使用它。的确,每个变量都持有 **数据data**,对吧?
2017-01-03 01:36:58 +03:00
……但是 `data` 已经用过了怎么办?可以尝试一下 `value`,它也很普遍。毕竟,一个变量总会有一个 **值value**,对吧?
2017-01-03 01:36:58 +03:00
- **根据变量的类型为变量命名:`str`、`num`……**
2017-01-03 01:36:58 +03:00
尝试一下吧。新手可能会诧异 — 这些名字对于忍者来说真的有用吗?事实上,有用的!
2017-01-03 01:36:58 +03:00
一方面,变量名仍然有着一些含义。它说明了变量内是什么:一个字符串、一个数字或是其他的东西。但是当一个局外人试图理解代码时,他会惊讶地发现实际上没有任何有效信息!最终就无法修改你精心思考过的代码。
2017-01-03 01:36:58 +03:00
我们可以通过代码调试,很容易地看出值的类型。但是变量名的含义呢?它存了哪一个字符串或数字?
2017-12-17 00:04:46 +03:00
如果思考的深度不够,是没有办法搞明白的。
2017-01-03 01:36:58 +03:00
- **……但是如果找不到更多这样的名字呢?** 可以加一个数字:`data1, item2, elem5`……
2017-01-03 01:36:58 +03:00
## 注意测试
2017-01-03 01:36:58 +03:00
只有一个真正细心的程序员才能理解你的代码。但是怎么检验呢?
2017-01-03 01:36:58 +03:00
**方式之一 —— 使用相似的变量名,像 `date` 和 `data`。**
2017-01-03 01:36:58 +03:00
尽你所能地将它们混合在一起。
2017-01-03 01:36:58 +03:00
想快速阅读这种代码是不可能的。并且如果有一个错别字时……额……我们卡在这儿好长时间了,到饭点了 (⊙v⊙)。
2017-01-03 01:36:58 +03:00
## 智能同义词
2017-01-03 01:36:58 +03:00
2020-10-16 21:08:59 +08:00
```quote author="老子《道德经》"
道,可道,非常道。名,可名,非常名。
2017-01-03 01:36:58 +03:00
```
对 **同一个** 东西使用 **类似** 的命名,可以使生活更有趣,并且能够展现你的创造力。
2017-01-03 01:36:58 +03:00
例如,函数前缀。如果一个函数的功能是在屏幕上展示一个消息 — 名称可以以 `display…` 开头,例如 `displayMessage`。如果另一个函数展示别的东西,比如一个用户名,名称可以以 `show…` 开始(例如 `showName`)。
2017-01-03 01:36:58 +03:00
暗示这些函数之间有微妙的差异,实际上并没有。
2017-01-03 01:36:58 +03:00
与团队中的其他忍者们达成一个协议:如果张三在他的代码中以 `display...` 来开始一个“显示”函数,那么李四可以用 `render..`,王二可以使用 `paint...`。你可以发现代码变得多么地有趣多样呀。
2017-01-03 01:36:58 +03:00
……现在是帽子戏法!
2017-01-03 01:36:58 +03:00
对于有非常重要的差异的两个函数 — 使用相同的前缀。
2017-01-03 01:36:58 +03:00
例如,`printPage(page)` 函数会使用一个打印机printer。`printText(text)` 函数会将文字显示到屏幕上。让一个不熟悉的读者来思考一下:“名字为 `printMessage(message)` 的函数会将消息放到哪里呢?打印机还是屏幕上?”。为了让代码真正耀眼,`printMessage(message)` 应该将消息输出到新窗口中!
2017-01-03 01:36:58 +03:00
## 重用名字
2017-01-03 01:36:58 +03:00
```quote author="老子(道德经)"
始制有名,<br>
名亦既有,<br>
夫亦将知止,<br>
知止可以不殆。
2017-01-03 01:36:58 +03:00
```
仅在绝对必要时才添加新变量。
2017-01-03 01:36:58 +03:00
否则,重用已经存在的名字。直接把新值写进变量即可。
2017-01-03 01:36:58 +03:00
在一个函数中,尝试仅使用作为参数传递的变量。
2017-01-03 01:36:58 +03:00
这样就很难确定这个变量的值现在是什么了。也不知道它是从哪里来的。目的是提高阅读代码的人的直觉和记忆力。一个直觉较弱的人必须逐行分析代码,跟踪每个代码分支中的更改。
2017-01-03 01:36:58 +03:00
**这个方法的一个进阶方案是,在循环或函数中偷偷地替换掉它的值。**
2017-03-24 17:28:37 +03:00
例如:
2017-01-03 01:36:58 +03:00
```js
function ninjaFunction(elem) {
// 基于变量 elem 进行工作的 20 行代码
2017-01-03 01:36:58 +03:00
elem = clone(elem);
// 又 20 行代码,现在使用的是 clone 后的 elem 变量。
2017-01-03 01:36:58 +03:00
}
```
想要在后半部分中使用 `elem` 的程序员会感到很诧异……只有在调试期间,检查代码之后,他才会发现他正在使用克隆过的变量!
2017-01-03 01:36:58 +03:00
经常看到这样的代码,即使对经验丰富的忍者来说也是致命的。
2017-01-03 01:36:58 +03:00
## 下划线的乐趣
2017-01-03 01:36:58 +03:00
2020-10-15 15:45:31 +08:00
在变量名前加上下划线 `_` 和 `__`。例如 `_name` 和 `__value`。如果只有你知道它们的含义,那就非常棒了。或者,加这些下划线只是为了好玩儿,没有任何含义,那就更棒了!
2017-01-03 01:36:58 +03:00
加下划线可谓是一箭双雕。首先,代码变得更长,可读性更低;并且,你的开发者小伙伴可能会花费很长时间,来弄清楚下划线是什么意思。
2017-01-03 01:36:58 +03:00
聪明的忍者会在代码的一个地方使用下划线,然后在其他地方刻意避免使用它们。这会使代码变得更加脆弱,并提高了代码未来出现错误的可能性。
2017-01-03 01:36:58 +03:00
## 展示你的爱
2017-01-03 01:36:58 +03:00
向大家展现一下你那丰富的情感!像 `superElement`、`megaFrame` 和 `niceItem` 这样的名字一定会启发读者。
事实上,从一方面来说,看似写了一些东西:`super..`、`mega..`、`nice..`。但从另一方面来说 — 并没有提供任何细节。阅读代码的人可能需要耗费一到两个小时的带薪工作时间,冥思苦想来寻找一个隐藏的含义。
2017-01-03 01:36:58 +03:00
## 重叠外部变量
2017-01-03 01:36:58 +03:00
```quote author="关尹子"
处明者不见暗中一物,<br>
处暗者能见明中区事。
2017-01-03 01:36:58 +03:00
```
对函数内部和外部的变量,使用相同的名称。很简单,不用费劲想新的名称。
2017-01-03 01:36:58 +03:00
```js
let *!*user*/!* = authenticateUser();
function render() {
let *!*user*/!* = anotherValue();
...
...许多行代码...
2017-01-03 01:36:58 +03:00
...
... // <-- 某个程序员想要在这里使用 user 变量……
2017-01-03 01:36:58 +03:00
...
}
```
在研究 `render` 内部代码的程序员可能不会注意到,有一个内部变量 `user` 屏蔽了外部的 `user` 变量。
2017-01-03 01:36:58 +03:00
然后他会假设 `user` 仍然是外部的变量然后使用它,`authenticateUser()` 的结果……陷阱出来啦!你好呀,调试器……
2017-01-03 01:36:58 +03:00
## 无处不在的副作用!
2017-01-03 01:36:58 +03:00
2020-10-15 15:45:31 +08:00
有些函数看起来它们不会改变任何东西。例如 `isReady()``checkPermission()``findTags()`……它们被假定用于执行计算、查找和返回数据,而不会更改任何它们自身之外的数据。这被称为“无副作用”。
**一个非常惊喜的技巧就是,除了主要任务之外,给它们添加一个“有用的”行为。**
2017-01-03 01:36:58 +03:00
当你的同事看到被命名为 `is..`、`check..` 或 `find...` 的函数改变了某些东西的时候,他脸上肯定是一脸懵逼的表情 — 这会扩大你的理性界限。
2017-01-03 01:36:58 +03:00
**另一个惊喜的方式是,返回非标准的结果。**
2017-01-03 01:36:58 +03:00
展示你原来的想法!让调用 `checkPermission` 时的返回值不是 `true/false`,而是一个包含检查结果的复杂对象。
2017-01-03 01:36:58 +03:00
那些尝试写 `if (checkPermission(..))` 的开发者,会很疑惑为什么它不能工作。告诉他们:“去读文档吧”。然后给出这篇文章。
2017-01-03 01:36:58 +03:00
## 强大的函数!
2017-01-03 01:36:58 +03:00
```quote author="老子(道德经)"
大道泛兮,<br>
其左可右。
2017-03-24 17:28:37 +03:00
```
2017-01-03 01:36:58 +03:00
不要让函数受限于名字中写的内容。拓宽一些。
2017-01-03 01:36:58 +03:00
例如,函数 `validateEmail(email)` 可以(除了检查邮件的正确性之外)显示一个错误消息并要求重新输入邮件。
2017-01-03 01:36:58 +03:00
额外的行为在函数名称中不应该很明显。一个真正的忍者会使它们在代码中也不明显。
2017-01-03 01:36:58 +03:00
**将多个行为合并到一起,可以保护你的代码不被重用。**
2017-01-03 01:36:58 +03:00
想象一下,另一个开发者只想检查邮箱而不想输出任何信息。你的函数 `validateEmail(email)` 对他而言就不合适啦。所以他不会找你问关于这些函数的任何事而打断你的思考。
2017-01-03 01:36:58 +03:00
## 总结
2017-01-03 01:36:58 +03:00
上面的所有“建议”都是从真实的代码中提炼而来的……有时候,这些代码是由有经验的开发者写的。也许比你更有经验 ;
2017-01-03 01:36:58 +03:00
- 遵从其中的一丢丢,你的代码就会变得充满惊喜。
- 遵从其中的一大部分,你的代码将真正成为你的代码,没有人会想改变它。
- 遵从所有,你的代码将成为寻求启发的年轻开发者的宝贵案例。