五个需要矫正的 JavaScript 编程陋习,你占了几个?

在阅读 JavaScript(JS)代码时,您是否有过这样的感觉:

你几乎完全不明白这条代码的作用?

这些代码使用了大量的 JS 技巧?

命名和编程风格相当随意?

这些是编程陋习的征兆。

在这篇文章中,我将会概述 JS 中 5 种常见的编程陋习。重要的是,我将提出我认为的,关于如何矫正这些陋习的可行的建议。

目录:

1). 不要使用隐式类型转换

JavaScript 是一种松散类型的程序语言。如果使用得当,这对我们是有利的,因为它提供了很大的灵活性。

大多数操作符 + - * / ==(但不是指 ===) 在处理不同类型的操作数时会进行隐式转换。 语句 if (condition) {…},(while(condition) {…} 隐式地将条件转换为布尔值。

下面的示例依赖于类型的隐式转换。我猜你一定感很到困惑:

五个需要矫正的 JavaScript 编程陋习,你占了几个?

过分依赖于隐式类型转换是一个陋习.

首先,它使你的代码在边缘情况下不够稳定。其次,增加了出现难以重现和修复的 bug 的机率。

现在咱们使用一个获取对象属性的函数。如果属性不存在,函数返回一个默认值:

五个需要矫正的 JavaScript 编程陋习,你占了几个?

getProp() 读取 name 属性的值,即为’Batman’。

那么如果我们试图访问 isVillian 属性呢:

五个需要矫正的 JavaScript 编程陋习,你占了几个?

这是一个错误。即使 hero 的属性 isVillian 为 false,函数 getProp() 也会错误得返回 true。

这是因为属性存在的验证依赖于 if (!object[propertyName]) {…} 隐式转换的布尔值。

这些错误很难发现,要修正该函数,就要明确验证值的类型:

五个需要矫正的 JavaScript 编程陋习,你占了几个?

object[propertyName] === undefined 确切地验证了属性是否为 undefined。

你们有什么其他方法来验证对象中的属性是否存在吗?如果有,请在下面的评论区留言!

边注:在第 4(此处需要一个超链接直接跳到第 4 部分)部分我有建议避免直接使用 undefined。因此,上述解决方案可以用运算符 in 进一步改进:

五个需要矫正的 JavaScript 编程陋习,你占了几个?

我的建议是: 尽可能不要使用隐式类型转换。相反,请确保变量和函数参数始终具有相同的类型,必要时使用显式类型转换。

大家最好这么做:

始终使用严格的相等运算符 === 进行比较

不要使用松散等式运算符 ==

加法运算符 operand1 + operand2:两个操作数应该是数字或字符串

算术运算符 - * / % **:两个操作数都应该是数字

if (condition) {…},while (condition) {…} 等语句中的 condition 必须是一个布尔类型值

大家可能会觉得这种方式需要编写更多代码…的确!但是通过明确的方法,我们能够控制代码的行为。此外,显性类型转换增强了可读性。

2). 不要使用早期的 JavaScript 技巧

JS 的有趣之处在于,它的创建者并没有料到这种语言会如此流行。 基于 JS 构建的应用程序的复杂性比语言发展的速度还要快。这种情况迫使开发人员使用 JS 技巧和变通方法,只是为了让事情正常运行。

一个典型的例子是查看数组是否包含某个元素。我从不会使用 array.indexOf(item) !== -1 来检查。

ECMAScript 2015 及以后版本的功能要强大得多,可以使用新的语言特性安全地重构许多技巧。

五个需要矫正的 JavaScript 编程陋习,你占了几个?

在新的 ES2015 中可以使用 array.includes(item) 来代替 array.indexOf(item) !== -1。

大家可以根据我写的重构列表来清除你的 JS 代码中的老旧技巧。

3). 不要污染函数作用域

在 ES2015 之前,JS 变量在函数作用域内,因此大家可能会习惯了将所有变量声明在函数作用域里面。

我们来看一个例子:

五个需要矫正的 JavaScript 编程陋习,你占了几个?

变量 index,item 和 length 在函数作用域内。但是这些变量会影响函数作用域,因为它们只在 for() 块作用域内才被需要。

因为已经引入了具有块作用域 let 和 const,我们应该尽可能地限制变量的生命周期。

让我们来清理一下函数作用域:

五个需要矫正的 JavaScript 编程陋习,你占了几个?

变量 index 和 item 被限制为 for() 循环块作用域。length 被挪到使用地方的附近。

重构后的代码更容易理解,因为变量不会分散在整个函数作用域内,它们存在于使用地方的附近。

在使用的块作用域定义变量:

if 块作用域

五个需要矫正的 JavaScript 编程陋习,你占了几个?

五个需要矫正的 JavaScript 编程陋习,你占了几个?

for 块作用域

五个需要矫正的 JavaScript 编程陋习,你占了几个?

五个需要矫正的 JavaScript 编程陋习,你占了几个?

4). 尽量避免 undefined 和 null

未赋值的变量默认被赋值为 undefined。例如

五个需要矫正的 JavaScript 编程陋习,你占了几个?

count 变量已定义,但尚未使用值初始化。JS 隐式赋值给它 undefined。

在我们访问不存在的属性 hero.city 时,也会返回 undefined。

为什么直接使用 undefined 是一个陋习呢?因为与 undefined 进行比较时,实际上我们正在处理的是未初始化状态的变量。

变量、对象属性和数组在使用前必须用值初始化

JS 提供了很多避免与 undefined 进行比较的方式。

判断属性是否存在

五个需要矫正的 JavaScript 编程陋习,你占了几个?

五个需要矫正的 JavaScript 编程陋习,你占了几个?

对象的默认属性

五个需要矫正的 JavaScript 编程陋习,你占了几个?

五个需要矫正的 JavaScript 编程陋习,你占了几个?

默认函数参数

五个需要矫正的 JavaScript 编程陋习,你占了几个?

五个需要矫正的 JavaScript 编程陋习,你占了几个?

null 是一个缺失对象的指示符。 我们应该尽量避免从函数返回 null,特别是使用 null 作为参数调用函数。

一旦 null 出现在调用堆栈中,我们就必须在每个可能访问 null 的函数中检查它的存在,这样我们一不小心就出错了。

五个需要矫正的 JavaScript 编程陋习,你占了几个?

大家尽量编写不涉及 null 的代码。可以用 try /catch 机制替代,默认对象的使用。

Algol 的发明者 Tony Hoare 曾说过: “我把它称为我的十几亿美元的错误…[…] 我设计了第一个面向对象语言的综合类型系统。[…] 但是我无法抗拒放入 null 引用的诱惑,因为它很容易实现。这导致了无数的错误、漏洞和系统崩溃,在过去四十年中,这些错误和崩溃可能造成了十亿美元的损失。”

“计算机科学中最糟糕的错误”一文深入分析了为什么 null 会破坏代码的质量。

5). 不要使用随意的编码风格,执行一个标准

有什么比阅读具有随机编码风格的代码更令人生畏的事情?你永远不知道会发生什么! 如果代码库包含许多开发人员的不同编码风格,该怎么办?,这种就像各色人物涂鸦墙。

五个需要矫正的 JavaScript 编程陋习,你占了几个?

整个团队和应用程序代码库都需要相同的编码风格,它提高了代码的可读性。一些有用的编码风格的例子:

爱彼迎 JS 风格指南

谷歌 JS 风格指南

老实说,我很懒。当我正面临着死线或者在回家前准备提交时,我可能会“忘记”设计代码的样式。

懒惰的我总对自己说:先这样吧,以后再更新它,但是 "以后" 意味着永远不会。

所以在这里我建议使用 eslint 来规范编码风格。 安装 eslint

使用最适合自己的编码风格去配置 eslint

设置一个预提交 hook,在提交之前运行 eslint 验证。

6). 总结

编写高质量和干净的代码需要有纪律性,我们克服编码陋习。

JS 是一种自由的编程语言,具有很大的灵活性。但是我们必须注意我们所使用的特性。这里我的建议是避免使用隐式类型转换 undefined 和 null。

现在这种编程语言发展得相当快。找出复杂的代码,并使用最新的 JS 特性来重构简化。 整个代码库的一致编码风格有益于可读性。 优秀的编程技能总是一个双赢的解决方案。