Airbnb公司版JavaScript 风格指南/编码规范
我弄得xmind的更加的清晰全面,还有很多备注,想要思维导图的直接评论发邮鞋就好,我会发给你。
- 基本类型:当你访问基本类型时,你是直接对它的值进行操作。
string
number
boolean
null
-
undefined
123456var foo = 1,bar = foo;bar = 9;console.log(foo, bar); // => 1, 9
object
array
function
1
2
3
4
5
6
|
var foo = [1, 2],
bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
|
对象
- 使用字面量语法来创建对象
- 12345// badvar item = new Object();// goodvar item = {};
- 不要使用保留字作为“键值”,因为在IE8不能运行。More info
- 1234567891011// badvar superman = {default: { clark: 'kent' },private: true};// goodvar superman = {defaults: { clark: 'kent' },hidden: true};
- 使用容易理解的同义词来替代保留字
- 1234567891011121314// badvar superman = {class: 'alien'};// badvar superman = {klass: 'alien'};// goodvar superman = {type: 'alien'};
数组
- 使用字面量语法来创建数组
- 12345// badvar items = new Array();// goodvar items = [];
- 如果你不知道数组长度,数组添加成员使用push方法。
- 1234567var someStack = [];// badsomeStack[someStack.length] = 'abracadabra';// goodsomeStack.push('abracadabra');
- 当你需要复制一个数组时,使用数组的slice方法。jsPerf
- 1234567891011var len = items.length,itemsCopy = [],i;// badfor (i = 0; i < len; i++) {itemsCopy[i] = items[i];}// gooditemsCopy = items.slice();
- 当你需要把“类似数组对象”转为数组时,使用数组的slice方法。
- 1234function trigger() {var args = Array.prototype.slice.call(arguments);...}
字符串
- 字符串使用单引号‘’
- 1234567891011// badvar name = "Bob Parr";// goodvar name = 'Bob Parr';// badvar fullName = "Bob " + this.lastName;// goodvar fullName = 'Bob ' + this.lastName;
- 大于80个元素的字符串需要通过分隔符进行多行操作。
- 注意:如果在长字符串中过度使用分隔符会影响性能。. jsPerf & Discussion
- 12345678910111213// badvar errorMessage = '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.';// badvar errorMessage = 'This is a super long error that was thrown becauseof Batman. When you stop to think about how Batman had anything to dowith this, you would get nowherefast.';// goodvar errorMessage = '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.';
- 通过编程的方式创建字符串,应该使用数组的join方法,而不是字符串链接方法。特别是对于IE而言。 jsPerf.
- 1234567891011121314151617181920212223242526272829303132333435363738var items,messages,length,i;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;// badfunction inbox(messages) {items = '<ul>';for (i = 0; i < length; i++) {items += '<li>' + messages[i].message + '</li>';}return items + '</ul>';}// goodfunction inbox(messages) {items = [];for (i = 0; i < length; i++) {items[i] = messages[i].message;}return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
函数
- 函数表达式
- 1234567891011121314// anonymous function expressionvar anonymous = function() {return true;};// named function expressionvar named = function named() {return true;};// immediately-invoked function expression (IIFE)(function() {console.log('Welcome to the Internet. Please follow me.');})();
- 不要直接在非函数块(if,while等)里声明一个函数,最好将函数赋值给一个变量。虽然浏览器允许你在非函数块中声明函数,但是由于不同的浏览器对Javascript的解析方式不同,这样做就很可能造成麻烦。
- 注意:ECMA-262 将块定义为一组语句,而函数声明不是语句。Read ECMA-262′s note on this issue
- 1234567891011121314// badif (currentUser) {function test() {console.log('Nope.');}}// goodvar test;if (currentUser) {test = function test() {console.log('Yup.');};}
- 不要将参数命名为arguments,它将在每个函数的作用范围内优先于arguments对象。
- 123456789// badfunction nope(name, options, arguments) {// ...stuff...}// goodfunction yup(name, options, args) {// ...stuff...}
属性
- 使用点符号 . 来访问属性
- 12345678910var luke = {jedi: true,age: 28};// badvar isJedi = luke['jedi'];// goodvar isJedi = luke.jedi;
- 当你在变量中访问属性时,使用[ ]符号
- 12345678910var luke = {jedi: true,age: 28};function getProp(prop) {return luke[prop];}var isJedi = getProp('jedi');
变量
- 使用var来声明变量,否则将声明全局变量,我们需要尽量避免污染全局命名空间,Captain Planet这样警告过我们。
- 12345// badsuperPower = new SuperPower();// goodvar superPower = new SuperPower();
- 多个变量时只使用一个var声明,每个变量占一新行。
- 123456789// badvar items = getItems();var goSportsTeam = true;var dragonball = 'z';// goodvar items = getItems(),goSportsTeam = true,dragonball = 'z';
- 最后再声明未赋值的变量,这对你之后需要依赖之前变量的变量赋值会有帮助。
- 123456789101112131415161718// badvar i, len, dragonball,items = getItems(),goSportsTeam = true;// badvar i, items = getItems(),dragonball,goSportsTeam = true,len;// goodvar items = getItems(),goSportsTeam = true,dragonball,length,i;·
- 在范围内将变量赋值置顶,这有助于避免变量声明和赋值提升相关的问题
- 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253// badfunction() {test();console.log('doing stuff..');//..other stuff..var name = getName();if (name === 'test') {return false;}return name;}// goodfunction() {var name = getName();test();console.log('doing stuff..');//..other stuff..if (name === 'test') {return false;}return name;}// badfunction() {var name = getName();if (!arguments.length) {return false;}return true;}// goodfunction() {if (!arguments.length) {return false;}var name = getName();return true;}
提升
- 变量声明在范围内提升,但赋值并没有提升
- 1234567891011121314151617181920212223// we know this wouldn't work (assuming there// is no notDefined global variable)function example() {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.function example() {console.log(declaredButNotAssigned); // => undefinedvar declaredButNotAssigned = true;}// The interpreter is hoisting the variable// declaration to the top of the scope.// Which means our example could be rewritten as:function example() {var declaredButNotAssigned;console.log(declaredButNotAssigned); // => undefineddeclaredButNotAssigned = true;}
- 匿名函数表达式提升变量名,但函数赋值并没有提升,
- 123456789· function example() {console.log(anonymous); // => undefinedanonymous(); // => TypeError anonymous is not a functionvar anonymous = function() {console.log('anonymous function expression');};}
- 命名函数表达式提升变量名,但函数名字和函数体并没有提升。
- 1234567891011121314151617181920212223function example() {console.log(named); // => undefinednamed(); // => TypeError named is not a functionsuperPower(); // => ReferenceError superPower is not definedvar named = function superPower() {console.log('Flying');};}// the same is true when the function name// is the same as the variable name.function example() {console.log(named); // => undefinednamed(); // => TypeError named is not a functionvar named = function named() {console.log('named');}}
- 函数声明能提升他们的函数名和函数体
- 1234567function example() {superPower(); // => Flyingfunction superPower() {console.log('Flying');}}
- 更多的信息在JavaScript Scoping & Hoisting by Ben Cherry
条件表达式和相等
- 条件表达式强制使用 ToBoolean方法进行解析,并遵从以下简单的规则Object :返回 true
- Undefined: 返回 false
- Null: 返回 false
- Booleans :返回 boolean的值
- Numbers :如果是+0, -0, or NaN返回 false, 其他则 true
-
Strings :空字符串
''
返回 false 其他返回true - 1234if ([0]) {// true// An array is an object, objects evaluate to true}
- 使用简易方式
- 12345678910111213141516171819// badif (name !== '') {// ...stuff...}// goodif (name) {// ...stuff...}// badif (collection.length > 0) {// ...stuff...}// goodif (collection.length) {// ...stuff...}
- 更多的信息查看 Truth Equality and JavaScript by Angus Croll
块
- 在多行块中使用大括号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function() { return false; }
// good
function() {
return false;
}
|
注释
- 多行注释使用
/** ... */
,包括描述,指定类型、所有参数的值和返回值 - 123456789101112131415161718192021222324252627// bad// make() returns a new element// based on the passed in tag name//// @param <String> tag// @return <Element> elementfunction make(tag) {// ...stuff...return element;}// good/*** make() returns a new element* based on the passed in tag name** @param <String> tag* @return <Element> element*/function make(tag) {// ...stuff...return element;}
- 单行注释使用 //, 在注释的内容前另起一行进行单行注释,并在注释前留一空行。
- 12345678910111213141516171819202122232425// badvar active = true; // is current tab// good// is current tabvar active = true;// badfunction getType() {console.log('fetching type...');// set the default type to 'no type'var type = this._type || 'no type';return type;}// goodfunction getType() {console.log('fetching type...');// set the default type to 'no type'var type = this._type || 'no type';return type;}
- 在你的注释加上FIXME或TODO的前缀可以帮助其他开发者迅速理解你指出的问题和需要的帮助,以及你建议需要完善的解决问题,这跟常规注释不一样因为其具有可行动性,FIXME——是需要解决的而TODO——是需要完善的
- 使用
// FIXME:
来标记问题 - 1234567function Calculator() {// FIXME: shouldn't use a global heretotal = 0;return this;}
- 使用 // TODO:给待解决问题进行标注
- 1234567function Calculator() {// TODO: total should be configurable by an options paramthis.total = 0;return this;}
空格
- 使用tabs设置两个空格
- 1234567891011121314// badfunction() {∙∙∙∙var name;}// badfunction() {∙var name;}// goodfunction() {∙∙var name;}
- 分支前面空一格
- 123456789101112131415161718192021// badfunction test(){console.log('test');}// goodfunction test() {console.log('test');}// baddog.set('attr',{age: '1 year',breed: 'Bernese Mountain Dog'});// gooddog.set('attr', {age: '1 year',breed: 'Bernese Mountain Dog'});
- 操作符前后空一格
- 12345// badvar x=y+5;// goodvar x = y + 5;
- 文件末尾用换行符结束
- 1234567891011121314// bad(function(global) {// ...stuff...})(this);// bad(function(global) {// ...stuff...})(this);// good(function(global) {// ...stuff...})(this)
- 使用长方法链时使用缩进
- 1234567891011121314151617181920212223242526// bad$('#items').find('.selected').highlight().end().find('.open').updateCount();// good$('#items').find('.selected').highlight().end().find('.open').updateCount();// badvar leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true).attr('width', (radius + margin) * 2).append('svg:g').attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')').call(tron.led);// goodvar leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true).attr('width', (radius + margin) * 2).append('svg:g').attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')').call(tron.led);
逗号
- 不要在句首使用逗号
- 12345678910111213141516171819202122232425// badvar once, upon, aTime;// goodvar once,upon,aTime;// badvar hero = {firstName: 'Bob', lastName: 'Parr', heroName: 'Mr. Incredible', superPower: 'strength'};// goodvar hero = {firstName: 'Bob',lastName: 'Parr',heroName: 'Mr. Incredible',superPower: 'strength'};
- 不要使用多余的逗号,这在IE6/7 和 IE9的混杂模式中会造成问题,同样,在ES3中有些实现,如果使用多余的逗号将增加数组的长度,这在ES5中有阐明(source):
- 123456789101112131415161718192021// badvar hero = {firstName: 'Kevin',lastName: 'Flynn',};var heroes = ['Batman','Superman',];// goodvar hero = {firstName: 'Kevin',lastName: 'Flynn'};var heroes = ['Batman','Superman'];
-
分号
1234567891011121314151617// bad(function() {var name = 'Skywalker'return name})()// good(function() {var name = 'Skywalker';return name;})();// good (guards against the function becoming an argument when two files with IIFEs are concatenated);(function() {var name = 'Skywalker';return name;})(); 转型&强制
- 在语句的开头执行强制转型。
- Strings:
-
12345678910111213// => this.reviewScore = 9;// badvar totalScore = this.reviewScore + '';// goodvar totalScore = '' + this.reviewScore;// badvar totalScore = '' + this.reviewScore + ' total score';// goodvar totalScore = this.reviewScore + ' total score';
- 使用
parseInt
对
Numbers
型进行强制转型。
-
12345678910111213141516171819var inputValue = '4';// badvar val = new Number(inputValue);// badvar val = +inputValue;// badvar val = inputValue >> 0;// badvar val = parseInt(inputValue);// goodvar val = Number(inputValue);// goodvar val = parseInt(inputValue, 10);
- 如果出于某种原因你需要做些特别的事,而
parseInt
是你的瓶颈,出于性能考虑你需要使用
位移,那么留下你的注释来解释原因。 -
1234567// good/*** parseInt was the reason my code was slow.* Bitshifting the String to coerce it to a* Number made it a lot faster.*/var val = inputValue >> 0;
- 注意:小心位移操作符,Numbers代表着64位的值,而位移操作符只能返回32位的整型,位移对于大于32位的整型的值会有不好的影响,32位最大的有符号整型是2,147,483,647。
- (有关讨论:Discussion)
-
1232147483647 >> 0 //=> 21474836472147483648 >> 0 //=> -21474836482147483649 >> 0 //=> -2147483647
- Booleans:
-
12345678910var age = 0;// badvar hasAge = new Boolean(age);// goodvar hasAge = Boolean(age);// goodvar hasAge = !!age;
命名规则
- 不要使用一个字母命名,而应该用单词描述
-
123456789// badfunction q() {// ...stuff...}// goodfunction query() {// ..stuff..}
- 使用驼峰法来给对象、函数、实例命名。
-
1234567891011121314// badvar OBJEcttsssss = {};var this_is_my_object = {};function c() {}var u = new user({name: 'Bob Parr'});// goodvar thisIsMyObject = {};function thisIsMyFunction() {}var user = new User({name: 'Bob Parr'});
- 使用驼峰式大小写给构造函数和类命名。
-
1234567891011121314151617// badfunction user(options) {this.name = options.name;}var bad = new user({name: 'nope'});// goodfunction User(options) {this.name = options.name;}var good = new User({name: 'yup'});
- 使用下划线前缀_来命名私有属性。
-
123456// badthis.__firstName__ = 'Panda';this.firstName_ = 'Panda';// goodthis._firstName = 'Panda';
- 储存this的引用使用_this
-
1234567891011121314151617181920212223// badfunction() {var self = this;return function() {console.log(self);};}// badfunction() {var that = this;return function() {console.log(that);};}// goodfunction() {var _this = this;return function() {console.log(_this);};}
- 给你的函数命名,这有助于堆栈重写
-
123456789// badvar log = function(msg) {console.log(msg);};// goodvar log = function log(msg) {console.log(msg);};
- 注意:IE8以下还有一些关于命名函数表达式的怪癖。详情见http://kangax.github.io/nfe/
访问器
- 如果你创建访问函数使用getVal() 和 setVal(‘hello’)
- 1234567891011// baddragon.age();// gooddragon.getAge();// baddragon.age(25);// gooddragon.setAge(25);
如果这个属性是boolean,使用isVal() 或者 hasVal()
1
2
3
4
5
6
7
8
9
|
// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}
|
.也可以使用get() 和 set()函数来创建,但是必须一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function Jedi(options) {
options || (options = {});
var lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
Jedi.prototype.set = function(key, val) {
this[key] = val;
};
Jedi.prototype.get = function(key) {
return this[key];
};
|
构造器
- 给原型对象添加方法,相比用新对象重写原型,重写原型会有继承问题。如果你要重写原型方法,请重写基础方法。
- 1234567891011121314151617181920212223function Jedi() {console.log('new jedi');}// badJedi.prototype = {fight: function fight() {console.log('fighting');},block: function block() {console.log('blocking');}};// goodJedi.prototype.fight = function fight() {console.log('fighting');};Jedi.prototype.block = function block() {console.log('blocking');};
- 方法返回this有助于方法链
- 1234567891011121314151617181920212223242526272829// badJedi.prototype.jump = function() {this.jumping = true;return true;};Jedi.prototype.setHeight = function(height) {this.height = height;};var luke = new Jedi();luke.jump(); // => trueluke.setHeight(20); // => undefined// goodJedi.prototype.jump = function() {this.jumping = true;return this;};Jedi.prototype.setHeight = function(height) {this.height = height;return this;};var luke = new Jedi();luke.jump().setHeight(20);
- 可以重写常规的toString()方法。但必须保证可以成功运行并没有别的影响
- 123456789101112function Jedi(options) {options || (options = {});this.name = options.name || 'no name';}Jedi.prototype.getName = function getName() {return this.name;};Jedi.prototype.toString = function toString() {return 'Jedi - ' + this.getName();};
- 将为事件加载数据时(不管是DOM事件还是其他专用事件的,比如Backbone事件)用散列表来取代原始值。因为这将允许后续添加更多的数据载入事件而不用更新每个事件的处理程序。例如:
1
2
3
4
5
6
7
8
|
// bad
$(this).trigger('listingUpdated', listing.id);
...
$(this).on('listingUpdated', function(e, listingId) {
// do something with listingId
});
|
更好的方式:
1
2
3
4
5
6
7
8
|
// good
$(this).trigger('listingUpdated', { listingId : listing.id });
...
$(this).on('listingUpdated', function(e, data) {
// do something with data.listingId
});
|
模块
- 模块应该以 “!”开始,以确保当模块忘记包含最后一个分号时,在脚本连接时不会报错。
- 文档需要用驼峰法命名,同一文件内要用一样的名字以及要与单个输出相匹配。
- 增加一个叫
noConflict()
的方法,使模块输出早期版本并返回。
- 在模块开始的部位声明
'use strict'
。