记录实用有价值的内容

JavaScript最全编码规范

来自:淡忘~浅思

原文:https://github.com/airbnb/javascript

译文:http://www.ido321.com/1520.html

译者:dwqs

类型 
1.基本类型:访问基本类型时,应该直接操作类型值

  • string

  • number

  • boolean

  • null

  • undefined

varfoo=1;

varbar=foo;

bar=9;

console.log(foo,bar);// => 1, 9

2.复合类型:访问复合类型时,应该操作其引用

  • object

  • array

  • function

varfoo=[1,2];

varbar=foo;

bar[0]=9;

console.log(foo[0],bar[0]);// => 9, 9

对象

  • 使用字面量语法创建对象

// bad

varitem=newObject();

 

// good

varitem={};

  • 不要使用保留字,在IE8中不起作用,更多相关信息

// bad

varsuperman={

default:{clark:'kent'},

private:true

};

 

// good

varsuperman={

defaults:{clark:'kent'},

hidden:true

};

  • 使用易读的同义词代替保留字

// bad

varsuperman={

class:'alien'

};

 

// bad

varsuperman={

klass:'alien'

};

 

// good

varsuperman={

type:'alien'

};

数组

  • 使用字面量语法创建数组

// bad

varitems=newArray();

 

// good

varitems=[];

  • 添加数组元素时,使用push而不是直接添加

varsomeStack=[];

 

// bad

someStack[someStack.length]='abracadabra';

 

// good

someStack.push('abracadabra');

  • 需要复制数组时,可以使用slice,jsPerf的相关文章

varlen=items.length;

varitemsCopy=[];

vari;

 

// bad

for(i=0;i<len;i++){

itemsCopy[i]=items[i];

}

 

// good

itemsCopy=items.slice();

  • 使用slice将类数组对象转为数组

functiontrigger(){

varargs=Array.prototype.slice.call(arguments);

...

}

字符串

  • 对字符串使用单引号

// bad

varname="Bob Parr";

 

// good

varname='Bob Parr';

 

// bad

varfullName="Bob "+this.lastName;

 

// good

varfullName='Bob '+this.lastName;

  • 超过80个字符的字符串应该使用字符串连接符进行跨行

  • 注意:对长字符串过度使用连接符将会影响性能。相关的文章和主题讨论: jsPerf & Discussion.

// bad

varerrorMessage='This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';

 

// bad

varerrorMessage='This is a super long error that was thrown because \

of Batman. When you stop to think about how Batman had anything to do \

with this, you would get nowhere \

fast.';

 

// good

varerrorMessage='This is a super long error that was thrown because '+

'of Batman. When you stop to think about how Batman had anything to do '+

'with this, you would get nowhere fast.';

  • 以编程方式创建字符串的时应该使用Array的join方法而不是通过连接符,尤其是在IE中:jsPerf.

varitems;

varmessages;

varlength;

vari;

 

messages=[{

state:'success',

message:'This one worked.'

},{

state:'success',

message:'This one worked as well.'

},{

state:'error',

message:'This one did not work.'

}];

 

length=messages.length;

 

// bad

functioninbox(messages){

items='<ul>';

 

for(i=0;i<length;i++){

items+='<li>'+messages[i].message+'</li>';

}

 

returnitems+'</ul>';

}

 

// good

functioninbox(messages){

items=[];

 

for(i=0;i<length;i++){

items[i]='<li>'+messages[i].message+'</li>';

}

 

return'<ul>'+items.join('')+'</ul>';

}

函数

  • 函数表达式

// anonymous function expression

varanonymous=function(){

returntrue;

};

 

// named function expression

varnamed=functionnamed(){

returntrue;

};

 

// immediately-invoked function expression (IIFE)

(function(){

console.log('Welcome to the Internet. Please follow me.');

})();

  • 不要在非函数块中(if, while, etc)声明函数,尽管浏览器允许你分配函数给一个变量,但坏消息是,不同的浏览器用不同的方式解析它

  • 注意:ECMA-262把块定义为一组语句,但函数声明不是一个语句:Read ECMA-262’s note on this issue.

// bad

if(currentUser){

functiontest(){

console.log('Nope.');

}

}

 

// good

vartest;

if(currentUser){

test=functiontest(){

console.log('Yup.');

};

}

  • 不要命名一个参数为arguments,否则它将优先于传递给每个函数作用域中的arguments对象,

// bad

functionnope(name,options,arguments){

// ...stuff...

}

 

// good

functionyup(name,options,args){

// ...stuff...

}

属性

  • 使用点表示法访问属性

varluke={

jedi:true,

age:28

};

 

// bad

varisJedi=luke['jedi'];

 

// good

varisJedi=luke.jedi;

  • 用变量访问属性时要使用下标表示法([])

varluke={

jedi:true,

age:28

};

 

functiongetProp(prop){

returnluke[prop];

}

 

varisJedi=getProp('jedi');

变量

  • 总是使用var声明变量,不然其将变为全局变量。我们要想办法避免全局空间污染

// bad

superPower=newSuperPower();

 

// good

varsuperPower=newSuperPower();

  • 使用var声明每个变量,这样很容易添加新的变量声明,而不用去担心用a;替换a,

// bad

varitems=getItems(),

goSportsTeam=true,

dragonball='z';

 

// bad

// (compare to above, and try to spot the mistake)

varitems=getItems(),

goSportsTeam=true;

dragonball='z';

 

// good

varitems=getItems();

vargoSportsTeam=true;

vardragonball='z';

  • 最后声明未赋值的变量,这对于你需要根据之前已经赋值的变量对一个变量进行赋值时是很有帮助的

// bad

vari,len,dragonball,

items=getItems(),

goSportsTeam=true;

 

// bad

vari;

varitems=getItems();

vardragonball;

vargoSportsTeam=true;

varlen;

 

// good

varitems=getItems();

vargoSportsTeam=true;

vardragonball;

varlength;

vari;

  • 在作用域顶端对变量赋值,这有助于避免变量声明问题和与声明提升相关的问题

// bad

function(){

test();

console.log('doing stuff..');

 

//..other stuff..

 

varname=getName();

 

if(name==='test'){

returnfalse;

}

 

returnname;

}

 

// good

function(){

varname=getName();

 

test();

console.log('doing stuff..');

 

//..other stuff..

 

if(name==='test'){

returnfalse;

}

 

returnname;

}

 

// bad

function(){

varname=getName();

 

if(!arguments.length){

returnfalse;

}

 

returntrue;

}

 

// good

function(){

if(!arguments.length){

returnfalse;

}

 

varname=getName();

 

returntrue;

}

声明提升

  • 变量声明是在作用域的顶端,但是赋值没有

// we know this wouldn't work (assuming there

// is no notDefined global variable)

functionexample(){

console.log(notDefined);// => throws a ReferenceError

}

 

// creating a variable declaration after you

// reference the variable will work due to

// variable hoisting. Note: the assignment

// value of `true` is not hoisted.

functionexample(){

console.log(declaredButNotAssigned);// => undefined

vardeclaredButNotAssigned=true;

}

 

// The interpreter is hoisting the variable

// declaration to the top of the scope,

// which means our example could be rewritten as:

functionexample(){

vardeclaredButNotAssigned;

console.log(declaredButNotAssigned);// => undefined

declaredButNotAssigned=true;

}

  • 匿名表达式能提升他们的变量名,但不能提升函数赋值

functionexample(){

console.log(anonymous);// => undefined

 

anonymous();// => TypeError anonymous is not a function

 

varanonymous=function(){

console.log('anonymous function expression');

};

}

  • 命名函数表达式会提升变量名,而不是函数名或者函数体

functionexample(){

console.log(named);// => undefined

 

named();// => TypeError named is not a function

 

superPower();// => ReferenceError superPower is not defined

 

varnamed=functionsuperPower(){

console.log('Flying');

};

}

 

// the same is true when the function name

// is the same as the variable name.

functionexample(){

console.log(named);// => undefined

 

named();// => TypeError named is not a function

 

varnamed=functionnamed(){

console.log('named');

}

}

  • 函数声明会提升变量名和函数体

functionexample(){

superPower();// => Flying

 

functionsuperPower(){

console.log('Flying');

}

}

更多信息指引:JavaScript Scoping & Hoisting by Ben Cherry. 
比较运算符&相等

  • 使用===和!==代替==和!=

  • 比较运算符进行计算时会利用ToBoolean方法进行强制转换数据类型,并遵从一下规则

  • Objects的计算值是true

  • Undefined的计算值是false

  • Boolean的计算值是boolean的值

  • Numbers如果是-0,+0或者NaN,则计算值是false,反之是true

  • Strings如果是空,则计算值是false,反之是true

if([0]){

// true

// An array is an object, objects evaluate to true

}

  • 使用快捷方式

// bad

if(name!==''){

// ...stuff...

}

 

// good

if(name){

// ...stuff...

}

 

// bad

if(collection.length>0){

// ...stuff...

}

 

// good

if(collection.length){

// ...stuff...

}

语句块

  • 对多行的语句块使用大括号

// bad

if(test)

returnfalse;

 

// good

if(test)returnfalse;

 

// good

if(test){

returnfalse;

}

 

// bad

function(){returnfalse;}

 

// good

function(){

returnfalse;

}

  • 对于使用if和else的多行语句块,把else和if语句块的右大括号放在同一行

// bad

if(test){

thing1();

thing2();

}

else{

thing3();

}

 

// good

if(test){

thing1();

thing2();

}else{

thing3();

}

注释

  • 多行注释使用/** … */,需包含一个描述、所有参数的具体类型和值以及返回值

// bad

// make() returns a new element

// based on the passed in tag name

//

// @param {String} tag

// @return {Element} element

functionmake(tag){

 

// ...stuff...

 

returnelement;

}

 

// good

/**

* make() returns a new element

* based on the passed in tag name

*

* @param {String} tag

* @return {Element} element

*/

functionmake(tag){

 

// ...stuff...

 

returnelement;

}

  • 单行注释使用//,把单行注释放在语句的上一行,并且在注释之前空一行

// bad

varactive=true;// is current tab

 

// good

// is current tab

varactive=true;

 

// bad

functiongetType(){

console.log('fetching type...');

// set the default type to 'no type'

vartype=this._type||'no type';

 

returntype;

}

 

// good

functiongetType(){

console.log('fetching type...');

 

// set the default type to 'no type'

vartype=this._type||'no type';

 

returntype;

}

  • 如果你指出的问题需要重新定位或者提出一个待解决的问题需要实现,给注释添加FIXME or TODO 前缀有利于其他开发者快速理解。这些注释不同于通常的注释,因为它们是可实施的。这些实施措施就是FIXME – need to figure this out or TODO – need to implement.

  • 使用// FIXME:给一个问题作注释

functionCalculator(){

// FIXME: shouldn't use a global here

total=0;

returnthis;

}

  • 使用//TODO:给问题解决方案作注释

functionCalculator(){

// TODO: total should be configurable by an options param

this.total=0;

returnthis;

}

空白

  • 使用软制表符设置两个空格

// bad

function(){

∙∙∙∙varname;

}

 

// bad

function(){

∙varname;

}

 

// good

function(){

∙∙varname;

}

  • 在左大括号之前留一个空格

// bad

functiontest(){

console.log('test');

}

 

// good

functiontest(){

console.log('test');

}

 

// bad

dog.set('attr',{

age:'1 year',

breed:'Bernese Mountain Dog'

});

 

// good

dog.set('attr',{

age:'1 year',

breed:'Bernese Mountain Dog'

});

  • 在控制语句中(if, while etc),左括号之前留一个空格。函数的参数列表之前不要有空格

// bad

if(isJedi){

fight();

}

 

// good

if(isJedi){

fight();

}

 

// bad

functionfight(){

console.log('Swooosh!');

}

 

// good

functionfight(){

console.log('Swooosh!');

}

  • 用空白分隔运算符

// bad

varx=y+5;

 

// good

varx=y+5;

  • 用一个换行符结束文件

// bad

(function(global){

// ...stuff...

})(this);

// bad

(function(global){

// ...stuff...

})(this);↵

// good

(function(global){

// ...stuff...

})(this);↵

  • 当调用很长的方法链时使用缩进,可以强调这行是方法调用,不是新的语句

// bad

$('#items').find('.selected').highlight().end().find('.open').updateCount();

 

// bad

$('#items').

find('.selected').

highlight().

end().

find('.open').

updateCount();

 

// good

$('#items')

.find('.selected')

.highlight()

.end()

.find('.open')

.updateCount();

 

// bad

varleds=stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led',true)

.attr('width',(radius+margin)*2).append('svg:g')

.attr('transform','translate('+(radius+margin)+','+(radius+margin)+')')

.call(tron.led);

 

// good

varleds=stage.selectAll('.led')

.data(data)

.enter().append('svg:svg')

.classed('led',true)

.attr('width',(radius+margin)*2)

.append('svg:g')

.attr('transform','translate('+(radius+margin)+','+(radius+margin)+')')

.call(tron.led);

  • 在语句块和下一个语句之前留一个空行

// bad

if(foo){

returnbar;

}

returnbaz;

 

// good

if(foo){

returnbar;

}

 

returnbaz;

 

// bad

varobj={

foo:function(){

},

bar:function(){

}

};

returnobj;

 

// good

varobj={

foo:function(){

},

 

bar:function(){

}

};

 

returnobj;

逗号

  • 不要在语句前留逗号

// bad

varstory=[

once

,upon

,aTime

];

 

// good

varstory=[

once,

upon,

aTime

];

 

// bad

varhero={

firstName:'Bob'

,lastName:'Parr'

,heroName:'Mr. Incredible'

,superPower:'strength'

};

 

// good

varhero={

firstName:'Bob',

lastName:'Parr',

heroName:'Mr. Incredible',

superPower:'strength'

};

  • 不要有多余逗号:这会在IE6、IE7和IE9的怪异模式中导致一些问题;同时,在ES3的一些实现中,多余的逗号会增加数组的长度。在ES5中已经澄清(source)

// bad

varhero={

firstName:'Kevin',

lastName:'Flynn',

};

 

varheroes=[

'Batman',

'Superman',

];

 

// good

varhero={

firstName:'Kevin',

lastName:'Flynn'

};

 

varheroes=[

'Batman',

'Superman'

];

分号

  • 恩,这也是规范一部分

// bad

(function(){

varname='Skywalker'

returnname

})()

 

// good

(function(){

varname='Skywalker';

returnname;

})();

 

// good (guards against the function becoming an argument when two files with IIFEs are concatenated)

;(function(){

varname='Skywalker';

returnname;

})();

阅读更多

类型分配&强制转换

  • 执行强制类型转换的语句。

  • Strings:

// => this.reviewScore = 9;

 

// bad

vartotalScore=this.reviewScore+'';

 

// good

vartotalScore=''+this.reviewScore;

 

// bad

vartotalScore=''+this.reviewScore+' total score';

 

// good

vartotalScore=this.reviewScore+' total score';

  • 使用parseInt对Numbers进行转换,并带一个进制作为参数

varinputValue='4';

 

// bad

varval=newNumber(inputValue);

 

// bad

varval=+inputValue;

 

// bad

varval=inputValue>>0;

 

// bad

varval=parseInt(inputValue);

 

// good

varval=Number(inputValue);

 

// good

varval=parseInt(inputValue,10);

  • 无论出于什么原因,或许你做了一些”粗野”的事;或许parseInt成了你的瓶颈;或许考虑到性能,需要使用位运算,都要用注释说明你为什么这么做

// good

/**

* parseInt was the reason my code was slow.

* Bitshifting the String to coerce it to a

* Number made it a lot faster.

*/

varval=inputValue>>0;

  • 注意:当使用位运算时,Numbers被视为64位值,但是位运算总是返回32位整型(source)。对于整型值大于32位的进行位运算将导致不可预见的行为。Discussion.最大的有符号32位整数是2,147,483,647

2147483647>>0//=> 2147483647

2147483648>>0//=> -2147483648

2147483649>>0//=> -2147483647

  • Booleans:

varage=0;

 

// bad

varhasAge=newBoolean(age);

 

// good

varhasAge=Boolean(age);

 

// good

varhasAge=!!age;

命名规范

  • 避免单字母名称,让名称具有描述性

// bad

functionq(){

// ...stuff...

}

 

// good

functionquery(){

// ..stuff..

}

  • 当命名对象、函数和实例时使用骆驼拼写法

// bad

varOBJEcttsssss={};

varthis_is_my_object={};

functionc(){}

varu=newuser({

name:'Bob Parr'

});

 

// good

varthisIsMyObject={};

functionthisIsMyFunction(){}

varuser=newUser({

name:'Bob Parr'

});

  • 当命名构造函数或类名时,使用驼峰式写法

// bad

functionuser(options){

this.name=options.name;

}

 

varbad=newuser({

name:'nope'

});

 

// good

functionUser(options){

this.name=options.name;

}

 

vargood=newUser({

name:'yup'

});

  • 命名私有属性时使用前置下划线

// bad

this.__firstName__='Panda';

this.firstName_='Panda';

 

// good

this._firstName='Panda';

  • 保存this引用时使用_this

// bad

function(){

varself=this;

returnfunction(){

console.log(self);

};

}

 

// bad

function(){

varthat=this;

returnfunction(){

console.log(that);

};

}

 

// good

function(){

var_this=this;

returnfunction(){

console.log(_this);

};

}

  • 命名函数时,下面的方式有利于堆栈跟踪

// bad

varlog=function(msg){

console.log(msg);

};

 

// good

varlog=functionlog(msg){

console.log(msg);

};

  • 注意:IE8和怪异模式下命名函数表示,戳此:http://kangax.github.io/nfe/

  • 如果文件作为一个类被导出,文件名应该和类名保持一致

// file contents

classCheckBox{

// ...

}

module.exports=CheckBox;

 

// in some other file

// bad

varCheckBox=require('./checkBox');

 

// bad

varCheckBox=require('./check_box');

 

// good

varCheckBox=require('./CheckBox');

存取器

  • 对于属性,访问器函数不是必须的

  • 如果定义了存取器函数,应参照getVal() 和 setVal(‘hello’)格式.

// bad

dragon.age();

 

// good

dragon.getAge();

 

// bad

dragon.age(25);

 

// good

dragon.setAge(25);

  • 如果属性时boolean,格式应为isVal() or hasVal().

// bad

if(!dragon.age()){

returnfalse;

}

 

// good

if(!dragon.hasAge()){

returnfalse;

}

  • 创建get() and set()函数时不错的想法,但是要保持一致

functionJedi(options){

options||(options={});

varlightsaber=options.lightsaber||'blue';

this.set('lightsaber',lightsaber);

}

 

Jedi.prototype.set=function(key,val){

this[key]=val;

};

 

Jedi.prototype.get=function(key){

returnthis[key];

};

构造函数

  • 在原型对象上定义方法,而不是用新对象重写它。重写使继承变为不可能:重置原型将重写整个基类

functionJedi(){

console.log('new jedi');

}

 

// bad

Jedi.prototype={

fight:functionfight(){

console.log('fighting');

},

 

block:functionblock(){

console.log('blocking');

}

};

 

// good

Jedi.prototype.fight=functionfight(){

console.log('fighting');

};

 

Jedi.prototype.block=functionblock(){

console.log('blocking');

};

  • 方法应该返回this,有利于构成方法链

// bad

Jedi.prototype.jump=function(){

this.jumping=true;

returntrue;

};

 

Jedi.prototype.setHeight=function(height){

this.height=height;

};

 

varluke=newJedi();

luke.jump();// => true

luke.setHeight(20);// => undefined

 

// good

Jedi.prototype.jump=function(){

this.jumping=true;

returnthis;

};

 

Jedi.prototype.setHeight=function(height){

this.height=height;

returnthis;

};

 

varluke=newJedi();

 

luke.jump()

.setHeight(20);

  • 写一个自定义的toString()方法是可以的,只要确保它能正常运行并且不会产生副作用

functionJedi(options){

options||(options={});

this.name=options.name||'no name';

}

 

Jedi.prototype.getName=functiongetName(){

returnthis.name;

};

 

Jedi.prototype.toString=functiontoString(){

return'Jedi - '+this.getName();

};

事件

  • 当在事件对象上附加数据时(无论是DOM事件还是如Backbone一样拥有的私有事件),应传递散列对象而不是原始值,这可以让随后的贡献者给事件对象添加更多的数据,而不必去查找或者更新每一个事件处理程序。举个粟子,不要用下面的方式:

// bad

$(this).trigger('listingUpdated',listing.id);

$(this).on('listingUpdated',function(e,listingId){

// do something with listingId

});

  • 应该按如下方式:

// good

$(this).trigger('listingUpdated',{listingId:listing.id});

$(this).on('listingUpdated',function(e,data){

// do something with data.listingId

});

模块

  • 模块应该以 ! 开始,这能确保当脚本连接时,如果畸形模块忘记导入,包括最后一个分号,不会产生错误。Explanation

  • 文件应该以驼峰式命名,放在同名的文件夹中,和单出口的名称相匹配

  • 定义一个noConflict()方法来设置导出模块之前的版本,并返回当前版本。

  • 在模块的顶部申明’use strict’;

// fancyInput/fancyInput.js

!function(global){

'use strict';

varpreviousFancyInput=global.FancyInput;

functionFancyInput(options){

this.options=options||{};

}

FancyInput.noConflict=functionnoConflict(){

global.FancyInput=previousFancyInput;

returnFancyInput;

};

global.FancyInput=FancyInput;

}(this);

jQuery

  • jQuery对象变量使用前缀$

// bad

varsidebar=$('.sidebar');

 

// good

var$sidebar=$('.sidebar');

  • 缓存jQuery查询

// bad

functionsetSidebar(){

$('.sidebar').hide();

 

// ...stuff...

 

$('.sidebar').css({

'background-color':'pink'

});

}

 

// good

functionsetSidebar(){

var$sidebar=$('.sidebar');

$sidebar.hide();

 

// ...stuff...

 

$sidebar.css({

'background-color':'pink'

});

}

  • 使用级联('.sidebarul′)或父子(‘.sidebar > ul’)选择器进行DOM查询。jsPerf

  • 在范围内使用find进行jQuery对象查询

// bad

$('ul','.sidebar').hide();

 

// bad

$('.sidebar').find('ul').hide();

 

// good

$('.sidebar ul').hide();

 

// good

$('.sidebar > ul').hide();

 

// good

$sidebar.find('ul').hide();

上一篇:前端基础进阶(十三):透彻掌握Promise的使用,读这篇就够了

下一篇:javascript删除元素节点