12条写出高质量JS代码的方法

作者:代码拾荒

时间:2026-10-22

浏览:383

本文总结了12条编写高质量JavaScript代码的关键方法。涵盖可维护性原则、全局变量管理、变量声明规范、循环优化、原型扩展限制、类型转换、eval()使用禁忌及编码规范等核心话题。通过遵循这些实践,开发者能显著提升代码的可读性、执行效率及安全性,降低维护成本。

文章目录

引言

编写高质量的JavaScript代码不仅能提升代码的可读性,还能有效提高程序的运行效率。以下是经过整理的12条实用建议:

可维护性代码的编写原则

一、如何编写可维护的代码

当Bug出现时,若能立即修复,此时解决问题的思路在脑海中最为清晰。然而,如果转而处理其他任务,或者Bug是在一段时间后才被发现,开发者可能已经遗忘了特定代码的逻辑。此时再回头查看代码,就需要付出额外的努力:

1. 花费时间去学习和理解该问题;2. 花费时间去了解需要修复的代码部分。

此外,在大项目或公司环境中,修复Bug的人往往不是原代码的编写者(甚至发现者与修复者也不是同一人)。因此,降低理解代码所需的时间至关重要,无论是自己之前编写的代码还是团队成员的代码。这直接关系到公司的营收底线和开发人员的幸福感,因为我们应当将精力投入到开发令人兴奋的新功能上,而不是耗费数小时甚至数天去维护遗留代码。因此,创建可维护的代码至关重要,其一般原则包括以下几个方面:

可读性

一致性

可预测性

代码风格应看起来像是由同一个人编写的

已记录

全局变量的风险与管理

二、全局变量的问题

全局变量的主要问题在于,JavaScript应用程序和Web页面上的所有代码都共享这些全局变量,它们位于同一个全局命名空间中。因此,当程序的不同部分定义了同名但作用域不同的全局变量时,命名冲突在所难免。Web页面中经常包含非开发者编写的代码,例如:

第三方的JavaScript库

广告方的脚本代码

第三方用户跟踪和分析脚本代码

不同类型的小组件、标志和按钮

例如,如果第三方脚本定义了一个名为A的全局变量,而你的函数中也定义了一个名为A的全局变量,结果将是后面的变量覆盖前面的变量,导致第三方脚本立即失效!而且这种错误很难调试。

因此,尽可能少地使用全局变量非常重要,例如使用命名空间模式或立即执行函数(IIFE)。但减少全局变量最关键的做法始终是始终使用var来声明变量。

变量声明与全局对象访问

三、忘记var作用的副作用

隐式全局变量和明确定义的全局变量之间存在细微差异,主要体现在通过delete操作符使变量未定义的能力上。具体如下:

通过var创建的全局变量(在任何函数之外的程序中创建)是不能被删除的。

没有通过var创建的隐式全局变量(无论是否在函数中创建)是能被删除的。

因此,隐式全局变量并不是真正的全局变量,但它们作为全局对象的属性存在。属性可以通过delete操作符删除,而变量则不能,具体代码此处省略。

四、访问全局对象

在浏览器环境中,全局对象可以通过window属性在代码的任何位置访问(除非你做了些比较出格的事情,比如声明了一个名为window的局部变量)。但在其他环境中,这个方便的属性可能被称为其他名称(甚至在程序中不可用)。如果你需要在没有硬编码window标识符的情况下访问全局对象,你可以在任何层级的函数作用域中执行以下操作:


var global = (function () { return this; }());

循环优化与原型扩展限制

五、for循环

在for循环中,你可以遍历数组或类数组对象(如arguments和HTMLCollection对象)。通常的循环形式如下:

// 次佳的循环 for (var i = 0; i < myarray.length; i++) { // 使用myarray[i]做点什么 }

这种循环形式的不足之处在于,每次循环时都需要获取数组的长度。这会降低代码性能,尤其是当myarray不是数组而是HTMLCollection对象时。

六、不扩展内置原型

扩展构造函数的prototype属性是增加功能的一种强大方法,但有时它过于强大。扩展内置构造函数(如Object()、Array()或Function())的原型虽然诱人,但这会严重降低代码的可维护性,因为这使得你的代码变得难以预测。使用你代码的其他开发人员可能更期望内置的JavaScript方法持续正常工作,而不是你添加的方法。此外,将属性添加到原型中,如果在循环中不使用hasOwnProperty进行检查,可能会导致这些属性被枚举出来,从而造成混乱。

类型转换与eval()的使用禁忌

七、避免隐式类型转换

JavaScript在比较变量时会进行隐式类型转换。这就是为什么诸如false == 0或“” == 0返回true的原因。为避免引起混乱的隐式类型转换,在比较值和表达式类型时,应始终使用===和!==操作符。

var zero = 0; if (zero === false) { // 不执行,因为zero为0, 而不是false } // 反面示例 if (zero == false) { // 执行了... }

八、避免eval()

如果你的代码中使用了eval(),请记住这句咒语“eval()是魔鬼”。该方法接受任意字符串并将其作为JavaScript代码处理。当问题代码是事先已知的(而非运行时确定)时,没有理由使用eval()。如果代码是在运行时动态生成的,有更好的方法无需使用eval()即可达到相同目标。例如,使用方括号表示法来访问动态属性会更简单且更好:

// 反面示例 var property = "name"; alert(eval("obj." + property)); // 更好的 var property = "name"; alert(obj[property]);

使用eval()还带来了安全隐患,因为执行的代码(例如来自网络)可能已被篡改。这是一个常见的反面教材,特别是在处理Ajax请求得到的JSON响应时。在这些情况下,最好使用JavaScript内置方法来解析JSON响应,以确保安全和有效。若浏览器不支持JSON.parse(),你可以使用来自JSON.org的库。

同样重要的是要记住,给setInterval()、setTimeout()和Function()构造函数传递字符串,在大多数情况下,与使用eval()是类似的,因此应避免。在幕后,JavaScript仍需评估和执行你传递给程序的字符串:

// 反面示例 setTimeout("myFunc()", 1000); setTimeout("myFunc(1, 2, 3)", 1000); // 更好的 setTimeout(myFunc, 1000); setTimeout(function () { myFunc(1, 2, 3); }, 1000);

使用新的Function()构造类似于eval(),应谨慎使用。这可能是一个强大的构造,但往往被误用。如果你绝对必须使用eval(),你可以考虑使用new Function()代替。有一个小的潜在好处,因为在新Function()中代码评估是在局部函数作用域中运行,所以代码中任何通过var定义的变量都不会自动变成全局变量。另一种防止自动全局变量的方法是将eval()调用封装到一个即时函数中。

考虑下面这个例子,这里仅un作为全局变量污染了命名空间。


console.log(typeof un); // "undefined" console.log(typeof deux); // "undefined" console.log(typeof trois); // "undefined" var jsstring = "var un = 1; console.log(un);"; eval(jsstring); // logs "1" jsstring = "var deux = 2; console.log(deux);"; new Function(jsstring)(); // logs "2" jsstring = "var trois = 3; console.log(trois);"; (function () { eval(jsstring); }()); // logs "3" console.log(typeof un); // number console.log(typeof deux); // "undefined" console.log(typeof trois); // "undefined"

eval()和Function构造的另一区别是eval()可以干扰作用域链,而Function()更安分守己。无论你在哪里执行Function(),它只看到全局作用域。因此,它能很好地避免本地变量污染。在下面这个例子中,eval()可以访问和修改其外部作用域中的变量,这是Function做不到的(注意到使用Function和new Function是相同的)。


(function () { var local = 1; eval("local = 3; console.log(local)"); // logs "3" console.log(local); // logs "3" }()); (function () { var local = 1; Function("console.log(typeof local);")(); // logs undefined

编码规范与代码格式

九、编码规范

建立和遵循编码规范非常重要,这能让你的代码保持一致性、可预测性,更易于阅读和理解。新加入团队的开发者可以通过阅读规范,理解其他团队成员编写的代码,从而更快地上手工作。

十、缩进

没有缩进的代码基本上无法阅读。最糟糕的情况是不一致的缩进,因为它看起来似乎遵循了规范,但过程中可能伴随着混乱和意外。重要的是要规范地使用缩进。

十一、注释

你必须注释你的代码,即使只有你自己会接触它。通常,当你深入研究一个问题时,你会清楚地知道这段代码是做什么用的;但是,当你一周后再回头看时,想必也要耗费不少脑细胞去搞明白它到底是如何工作的。

显然,注释不能走极端:不要对每个单独的变量或单独一行进行注释。但是,你通常应该记录所有的函数、它们的参数和返回值,以及任何不寻常的技术和方法。要想到注释可以为代码未来的阅读者提供诸多提示;阅读者需要的是(不要读太多的东西)仅通过注释和函数属性名来理解你的代码。例如,当你有五六行程序执行特定任务时,如果你提供了一行代码目的以及为什么在这里的描述,阅读者就可以直接跳过这段细节。没有硬性规定注释代码的比例,代码的某些部分(如正则表达式)可能注释要比代码多。

十二、花括号{}

花括号(亦称大括号,下同)应始终被使用,即使在它们为可选的时候。技术上讲,在if或for中如果语句仅一条,花括号是不需要的,但你还是应该始终使用它们,这会让代码更具持续性和易于更新。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章