本文详细阐述了 JavaScript 项目开发中的关键编码规范,涵盖代码压缩兼容性、IDE 格式化、文档注释、变量命名、语法简洁性、DOM 解耦、类型一致性、JSON 标准及代码优化等核心领域。通过具体正反例对比,帮助开发者提升代码可读性、可维护性及团队协作效率,避免常见陷阱。
引言与背景
在过往的开发经历中,我曾与不遵守编码规范的同事合作,过程并不愉快。撰写本文旨在分享经验,为未来的团队协作提供借鉴,同时也欢迎前辈们指正其中的不足。以下列出的规范主要针对常见的编码陋习,部分行业通用规范将不再赘述。
基础编码规范:压缩、格式化与注释
1、确保代码压缩后无错误
大型 JavaScript 项目在发布时通常会对所有 JS 文件进行压缩,例如使用 Google Closure Compiler Service(新版 jQuery 已采用此工具)。压缩过程会移除注释、空格、换行,并将长变量名替换为短无意义的名称,以加快下载速度、减少流量消耗并提供一定的代码保护。为确保压缩后代码正常运行,通常要求语句以分号结尾,大括号严格闭合等,具体需遵循压缩工具的要求。若初期不遵循标准,后期排查压缩错误将耗费大量时间。

2、确保代码能通过特定 IDE 的自动格式化功能
完善的开发工具(如 Aptana Studio)通常具备“自动格式化”功能,可统一换行、缩进和空格等排版。你可以自定义格式标准,例如左大括号是否另起一行。这样做的好处是团队成员可以使用 IDE 将你的代码格式化为他们习惯的风格进行阅读。当你的代码作为通用方法被其他模块调用时,阅读源码能比简单的 API 文档更清晰地展示方法调用和实现细节。

3、使用标准的文档注释
这是基本要求,有利于在调用方法时查看参数提示,也可利用配套文档工具(如 jsdoc-toolkit)生成 HTML 或其他格式的开发文档供团队成员阅读。若你的 API 面向开放平台(如 Facebook 应用),文档将面向所有开发者。完整的注释不仅方便团队成员理解参数和实现细节,也便于日后维护,即使项目庞大,多年后回顾代码也能清晰理解当时的逻辑。

变量命名与语法简洁性
4、使用规范且有意义的变量名
规范且有意义的变量名能提高代码可读性。在大项目中,代码量巨大,规范命名不仅便于他人阅读,也方便自己日后查看,如系统升级或新增功能时,修改代码会更轻松。若日后连自己写的代码都看不懂,那将是极大的讽刺。
此外,变量名应尽量遵循标准。例如,`var me = this` 可能不如 `var self = this` 好,因为 `self` 是 Python 中的关键字,通常对应其他语言中的 `this`。再看以下例子,加 `s` 显然比不加更科学,表明该变量存储的是复数(可能是数组):
var li = document.getElementsByTagName('li') var lis = document.getElementsByTagName('li')

5、避免使用生僻语法
JavaScript 作为动态脚本语言,灵活性既是优点也是缺点。不同层次的开发者对同一功能的实现可能在规范或语法上存在较大差异。无论如何,规范编码、避免炫技、不将简单问题复杂化、不违反易读性原则是开发者应遵循的准则。
例如,语句 `typeof(b) == 'string' && alert(b)` 应改为:`if (typeof(b) == 'string') alert(b)`。前一种写法利用了 `&&` 运算符的短路机制:若前语句返回 false,则不检测后语句。虽然这在代码优化中提及将最可能出现的情况先判断,但如果条件较多且语句较长,代码可读性会极差。
又如:`+function(a){var p = a;}('a')` 应改为:`(function(a){var p = a;})( 'a')`。`function` 前的 `+` 号与包裹 `function` 的括号作用相同,均用于提高运算优先级。后者是常见且易懂的防止变量污染的做法,许多流行的 JavaScript 框架均采用此方式。
再举一个降低可读性的例子:`function getPostionTxt(type){return type == 2 ? "野外" : (type == 3 ? "商城" : (type == 4 ? "副本" : null));}` 应改为:`function getPostionTxt(type){var typeData={"2":"野外","3":"商城","4":"副本"};if (typeData[type]) return typeData[type]; else return null;}`。若 `type` 是从 0 开始的不间断整数,直接使用数组会更简单。这种写法清晰明了,而多层三元表达式嵌套令人头晕。
语言使用与参数定义规范
6、不在语句非赋值处使用中文
语句中出现中文通常被认为不符合行业标准,尽管这不影响程序运行,但我们并非使用“易语言”进行开发。虽然我不愿提及此问题,但确实有人这样做,可能是因为英语能力不足,至少可用拼音或翻译工具辅助。例如以下写法在教学场景中尚可理解:
this.user['名字'] = '张三' 或者 this.user.名字 = '张三'
7、明确定义函数固定数量的参数
固定数量参数的函数内部不应使用 `arguments` 获取参数,因为这样会导致方法包含较多脚本时,无法一眼看出该方法接受哪些参数及参数个数。例如: var $ = function(){return document.getElementById(arguments[0]);} 应改为:var $ = function(elemID){return document.getElementById(elemID);}
8、不必热衷动态事件绑定
虽然知道事件可以动态绑定(如使用 `addEventListener` 或 jQuery 的 `bind` 方法),且动态绑定可使 XHTML 更干净,但一般情况下仍建议直接在 DOM 节点上绑定事件。我认为这样使代码更易维护,因为查看源代码时即可清楚知道哪个 Element 绑定了什么方法,即更容易知晓按钮或链接点击时调用了什么脚本。

DOM 交互与事件处理
9、降低代码与 XHTML 的耦合性
不要过度依赖 DOM 的某些特征来调用不同的脚本代码,而应定义不同功能的方法,然后在 DOM 上调用。这样无论 DOM 是按钮还是链接,方法调用方式一致。例如以下实现存在问题:
function myBtnClick(obj) { if (/确定/.test(obj.innerHTML)) alert('OK'); else if (/取消/.test(obj.innerHTML)) alert('Cancel'); else alert('Other'); }
<a herf="javascript:;" onclick="myBtnClick(this)">确定</a><a herf="javascript:;" onclick="myBtnClick(this)">取消</a>
上述例子在一个函数内处理了两件事,应拆分为两个函数。如将链接改为按钮:`<input type="button" onclick="myBtnClick(this)" value="确定" />`,则 `myBtnClick` 函数内部的 `obj.innerHTML` 会出错,因为此时应使用 `obj.value`。此外,若按钮名称由中文改为英文,也会出现问题。因此,这种做法存在诸多缺陷。
数据类型与 JSON 标准
10、一个函数应返回统一的数据类型
由于 JavaScript 是弱类型语言,编写函数时有些人对返回类型的处理较为随意。我认为应像强类型语言那样返回,参考以下两个例子:
function getUserName(userID) { if (data[userID]) return data[userID]; else return false; }
应改为:
function getUserName(userID) { if (data[userID]) return data[userID]; else return ""; }
若此方法在 C# 中定义,我们知道其准备返回的数据类型应为字符串,因此未找到数据时应返回空字符串,而非布尔值或其他不合适类型。这并不影响函数未来的调用,因为返回的空字符串在逻辑判断中可被视为“非”,即与 `false` 相同,除非使用全等于 `===` 或 `typeof` 进行判断。
11、规范定义 JSON 对象,补全双引号
使用标准肯定有益,但为何有人不使用?可能是懒惰或习惯使然。或许有人辩称少写引号可减小文件体积,这有道理但非重点。对于服务器返回的 JSON 数据,使用标准结构可利用 Firefox 浏览器的 JSONView 插件方便查看(类似查看 XML 的树形显示)。此外,若使用 jQuery 开发,jQuery 1.4+ 版本对 JSON 格式有更高要求,具体可查阅 jQuery 更新文档。例如:`{name:"Tom"}` 或 `{'name':'Tom'}` 都应改为 `{"name":"Tom"}`。

代码维护与团队协作
12、不在文件中保留未来确定不再使用的代码片段
当代码调整或重构后,之前编写的不再使用的代码应及时删除。若认为这些代码仍有利用价值,可将其剪切到临时文件中。留在项目中不仅增加文件体积,还会对团队成员甚至自己造成干扰,担心日后回顾代码时不清楚该方法的作用或使用情况。当然,可使用文档注释标签 `@deprecated` 将方法标识为不推荐。
13、不重复定义其他团队成员已实现的方法
在大型项目中,通常部分开发成员实现一些通用方法,而其他成员需熟悉这些通用方法,并在编写模块需要调用时直接使用,而非如某些开发者喜欢“单干”,不阅读通用方法文档而在自己代码中重新实现。这不仅产生多余代码量,还影响团队开发效率,缺乏团队合作精神,是重复造轮子的悲剧。
例如,在通用类文件 Common.js 中定义了 `function $(elemID){return document.getElementById(elemID)}`,则不应在 Mail.js 中再次出现该功能函数的重复定义。对于复杂方法,更应如此。
方法选择与数据缓存
14、调用合适的方法
当有多个方法可实现同类功能时,应根据场景选择最合适的方法。以下以 jQuery 框架的两个 AJAX 方法为例说明。若确定服务器返回的数据是 JSON,应直接使用 `$.getJSON`,而非使用 `$.get` 获取数据再用 `eval` 函数转换为 JSON 对象。若因本次请求需传输大量数据而不得不使用 `$.post`,也应采用指定返回数据类型(设置 `dataType` 参数)的做法。若使用 `$.getJSON`,在代码中可一眼看出本次请求服务器返回的是 JSON。
温馨提示:jQuery 1.4 后,若服务器设置了数据输出的 ContentType,例如 ASP.NET C# 设置 `Response.ContentType = "application/json"`,则 `$.get` 与 `$.getJSON` 的使用无明显区别。
15、使用合适的控件存储合适的数据
曾发现有人利用 DIV 保存 JSON 数据,以待页面下载后将来使用,例如:`<div id="json">{ "name":"Tom"}</div>`。显然此 DIV 并非用于界面显示,若非要这样做以达到使用 HTML 文件进行数据缓存的目的,至少改为使用隐藏域存储该数据更合理,例如:`<input type="hidden" value=" { "name":"Tom"}" />`。
其实也可利用 `window` 对象保存一些数据,如上例,可在 AJAX 请求页直接包含如下脚本块:`<script>window.userData = { "name":"Tom"};</script>`,当在 AJAX 请求回调函数中执行完 `$( "#MyDiv ").html(data)` 后,`window` 上即刻拥有该变量。若采用第一种方法,将不可避免执行 `eval(document.getElementById("UserData").innerHTML)`。若在 `window` 对象存放大量数据,这些数据不用时要及时手动清理,因为它们需等待浏览器刷新或重启后才会消失,这将增加内存开销。
代码优化与面向对象设计
16、永远不要忽略代码优化工作
代码最优化是每位程序员应努力达到的目标,也应成为程序员永恒的追求。编写代码时,不应急于实现功能,而应思考如何编写代码,以提高执行效率。
举例:假设有定义 `getElementById` 的快捷方法 `functoin $(elemID){return document.getElementById(elemID)}`,有人可能会写出如下代码:`$("MyDiv").parentNode.removeChild($("MyDiv"))`。此处执行了两次 `getElementById` DOM 查找,若改为:`var myDiv = $("MyDiv"); myDiv.parentNode.removeChild(myDiv)` 则更佳。尽管 `getElementById` 的 DOM 查找相对较快,若换成 `getElementsByTagName` 则更应注重优化。jQuery 开发团队也提醒大家注意此问题。
此外,代码优化技巧需个人不断积累。曾有朋友称其编写网站后台代码从不考虑优化,因其网站使用至强四核服务器,我认为此观点十分可笑。
17、会分析策划文档,能用面向对象方法进行接口定义和代码组织
此能力对每位程序员至关重要,也是决定程序员水平高低的重要因素。能够将需求细化并抽象出不同类,然后有条理地编写代码,使代码结构清晰、可读性高、易于维护,不至于过于过程化且杂乱无章,这才算是一名优秀的程序员。
作者:WebFlash