JavaScript 规范

来源:互联网 发布:网络视频歌曲大全 编辑:程序博客网 时间:2024/05/22 17:04

前言:

代码规范有助于减少团队(个人)的维护成本和方便日后的自动化改进


一、开发规范

1、注释

函数级注释是必须的,关键语句注释可选的。

必须有注释,模块级API标准注释与关键语句辅助说明注释。

以/** 开头的注释将由工具(jsdoc等)生成API查询文档。

良好的注释降低团队其他成员与(日后的自己)阅读,理解,维护成本。

函数注释必备4参数:作用描述,办法名, 参数,返回值

其他选项添加在@return后面,避免不必要的选项:如修改时间,旧版注释,旧版作者,过去的修改人


行注释以 // 开始,写于语句开始上一行。

js 中不需要在注释中声明数据类型

// 队列记录器,全局级

var data= [];


文件开始注释

视该js文件功能做说明。组件库文件则是组件基本信息描述。如果有特殊的声明,如特别的在本文件内部约定的命名方式或内部全局变量指定方式都直接作出说明。


2、命名规范

变量, 属性名,函数,函数的参数,使用 驼峰式 命名法。

var createObject = {};

function getElementWidth(retrueData){};

var bIsPhone = true;// 匈牙利命名法(推荐)

b代表数据类型

 

私有[属性、变量、方法]以下划线 _ 开头。

var _createObject = {};

function _getElementWidth(retrueData){};

   objetc._test = {};

 

常量, 使用全部字母大写,单词间下划线分隔的命名方式。

   var PI = Math.PI;

 var CONCAT_NUM = 10;

 

对象属性,只使用符合js变量名要求的单词组合,[_$a-zA-z][0-9]。不要使用引号引起对象属性名。相反地:在json交互数据中则使用双引号包裹属性名。

  var _object = {

      width:10,

       _paddingWidth:20

}

 

json:

    {

         “width“:10,

          “_paddingWidth“:20

}

        

见名知义,按照约定,同级统一。

不得使用

无意义命名:如a,b,c,a1,bl, hl等

中文拼音命名:如fuqi是代表父亲?或夫妻?。

           

知义

    isNumber, childNode,getAttribute等

    (许多优秀命名方式可参考,如原生ECMAScript,jQuery)

 

当一个变量的命名过长,含有2个+以上动词,则要考虑一个变量或模块承载的操作是否过多?是否要进行拆分。尽可能一个变量或模块只做一件对应的事情

 

模块内部约定或文件类命名约定

如有特定环境中的命名方式,必须在文件头(模块起始点)注释中写明变量规则。

3、命名空间

模块化开发

模块化开发不是分文件,而是遵守一个模块只做一件明确的事的基本原则,按照功能进行抽象分类。

如:字符串操作工具类

若非全局级工具类,则使用立即执行匿名函数包裹。



合理使用命名空间

  如果一个命名空间的命名单词组成过长,则考虑是否需要功能拆分。

   如果命名空间挂载级数过多,达到3+级,则考虑是否需要退级,将2级空间全局放出成1级空间。

  全局级命名空间尽可能做到分类清晰,一种类型对应一种类型的子集。

如:StringTools的操作就不能有直接的数学运算操作。

4、编码应该避免的习惯

(语言使用注意事项详见DouglasCrockford所写<<JavaScript语言精粹>>--附录A毒瘤,附录B糟粕两章);

追求无意义的“代码字符”简洁

使用缩写变量名。

所谓一行代码(逗号连续执行,链式调用不换行,过于复杂的短路逻辑写法,如2级+的三目运算等)。

单函数语句数量过长。功能过多不拆分。

简洁是应该是逻辑简洁,分类清晰,功能明确。“代码字符的简洁”应该交给压缩工具来做。


二、组件级开发接口约定

API设计

一个对外接口只做一件明确的事情

参数接口要有默认行为,抛错或者默认参数

参数尽可能单一,如果参数项是2+,使用对象配置传值

如:

$.fn.css({

width:12,

height:13,

z-index:1

})

$.port(url,postData,callback)

全局命名空间污染与IIFE

总是将代码包裹成一个 IIFE(Immediately-Invoked Function Expression),用以创建独立隔绝的定义域。这一举措可防止全局命名空间被污染。

IIFE还可确保你的代码不会轻易被其它全局命名空间里的代码所修改(i.e. 第三方库,window 引用,被覆盖的未定义的关键字等等)。

不推荐

推荐

IIFE(立即执行的函数表达式)

无论何时,想要创建一个新的封闭的定义域,那就用 IIFE。它不仅避免了干扰,也使得内存在执行完后立即释放。

所有脚本文件建议都从 IIFE开始。

立即执行的函数表达式的执行括号应该写在外包括号内。虽然写在内还是写在外都是有效的,但写在内使得整个表达式看起来更像一个整体,因此推荐这么做。


不推荐


推荐


如果你想引用全局变量或者是外层 IIFE的变量,可以通过下列方式传参:



严格模式  

ECMAScript 5严格模式可在整个脚本或独个方法内被激活。它对应不同的 javascript 语境会做更加严格的错误检查。严格模式也确保了 javascript 代码更加的健壮,运行的也更加快速。

严格模式会阻止使用在未来很可能被引入的预留关键字。

你应该优先考虑在你的脚本中启用严格模式,最好是在独立的 IIFE中应用它。避免在你的脚本第一行使用它而导致你的所有脚本都启动了严格模式,这有可能会引发一些第三方类库的问题。

不推荐

推荐



变量声明

总是使用 var 来声明变量。如不指定 var,变量将被隐式地声明为全局变量,这将对变量难以控制。如果没有声明,变量处于什么定义域就变得不清(可以是在 document  window 中,也可以很容易地进入本地定义域)。所以,请总是使用 var 来声明变量。

采用严格模式带来的好处是,当你手误输入错误的变量名时,它可以通过报错信息来帮助你定位错误出处。

不推荐

x = 10;

y = 100;

推荐

var x =10,

y = 100;

理解 JavaScript 的定义域和定义域提升

在 JavaScript中变量和方法定义会自动提升到执行之前。JavaScript只有 function 级的定义域,而无其他很多编程语言中的块定义域,所以使得你在某一 function内的某语句和循环体中定义了一个变量,此变量可作用于整个 function 内,而不仅仅是在此语句或循环体中,因为它们的声明被JavaScript 自动提升了。

我们通过例子来看清楚这到底是怎么一回事:

原 function


被 JS提升过后,实际形式。


根据以上提升过程,你是否可理解以下代码?

有效代码(注意var i的顺序)

正如你所看到的这段令人充满困惑与误解的代码导致了出人意料的结果。只有良好的声明习惯,也就是下一章节我们要提到的声明规则,才能尽可能的避免这类错误风险

总是使用带类型判断的比较判断

总是使用 === 精确的比较操作符,避免在判断的过程中,由 JavaScript的强制类型转换所造成的困扰。

如果你使用 === 操作符,那比较的双方必须是同一类型为前提的条件下才会有效。

如果你想了解更多关于强制类型转换的信息,你可以读一读 DmitrySoshnikov 的这篇文章。

在只使用 == 的情况下,JavaScript所带来的强制类型转换使得判断结果跟踪变得复杂,下面的例子可以看出这样的结果有多怪了:


明智地使用真假判断

当我们在一个 if 条件语句中使用变量或表达式时,会做真假判断。if(a == true) 是不同于 if(a) 的。后者的判断比较特殊,我们称其为真假判断。这种判断会通过特殊的操作将其转换为 true  false,下列表达式统统返回falsefalse, 0, undefined, null, NaN, ''(空字符串).

这种真假判断在我们只求结果而不关心过程的情况下,非常的有帮助。

以下示例展示了真假判断是如何工作的:



变量赋值时的逻辑操作

逻辑操作符 ||  && 也可被用来返回布尔值。如果操作对象为非布尔对象,那每个表达式将会被自左向右地做真假判断。基于此操作,最终总有一个表达式被返回回来。这在变量赋值时,是可以用来简化你的代码的,但是不要过分使用,3+步骤以上最好拆分。

不推荐


推荐


这一小技巧经常用来给方法设定默认的参数。



分号

总是使用分号,因为隐式的代码嵌套与js自动补全分号会引发难以察觉的问题。当然我们更要从根本上来杜绝这些问题[1] 。以下几个示例展示了缺少分号的危害:

那么,发生了什么?

1. JavaScript错误 —— 首先返回 42 的那个 function 被第二个 function 当中参数传入调用,接着数字 42 也被“调用”而导致出错;

2. 八成你会得到 no suchproperty in undefined 的错误提示,因为在真实环境中的调用是这个样子:x[ffVersion,ieVersion][isIE]()

3. die 总是被调用。因为数组减 1 的结果是 NaN,它不等于任何东西(无论 resultOfOperation 是否返回 NaN)。所以最终的结果是 die() 执行完所获得值将赋给 THINGS_TO_EAT

为什么会这样?

JavaScript中语句要以分号结束,否则它将会继续执行下去,不管换不换行。以上的每一个示例中,函数声明或对象或数组,都变成了在一句语句体内。要知道闭合圆括号并不代表语句结束,JavaScript不会终结语句,除非它的下一个 token 是一个中缀符[2] 或者是圆括号操作符。

这真是让人大吃一惊,所以乖乖地给语句末加上分号吧。

澄清:分号与函数

分号需要用在表达式的结尾,特别是return的结尾,而并非函数声明的结尾。区分它们最好的例子是:

嵌套函数

嵌套函数是非常有用的,比如用在持续创建和隐藏辅助函数的任务中。你可以非常自由随意地使用它们。

语句块内的函数声明

切勿在语句块内声明函数,在 ECMAScript 5的严格模式下,这是不合法的。函数声明应该在定义域的顶层。但在语句块内可将函数申明转化为函数表达式赋值给变量。js 没有块作用域,变量与函数声明会提升到顶部。

不推荐


推荐


异常

基本上你无法避免出现异常,特别是在做大型开发时(使用应用开发框架等等)。

在没有自定义异常的情况下,从有返回值的函数中返回错误信息一定非常的棘手,更别提多不优雅了。不好的解决方案包括了传第一个引用类型来接纳错误信息,或总是返回一个对象列表,其中包含着可能的错误对象。以上方式基本上是比较简陋的异常处理方式。适时可做自定义异常处理。

在复杂的环境中,你可以考虑抛出对象而不仅仅是字符串(默认的抛出值)。

标准特性

总是优先考虑使用标准特性。为了最大限度地保证扩展性与兼容性,总是首选标准的特性,而不是非标准的特性(例如:首选 string.charAt(3)而不是 string[3];首选 DOM 的操作方法来获得元素引用,而不是某一应用特定的快捷方法)。

简易的原型继承

如果你想在 JavaScript中继承你的对象,请遵循一个简易的模式来创建此继承。如果你预计你会遇上复杂对象的继承,那可以考虑采用一个继承库,比如 Proto.js by Axel Rauschmayer

简易继承请用以下方式:

切勿在循环中创建函数

在简单的循环语句中加入函数是非常容易形成闭包而带来隐患的。下面的例子就是一个典型的陷阱:

不推荐


接下来的改进虽然已经解决了上述例子中的问题或 bug,但还是违反了不在循环中创建函数或闭包的原则。

推荐


接下来的改进已解决问题,而且也遵循了规范。可是,你会发现看上去似乎过于复杂繁冗了,应该会有更好的解决方案吧。

不完全推荐

将循环语句转换为函数执行的方式问题能得到立马解决,每一次循环都会对应地创建一次闭包。函数式的风格更加值得推荐,而且看上去也更加地自然和可预料。

推荐  

eval 函数(魔鬼)

eval() 不但混淆语境还很危险,总会有比这更好、更清晰、更安全的另一种方案来写你的代码,因此尽量不要使用 evil函数。

eval函数还有一个值得注意的地方,如果在函数内部进行引用,那么引用形式的eval的执行环境则变成全局环境(《JavaScript高级程序设计》第6版)

跳出循环

try{ loop() }catch(e){}能立即中断循环并跳出,return即是退出当前函数。

值得注意的是 ,不要在循环函数内部写try,catch语句块,在最外层创建即可。

this 关键字

只在对象构造器、方法和在设定的闭包中使用 this 关键字。this的语义在此有些误导。它时而指向全局对象(大多数时),时而指向调用者的定义域(在eval中),时而指向 DOM 树中的某一节点(当用事件处理绑定到 HTML 属性上时),时而指向一个新创建的对象(在构造器中),还时而指向其它的一些对象(如果函数被call()apply()执行和调用时)。

正因为它是如此容易地被搞错,请限制它的使用场景:

在构造函数中

在对象的方法中(包括由此创建出的闭包内)

使用 ECMA Script 5

建议使用 ECMA Script 5中新增的语法糖和函数。这将简化你的程序,并让你的代码更加灵活和可复用。

数组和对象的属性迭代

用 ECMA5的迭代方法来迭代数组。使用 Array.forEach 或者如果你要在特殊场合下中断迭代,那就用 Array.every

 

尽量少使用 switch

switch 在所有的编程语言中都是个非常错误的难以控制的语句,建议用 if else 来替换它。注意case如果没有break跟随,将会发生向下一个case继续执行(swtch穿越)。

数组和对象字面量  

用数组和对象字面量来代替数组和对象构造器。数组构造器很容易让人在它的参数上犯错。

不推荐

正因如此,如果将代码传参从两个变为一个,那数组很有可能发生意料不到的长度变化。为避免此类怪异状况,请总是采用更多可读的数组字面量。

推荐

对象构造器不会有类似的问题,但是为了可读性和统一性,我们应该使用对象字面量。

不推荐

应该写成这样:

推荐

不要修改内建对象的原型链

修改内建的诸如 Object.prototype  Array.prototype 是被严厉禁止的。修改其它的内建对象比如Function.prototype,虽危害没那么大,但始终还是会导致在开发过程中难以debug的问题,应当也要避免。

自定义 toString() 方法

你可以通过自定义 toString() 来控制对象字符串化。这很好,但你必须保证你的方法总是成功并不会有其它副作用。如果你的方法达不到这样的标准,那将会引发严重的问题。如果 toString() 调用了一个方法,这个方法做了一个断言1 ,当断言失败,它可能会输出它所在对象的名称,当然对象也需要调用 toString()

圆括号  

一般在语法和语义上真正需要时才谨慎地使用圆括号。不要用在一元操作符上,例如delete,typeofvoid,或在关键字之后,例如 return,throw,case,new等。

字符串

统一使用单引号'',不使用双引号""。这在创建 HTML字符串非常有好处:

var msg = '这是一段HTML <divclass="makes-sense"></div>';

三元条件判断(if 的快捷方法)

用三元操作符分配或返回语句。在比较简单的情况下使用,避免在复杂的情况下使用。没人愿意用 10行三元操作符把自己的脑子绕晕,不可滥用。

不推荐


推荐

return x ===10 ? 'valid' : 'invalid';

断言一般指程序员在测试测序时的假设,一般是一些布尔表达式,当返回是 true时,断言为真,代码运行会继续进行;如果条件判断为 false,代码运行停止,你的应用被终止。

 

------------------------------

【注】:附加部分不考虑ES6,需要关注技术环境的发展趋势作出改进。

 

 


















原创粉丝点击