ECMAScript 6 Style Guide
用更合理的方式写 JavaScript
参考自 Airbnb JavaScript Style Guide 。
目录
- 类型
- 引用
- 对象
- 数组
- 解构
- 字符串
- 函数
- 箭头函数
- 构造函数
- 模块
- Iterators & Generators
- 属性
- 变量
- 提升
- 比较运算符 & 等号
- 代码块
- 注释
- 空白
- 逗号
- 分号
- 类型转换
- 命名规则
- 存取器
- 事件
- jQuery
- ECMAScript 5 兼容性
- ECMAScript 6 编码规范
- 测试
- 性能
- 资源
- 使用人群
- 翻译
- JavaScript 编码规范说明
- 一起来讨论 JavaScript
- Contributors
- License
类型
1.1 基本类型: 直接存取基本类型。
字符串
数值
布尔类型
null
undefined
123456const foo = 1;let bar = foo;bar = 9;console.log(foo, bar); // => 1, 91.2 复制类型: 通过引用的方式存取复杂类型。
对象
数组
函数
123456const foo = [1, 2];const bar = foo;bar[0] = 9;console.log(foo[0], bar[0]); // => 9, 9
引用
2.1 对所有的引用使用
const
;不要使用var
。为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。
1234567// badvar a = 1;var b = 2;// goodconst a = 1;const b = 2;2.2 如果你一定需要可变动的引用,使用
let
代替var
。为什么?因为
let
是块级作用域,而var
是函数作用域。1234567891011// badvar count = 1;if (true) {count += 1;}// good, use the let.let count = 1;if (true) {count += 1;}2.3 注意
let
和const
都是块级作用域。
- 在一个作用域内不能重复声明
const
在声明的时候必须初始化let
在声明的时候可以不用初始化123456789101112// const 和 let 只存在于它们被定义的区块内。{let a = 1;const b = 1;// errorlet a = 2;// errorconst b ;const b = 3;}console.log(a); // ReferenceErrorconsole.log(b); // ReferenceError
对象
3.1 使用字面值创建对象。
12345// badconst item = new Object();// goodconst item = {};3.2 如果你的代码在浏览器环境下执行,别使用 保留字 作为键值。这样的话在 IE8 不会运行。 更多信息。 但在 ES6 模块和服务器端中使用没有问题。
1234567891011// badconst superman = {default: { clark: 'kent' },private: true,};// goodconst superman = {defaults: { clark: 'kent' },hidden: true,};3.3 使用同义词替换需要使用的保留字。
1234567891011121314// badconst superman = {class: 'alien',};// badconst superman = {klass: 'alien',};// goodconst superman = {type: 'alien',};3.4 创建有动态属性名的对象时,使用可被计算的属性名称。
为什么?因为这样可以让你在一个地方定义所有的对象属性。
1234567891011121314151617function getKey(k) {return `a key named ${k}`;}// badconst obj = {id: 5,name: 'San Francisco',};obj[getKey('enabled')] = true;// goodconst obj = {id: 5,name: 'San Francisco',[getKey('enabled')]: true,};3.5 使用对象方法的简写。
1234567891011121314151617// badconst atom = {value: 1,addValue: function (value) {return atom.value + value;},};// goodconst atom = {value: 1,addValue(value) {return atom.value + value;},};3.6 使用对象属性值的简写。
为什么?因为这样更短更有描述性。
1234567891011const lukeSkywalker = 'Luke Skywalker';// badconst obj = {lukeSkywalker: lukeSkywalker,};// goodconst obj = {lukeSkywalker,};3.7 在对象属性声明前把简写的属性分组。
为什么?因为这样能清楚地看出哪些属性使用了简写。
12345678910111213141516171819202122const anakinSkywalker = 'Anakin Skywalker';const lukeSkywalker = 'Luke Skywalker';// badconst obj = {episodeOne: 1,twoJedisWalkIntoACantina: 2,lukeSkywalker,episodeThree: 3,mayTheFourth: 4,anakinSkywalker,};// goodconst obj = {lukeSkywalker,anakinSkywalker,episodeOne: 1,twoJedisWalkIntoACantina: 2,episodeThree: 3,mayTheFourth: 4,};
数组
4.1 使用字面值创建数组。
12345// badconst items = new Array();// goodconst items = [];4.2 向数组添加元素时使用 Arrary#push 替代直接赋值。
12345678const someStack = [];// badsomeStack[someStack.length] = 'abracadabra';// goodsomeStack.push('abracadabra');4.3 使用拓展运算符
...
复制数组。1234567891011// badconst len = items.length;const itemsCopy = [];let i;for (i = 0; i < len; i++) {itemsCopy[i] = items[i];}// goodconst itemsCopy = [...items];4.4 使用 Array#from 把一个类数组对象转换成数组。
12const foo = document.querySelectorAll('.foo');const nodes = Array.from(foo);
解构
5.1 使用解构存取和使用多属性对象。
为什么?因为解构能减少临时引用属性。
123456789101112131415161718// badfunction getFullName(user) {const firstName = user.firstName;const lastName = user.lastName;return `${firstName} ${lastName}`;}// goodfunction getFullName(obj) {const { firstName, lastName } = obj;return `${firstName} ${lastName}`;}// bestfunction getFullName({ firstName, lastName }) {return `${firstName} ${lastName}`;}5.2 对数组使用解构赋值。
12345678const arr = [1, 2, 3, 4];// badconst first = arr[0];const second = arr[1];// goodconst [first, second] = arr;5.3 需要回传多个值时,使用对象解构,而不是数组解构。
为什么?增加属性或者改变排序不会改变调用时的位置。
1234567891011121314151617// badfunction processInput(input) {// then a miracle occursreturn [left, right, top, bottom];}// 调用时需要考虑回调数据的顺序。const [left, __, top] = processInput(input);// goodfunction processInput(input) {// then a miracle occursreturn { left, right, top, bottom };}// 调用时只选择需要的数据const { left, right } = processInput(input);
Strings
6.1 字符串使用单引号
''
。12345// badconst name = "Capt. Janeway";// goodconst name = 'Capt. Janeway';6.2 字符串超过 80 个字节应该使用字符串连接号换行。
6.3 注:过度使用字串连接符号可能会对性能造成影响。jsPerf 和 讨论.
12345678910111213// badconst 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.';// badconst 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.';// goodconst 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.';6.4 程序化生成字符串时,使用模板字符串代替字符串连接。
为什么?模板字符串更为简洁,更具可读性。
1234567891011121314// badfunction sayHi(name) {return 'How are you, ' + name + '?';}// badfunction sayHi(name) {return ['How are you, ', name, '?'].join();}// goodfunction sayHi(name) {return `How are you, ${name}?`;}
函数
7.1 使用函数声明代替函数表达式。
为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升(hoisted),而函数表达式只会把函数的引用变量名提升。这条规则使得箭头函数可以取代函数表达式。
1234567// badconst foo = function () {};// goodfunction foo() {}7.2 函数表达式:
1234// 立即调用的函数表达式 (IIFE)(() => {console.log('Welcome to the Internet. Please follow me.');})();7.3 永远不要在一个非函数代码块(
if
、while
等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。7.4 注意: ECMA-262 把
block
定义为一组语句。函数声明不是语句。阅读 ECMA-262 关于这个问题的说明。1234567891011121314// badif (currentUser) {function test() {console.log('Nope.');}}// goodlet test;if (currentUser) {test = () => {console.log('Yup.');};}7.5 永远不要把参数命名为
arguments
。这将取代原来函数作用域内的arguments
对象。123456789// badfunction nope(name, options, arguments) {// ...stuff...}// goodfunction yup(name, options, args) {// ...stuff...}7.6 不要使用
arguments
。可以选择 rest 语法...
替代。为什么?使用
...
能明确你要传入的参数。另外 rest 参数是一个真正的数组,而arguments
是一个类数组。12345678910// badfunction concatenateAll() {const args = Array.prototype.slice.call(arguments);return args.join('');}// goodfunction concatenateAll(...args) {return args.join('');}7.7 直接给函数的参数指定默认值,不要使用一个变化的函数参数。
12345678910111213141516171819202122// really badfunction handleThings(opts) {// 不!我们不应该改变函数参数。// 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。// 但这样的写法会造成一些 Bugs。//(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。)opts = opts || {};// ...}// still badfunction handleThings(opts) {if (opts === void 0) {opts = {};}// ...}// goodfunction handleThings(opts = {}) {// ...}7.8 直接给函数参数赋值时需要避免副作用。
为什么?因为这样的写法让人感到很困惑。
123456789var b = 1;// badfunction count(a = b++) {console.log(a);}count(); // 1count(); // 2count(3); // 3count(); // 3
箭头函数
8.1 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。
为什么?因为箭头函数创造了新的一个
this
执行环境(译注:参考 Arrow functions - JavaScript | MDN 和 ES6 arrow functions, syntax and lexical scoping),通常情况下都能满足你的需求,而且这样的写法更为简洁。为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。
123456789// bad[1, 2, 3].map(function (x) {return x * x;});// good[1, 2, 3].map((x) => {return x * x;});8.2 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和
return
都省略掉。如果不是,那就不要省略。为什么?语法糖。在链式调用中可读性很高。
为什么不?当你打算回传一个对象的时候。
1234567// good[1, 2, 3].map(x => x * x);// good[1, 2, 3].reduce((total, n) => {return total + n;}, 0);
构造器
9.1 总是使用
class
。避免直接操作prototype
。为什么? 因为
class
语法更为简洁更易读。12345678910111213141516171819202122// badfunction Queue(contents = []) {this._queue = [...contents];}Queue.prototype.pop = function() {const value = this._queue[0];this._queue.splice(0, 1);return value;}// goodclass Queue {constructor(contents = []) {this._queue = [...contents];}pop() {const value = this._queue[0];this._queue.splice(0, 1);return value;}}9.2 使用
extends
继承。为什么?因为
extends
是一个内建的原型继承方法并且不会破坏instanceof
。12345678910111213141516// badconst inherits = require('inherits');function PeekableQueue(contents) {Queue.apply(this, contents);}inherits(PeekableQueue, Queue);PeekableQueue.prototype.peek = function() {return this._queue[0];}// goodclass PeekableQueue extends Queue {peek() {return this._queue[0];}}9.3 方法可以返回
this
来帮助链式调用。12345678910111213141516171819202122232425262728293031// badJedi.prototype.jump = function() {this.jumping = true;return true;};Jedi.prototype.setHeight = function(height) {this.height = height;};const luke = new Jedi();luke.jump(); // => trueluke.setHeight(20); // => undefined// goodclass Jedi {jump() {this.jumping = true;return this;}setHeight(height) {this.height = height;return this;}}const luke = new Jedi();luke.jump().setHeight(20);9.4 可以写一个自定义的
toString()
方法,但要确保它能正常运行并且不会引起副作用。12345678910111213class Jedi {constructor(options = {}) {this.name = options.name || 'no name';}getName() {return this.name;}toString() {return `Jedi - ${this.getName()}`;}}
模块
10.1 总是使用模组 (
import
/export
) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。为什么?模块就是未来,让我们开始迈向未来吧。
1234567891011// badconst AirbnbStyleGuide = require('./AirbnbStyleGuide');module.exports = AirbnbStyleGuide.es6;// okimport AirbnbStyleGuide from './AirbnbStyleGuide';export default AirbnbStyleGuide.es6;// bestimport { es6 } from './AirbnbStyleGuide';export default es6;10.2 不要使用通配符 import。
为什么?这样能确保你只有一个默认 export。
12345// badimport * as AirbnbStyleGuide from './AirbnbStyleGuide';// goodimport AirbnbStyleGuide from './AirbnbStyleGuide';10.3 不要从 import 中直接 export。
为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。
12345678// bad// filename es6.jsexport { es6 as default } from './airbnbStyleGuide';// good// filename es6.jsimport { es6 } from './AirbnbStyleGuide';export default es6;
Iterators and Generators
11.1 不要使用 iterators。使用高阶函数例如
map()
和reduce()
替代for-of
。为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。
123456789101112131415161718const numbers = [1, 2, 3, 4, 5];// badlet sum = 0;for (let num of numbers) {sum += num;}sum === 15;// goodlet sum = 0;numbers.forEach((num) => sum += num);sum === 15;// best (use the functional force)const sum = numbers.reduce((total, num) => total + num, 0);sum === 15;11.2 现在还不要使用 generators。
为什么?因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators)
属性
12.1 使用
.
来访问对象的属性。12345678910const luke = {jedi: true,age: 28,};// badconst isJedi = luke['jedi'];// goodconst isJedi = luke.jedi;12.2 当通过变量访问属性时使用中括号
[]
。12345678910const luke = {jedi: true,age: 28,};function getProp(prop) {return luke[prop];}const isJedi = getProp('jedi');
变量
13.1 一直使用
const
来声明变量,如果不这样做就会产生全局变量。我们需要避免全局命名空间的污染。地球队长已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。)12345// badsuperPower = new SuperPower();// goodconst superPower = new SuperPower();13.2 使用
const
声明每一个变量。为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错
;
跟,
。123456789101112131415// badconst items = getItems(),goSportsTeam = true,dragonball = 'z';// bad// (compare to above, and try to spot the mistake)const items = getItems(),goSportsTeam = true;dragonball = 'z';// goodconst items = getItems();const goSportsTeam = true;const dragonball = 'z';13.3 将所有的
const
和let
分组为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。
123456789101112131415161718// badlet i, len, dragonball,items = getItems(),goSportsTeam = true;// badlet i;const items = getItems();let dragonball;const goSportsTeam = true;let len;// goodconst goSportsTeam = true;const items = getItems();let dragonball;let i;let length;13.4 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。
为什么?
let
和const
是块级作用域而不是函数作用域。12345678910111213141516171819202122232425262728293031323334353637383940// goodfunction() {test();console.log('doing stuff..');//..other stuff..const name = getName();if (name === 'test') {return false;}return name;}// bad - unnecessary function callfunction(hasName) {const name = getName();if (!hasName) {return false;}this.setFirstName(name);return true;}// goodfunction(hasName) {if (!hasName) {return false;}const name = getName();this.setFirstName(name);return true;}
Hoisting
14.1
var
声明会被提升至该作用域的顶部,但它们赋值不会提升。let
和const
被赋予了一种称为「暂时性死区(Temporal Dead Zones, TDZ)」的概念。这对于了解为什么 type of 不再安全相当重要。12345678910111213141516171819202122232425262728// 我们知道这样运行不了// (假设 notDefined 不是全局变量)function example() {console.log(notDefined); // => throws a ReferenceError}// 由于变量提升的原因,// 在引用变量后再声明变量是可以运行的。// 注:变量的赋值 `true` 不会被提升。function example() {console.log(declaredButNotAssigned); // => undefinedvar declaredButNotAssigned = true;}// 编译器会把函数声明提升到作用域的顶层,// 这意味着我们的例子可以改写成这样:function example() {let declaredButNotAssigned;console.log(declaredButNotAssigned); // => undefineddeclaredButNotAssigned = true;}// 使用 const 和 letfunction example() {console.log(declaredButNotAssigned); // => throws a ReferenceErrorconsole.log(typeof declaredButNotAssigned); // => throws a ReferenceErrorconst declaredButNotAssigned = true;}14.2 匿名函数表达式的变量名会被提升,但函数内容并不会。
123456789function example() {console.log(anonymous); // => undefinedanonymous(); // => TypeError anonymous is not a functionvar anonymous = function() {console.log('anonymous function expression');};}14.3 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。
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');}}14.4 函数声明的名称和函数体都会被提升。
1234567function example() {superPower(); // => Flyingfunction superPower() {console.log('Flying');}}想了解更多信息,参考 Ben Cherry 的 JavaScript Scoping & Hoisting。
比较运算符 & 等号
- 15.1 优先使用
===
和!==
而不是==
和!=
. 15.2 条件表达式例如
if
语句通过抽象方法ToBoolean
强制计算它们的表达式并且总是遵守下面的规则:- 对象 被计算为 true
- Undefined 被计算为 false
- Null 被计算为 false
- 布尔值 被计算为 布尔的值
- 数字 如果是 +0、-0、或 NaN 被计算为 false, 否则为 true
- 字符串 如果是空字符串
''
被计算为 false,否则为 true
1234if ([0]) {// true// An array is an object, objects evaluate to true}15.3 使用简写。
12345678910111213141516171819// badif (name !== '') {// ...stuff...}// goodif (name) {// ...stuff...}// badif (collection.length > 0) {// ...stuff...}// goodif (collection.length) {// ...stuff...}15.4 想了解更多信息,参考 Angus Croll 的 Truth Equality and JavaScript。
代码块
16.1 使用大括号包裹所有的多行代码块。
12345678910111213141516171819// badif (test)return false;// goodif (test) return false;// goodif (test) {return false;}// badfunction() { return false; }// goodfunction() {return false;}16.2 如果通过
if
和else
使用多行代码块,把else
放在if
代码块关闭括号的同一行。12345678910111213141516// badif (test) {thing1();thing2();}else {thing3();}// goodif (test) {thing1();thing2();} else {thing3();}
注释
17.1 使用
/** ... */
作为多行注释。包含描述、指定所有参数和返回值的类型和值。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;}17.2 使用
//
作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。12345678910111213141516171819202122232425// badconst active = true; // is current tab// good// is current tabconst active = true;// badfunction getType() {console.log('fetching type...');// set the default type to 'no type'const type = this._type || 'no type';return type;}// goodfunction getType() {console.log('fetching type...');// set the default type to 'no type'const type = this._type || 'no type';return type;}17.3 给注释增加
FIXME
或TODO
的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用FIXME -- need to figure this out
或者TODO -- need to implement
。17.4 使用
// FIXME
: 标注问题。123456class Calculator {constructor() {// FIXME: shouldn't use a global heretotal = 0;}}17.5 使用
// TODO
: 标注问题的解决方式。123456class Calculator {constructor() {// TODO: total should be configurable by an options paramthis.total = 0;}}
空白
18.1 使用 2 个空格作为缩进。
1234567891011121314// badfunction() {∙∙∙∙const name;}// badfunction() {∙const name;}// goodfunction() {∙∙const name;}18.2 在花括号前放一个空格。
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',});18.3 在控制语句(
if
、while
等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。12345678910111213141516171819// badif(isJedi) {fight ();}// goodif (isJedi) {fight();}// badfunction fight () {console.log ('Swooosh!');}// goodfunction fight() {console.log('Swooosh!');}18.4 使用空格把运算符隔开。
12345// badconst x=y+5;// goodconst x = y + 5;18.5 在文件末尾插入一个空行。
1234// bad(function(global) {// ...stuff...})(this);12345// bad(function(global) {// ...stuff...})(this);↵↵1234// good(function(global) {// ...stuff...})(this);↵18.5 在使用长方法链时进行缩进。使用前面的点
.
强调这是方法调用而不是新语句。12345678910111213141516171819202122232425262728293031323334// 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();// badconst 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);// goodconst leds = 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);18.6 在块末和新语句前插入空行。
1234567891011121314151617181920212223242526272829303132// badif (foo) {return bar;}return baz;// goodif (foo) {return bar;}return baz;// badconst obj = {foo() {},bar() {},};return obj;// goodconst obj = {foo() {},bar() {},};return obj;
逗号
19.1 行首逗号:不需要。
1234567891011121314151617181920212223242526272829// badconst story = [once, upon, aTime];// goodconst story = [once,upon,aTime,];// badconst hero = {firstName: 'Ada', lastName: 'Lovelace', birthYear: 1815, superPower: 'computers'};// goodconst hero = {firstName: 'Ada',lastName: 'Lovelace',birthYear: 1815,superPower: 'computers',};19.2 增加结尾的逗号: 需要。
为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的尾逗号问题。
123456789101112131415161718192021222324252627282930313233343536// bad - git diff without trailing commaconst hero = {firstName: 'Florence',- lastName: 'Nightingale'+ lastName: 'Nightingale',+ inventorOf: ['coxcomb graph', 'modern nursing']}// good - git diff with trailing commaconst hero = {firstName: 'Florence',lastName: 'Nightingale',+ inventorOf: ['coxcomb chart', 'modern nursing'],}// badconst hero = {firstName: 'Dana',lastName: 'Scully'};const heroes = ['Batman','Superman'];// goodconst hero = {firstName: 'Dana',lastName: 'Scully',};const heroes = ['Batman','Superman',];
分号
20.1 使用分号
1234567891011121314151617// bad(function() {const name = 'Skywalker'return name})()// good(() => {const name = 'Skywalker';return name;})();// good (防止函数在两个 IIFE 合并时被当成一个参数);(() => {const name = 'Skywalker';return name;})();
类型转换
- 21.1 在语句开始时执行类型转换。
21.2 字符串:
1234567// => this.reviewScore = 9;// badconst totalScore = this.reviewScore + '';// goodconst totalScore = String(this.reviewScore);21.3 对数字使用
parseInt
转换,并带上类型转换的基数。12345678910111213141516171819const inputValue = '4';// badconst val = new Number(inputValue);// badconst val = +inputValue;// badconst val = inputValue >> 0;// badconst val = parseInt(inputValue);// goodconst val = Number(inputValue);// goodconst val = parseInt(inputValue, 10);21.4 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决性能问题时,留个注释说清楚原因和你的目的。
123456// good/*** 使用 parseInt 导致我的程序变慢,* 改成使用位操作转换数字快多了。*/const val = inputValue >> 0;21.5 注: 小心使用位操作运算符。数字会被当成 64 位值,但是位操作运算符总是返回 32 位的整数(参考)。位操作处理大于 32 位的整数值时还会导致意料之外的行为。关于这个问题的讨论。最大的 32 位整数是 2,147,483,647:
1232147483647 >> 0 //=> 21474836472147483648 >> 0 //=> -21474836482147483649 >> 0 //=> -214748364721.6 布尔:
12345678910const age = 0;// badconst hasAge = new Boolean(age);// goodconst hasAge = Boolean(age);// goodconst hasAge = !!age;
命名规则
22.1 避免单字母命名。命名应具备描述性。
123456789// badfunction q() {// ...stuff...}// goodfunction query() {// ..stuff..}22.2 使用驼峰式命名对象、函数和实例。
12345678// badconst OBJEcttsssss = {};const this_is_my_object = {};function c() {}// goodconst thisIsMyObject = {};function thisIsMyFunction() {}22.3 使用帕斯卡式命名构造函数或类。
12345678910111213141516171819// badfunction user(options) {this.name = options.name;}const bad = new user({name: 'nope',});// goodclass User {constructor(options) {this.name = options.name;}}const good = new User({name: 'yup',});22.4 使用下划线
_
开头命名私有属性。123456// badthis.__firstName__ = 'Panda';this.firstName_ = 'Panda';// goodthis._firstName = 'Panda';22.5 别保存
this
的引用。使用箭头函数或 Function#bind。12345678910111213141516171819202122// badfunction foo() {const self = this;return function() {console.log(self);};}// badfunction foo() {const that = this;return function() {console.log(that);};}// goodfunction foo() {return () => {console.log(this);};}22.6 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。
123456789101112131415// file contentsclass CheckBox {// ...}export default CheckBox;// in some other file// badimport CheckBox from './checkBox';// badimport CheckBox from './check_box';// goodimport CheckBox from './CheckBox';22.7 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。
1234function makeStyleGuide() {}export default makeStyleGuide;22.8 当你导出单例、函数库、空对象时使用帕斯卡式命名。
123456const AirbnbStyleGuide = {es6: {}};export default AirbnbStyleGuide;
存取器
- 23.1 属性的存取函数不是必须的。
23.2 如果你需要存取函数时使用
getVal()
和setVal('hello')
。1234567891011// baddragon.age();// gooddragon.getAge();// baddragon.age(25);// gooddragon.setAge(25);23.3 如果属性是布尔值,使用
isVal()
或hasVal()
。123456789// badif (!dragon.age()) {return false;}// goodif (!dragon.hasAge()) {return false;}23.4 创建
get()
和set()
函数是可以的,但要保持一致。1234567891011121314class Jedi {constructor(options = {}) {const lightsaber = options.lightsaber || 'blue';this.set('lightsaber', lightsaber);}set(key, val) {this[key] = val;}get(key) {return this[key];}}
事件
24.1 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法:
12345678// bad$(this).trigger('listingUpdated', listing.id);...$(this).on('listingUpdated', function(e, listingId) {// do something with listingId});更好的写法:
12345678// good$(this).trigger('listingUpdated', { listingId : listing.id });...$(this).on('listingUpdated', function(e, data) {// do something with data.listingId});
jQuery
25.1 使用
$
作为存储 jQuery 对象的变量名前缀。12345// badconst sidebar = $('.sidebar');// goodconst $sidebar = $('.sidebar');25.2 缓存 jQuery 查询。
12345678910111213141516171819202122// badfunction setSidebar() {$('.sidebar').hide();// ...stuff...$('.sidebar').css({'background-color': 'pink'});}// goodfunction setSidebar() {const $sidebar = $('.sidebar');$sidebar.hide();// ...stuff...$sidebar.css({'background-color': 'pink'});}25.3 对 DOM 查询使用层叠
$('.sidebar ul')
或 父元素 > 子元素$('.sidebar > ul')
。 jsPerf25.4 对有作用域的 jQuery 对象查询使用
find
。1234567891011121314// bad$('ul', '.sidebar').hide();// bad$('.sidebar').find('ul').hide();// good$('.sidebar ul').hide();// good$('.sidebar > ul').hide();// good$sidebar.find('ul').hide();
ECMAScript 5 兼容性
ECMAScript 6 规范
- 27.1 以下是链接到 ES6 的各个特性的列表。
- Arrow Functions
- Classes
- Object Shorthand
- Object Concise
- Object Computed Properties
- Template Strings
- Destructuring
- Default Parameters
- Rest
- Array Spreads
- Let and Const
- Iterators and Generators
- Modules
测试
28.1 Yup.
123function() {return true;}
性能
- On Layout & Web Performance
- String vs Array Concat
- Try/Catch Cost In a Loop
- Bang Function
- jQuery Find vs Context, Selector
- innerHTML vs textContent for script text
- Long String Concatenation
- Loading…
资源
Learning ES6
- Draft ECMA 2015 (ES6) Spec
- ExploringJS
- ES6 Compatibility Table
- Comprehensive Overview of ES6 Features
Read This
Tools
- Code Style Linters
Other Styleguides
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
Other Styles
- Naming this in nested functions - Christian Johansen
- Conditional Callbacks - Ross Allen
- Popular JavaScript Coding Conventions on Github - JeongHoon Byun
- Multiple var statements in JavaScript, not superfluous - Ben Alman
Further Reading
- Understanding JavaScript Closures - Angus Croll
- Basic JavaScript for the impatient programmer - Dr. Axel Rauschmayer
- You Might Not Need jQuery - Zack Bloom & Adam Schwartz
- ES6 Features - Luke Hoban
- Frontend Guidelines - Benjamin De Cock
Books
- JavaScript: The Good Parts - Douglas Crockford
- JavaScript Patterns - Stoyan Stefanov
- Pro JavaScript Design Patterns - Ross Harmes and Dustin Diaz
- High Performance Web Sites: Essential Knowledge for Front-End Engineers - Steve Souders
- Maintainable JavaScript - Nicholas C. Zakas
- JavaScript Web Applications - Alex MacCaw
- Pro JavaScript Techniques - John Resig
- Smashing Node.js: JavaScript Everywhere - Guillermo Rauch
- Secrets of the JavaScript Ninja - John Resig and Bear Bibeault
- Human JavaScript - Henrik Joreteg
- Superhero.js - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
- JSBooks - Julien Bouquillon
- Third Party JavaScript - Ben Vinegar and Anton Kovalyov
- Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript - David Herman
- Eloquent JavaScript - Marijn Haverbeke
Blogs
- DailyJS
- JavaScript Weekly
- JavaScript, JavaScript…
- Bocoup Weblog
- Adequately Good
- NCZOnline
- Perfection Kills
- Ben Alman
- Dmitry Baranovskiy
- Dustin Diaz
- nettuts
Podcasts
License
(The MIT License)
Copyright (c) 2014 Airbnb
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
‘Software’), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.