ES6 编码规范
原文: https://github.com/yuche/javascript
目录
- 类型
- 引用
- 对象
- 数组
- 解构
- 字符串
- 函数
- 箭头函数
- 构造函数
- 模块
- 迭代器和生成器
- 属性
- 变量
- 提升
- 比较运算符和等号
- 代码块
- 注释
- 空白
- 逗号
- 分号
- 类型转换
- 命名规则
- 存取器
- 事件
- jQuery
- ECMAScript 5 兼容性
- ECMAScript 6 编码规范
- 测试
- 性能
- 相关资源
- 使用情况
- 其他翻译
- JavaScript 编码规范说明
- 讨论 JavaScript
- 贡献者
- 许可协议
类型
-
1.1 基本类型: 直接存取基本类型。
字符串
数值
布尔类型
null
undefined
1const foo = 1; 2let bar = foo; 3 4bar = 9; 5 6console.log(foo, bar); // => 1, 9
-
1.2 复杂类型: 通过引用的方式存取复杂类型。
对象
数组
函数
1const foo = [1, 2]; 2const bar = foo; 3 4bar[0] = 9; 5 6console.log(foo[0], bar[0]); // => 9, 9
引用
-
2.1 对所有的引用使用
const
;不要使用var
。为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。
1// bad 2var a = 1; 3var b = 2; 4 5// good 6const a = 1; 7const b = 2;
-
2.2 如果你一定需要可变动的引用,使用
let
代替var
。为什么?因为
let
是块级作用域,而var
是函数作用域。1// bad 2var count = 1; 3if (true) { 4 count += 1; 5} 6 7// good, use the let. 8let count = 1; 9if (true) { 10 count += 1; 11}
-
2.3 注意
let
和const
都是块级作用域。1// const 和 let 只存在于它们被定义的区块内。 2{ 3 let a = 1; 4 const b = 1; 5} 6console.log(a); // ReferenceError 7console.log(b); // ReferenceError
对象
-
3.1 使用字面值创建对象。
1// bad 2const item = new Object(); 3 4// good 5const item = {};
-
3.2 如果你的代码在浏览器环境下执行,别使用 保留字 作为键值。这样的话在 IE8 不会运行。 更多信息。 但在 ES6 模块和服务器端中使用没有问题。
1// bad 2const superman = { 3 default: { clark: 'kent' }, 4 private: true, 5}; 6 7// good 8const superman = { 9 defaults: { clark: 'kent' }, 10 hidden: true, 11};
-
3.3 使用同义词替换需要使用的保留字。
1// bad 2const superman = { 3 class: 'alien', 4}; 5 6// bad 7const superman = { 8 klass: 'alien', 9}; 10 11// good 12const superman = { 13 type: 'alien', 14};
-
3.4 创建有动态属性名的对象时,使用可被计算的属性名称。
为什么?因为这样可以让你在一个地方定义所有的对象属性。
1function getKey(k) { 2 return `a key named ${k}`; 3} 4 5// bad 6const obj = { 7 id: 5, 8 name: 'San Francisco', 9}; 10obj[getKey('enabled')] = true; 11 12// good 13const obj = { 14 id: 5, 15 name: 'San Francisco', 16 [getKey('enabled')]: true, 17};
-
3.5 使用对象方法的简写。
1// bad 2const atom = { 3 value: 1, 4 5 addValue: function (value) { 6 return atom.value + value; 7 }, 8}; 9 10// good 11const atom = { 12 value: 1, 13 14 addValue(value) { 15 return atom.value + value; 16 }, 17};
-
3.6 使用对象属性值的简写。
为什么?因为这样更短更有描述性。
1const lukeSkywalker = 'Luke Skywalker'; 2 3// bad 4const obj = { 5 lukeSkywalker: lukeSkywalker, 6}; 7 8// good 9const obj = { 10 lukeSkywalker, 11};
-
3.7 在对象属性声明前把简写的属性分组。
为什么?因为这样能清楚地看出哪些属性使用了简写。
1const anakinSkywalker = 'Anakin Skywalker'; 2const lukeSkywalker = 'Luke Skywalker'; 3 4// bad 5const obj = { 6 episodeOne: 1, 7 twoJedisWalkIntoACantina: 2, 8 lukeSkywalker, 9 episodeThree: 3, 10 mayTheFourth: 4, 11 anakinSkywalker, 12}; 13 14// good 15const obj = { 16 lukeSkywalker, 17 anakinSkywalker, 18 episodeOne: 1, 19 twoJedisWalkIntoACantina: 2, 20 episodeThree: 3, 21 mayTheFourth: 4, 22};
数组
-
4.1 使用字面值创建数组。
1// bad 2const items = new Array(); 3 4// good 5const items = [];
-
4.2 向数组添加元素时使用 Arrary#push 替代直接赋值。
1const someStack = []; 2 3 4// bad 5someStack[someStack.length] = 'abracadabra'; 6 7// good 8someStack.push('abracadabra');
-
4.3 使用拓展运算符
...
复制数组。1// bad 2const len = items.length; 3const itemsCopy = []; 4let i; 5 6for (i = 0; i < len; i++) { 7 itemsCopy[i] = items[i]; 8} 9 10// good 11const itemsCopy = [...items];
-
4.4 使用 Array#from 把一个类数组对象转换成数组。
1const foo = document.querySelectorAll('.foo'); 2const nodes = Array.from(foo);
解构
-
5.1 使用解构存取和使用多属性对象。
为什么?因为解构能减少临时引用属性。
1// bad 2function getFullName(user) { 3 const firstName = user.firstName; 4 const lastName = user.lastName; 5 6 return `${firstName} ${lastName}`; 7} 8 9// good 10function getFullName(obj) { 11 const { firstName, lastName } = obj; 12 return `${firstName} ${lastName}`; 13} 14 15// best 16function getFullName({ firstName, lastName }) { 17 return `${firstName} ${lastName}`; 18}
-
5.2 对数组使用解构赋值。
1const arr = [1, 2, 3, 4]; 2 3// bad 4const first = arr[0]; 5const second = arr[1]; 6 7// good 8const [first, second] = arr;
-
5.3 需要回传多个值时,使用对象解构,而不是数组解构。
为什么?增加属性或者改变排序不会改变调用时的位置。
1// bad 2function processInput(input) { 3 // then a miracle occurs 4 return [left, right, top, bottom]; 5} 6 7// 调用时需要考虑回调数据的顺序。 8const [left, __, top] = processInput(input); 9 10// good 11function processInput(input) { 12 // then a miracle occurs 13 return { left, right, top, bottom }; 14} 15 16// 调用时只选择需要的数据 17const { left, right } = processInput(input);
Strings
-
6.1 字符串使用单引号
''
。1// bad 2const name = "Capt. Janeway"; 3 4// good 5const name = 'Capt. Janeway';
-
6.2 字符串超过 80 个字节应该使用字符串连接号换行。
-
6.3 注:过度使用字串连接符号可能会对性能造成影响。jsPerf 和 讨论.
1// bad 2const 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.'; 3 4// bad 5const errorMessage = 'This is a super long error that was thrown because \ 6of Batman. When you stop to think about how Batman had anything to do \ 7with this, you would get nowhere \ 8fast.'; 9 10// good 11const errorMessage = 'This is a super long error that was thrown because ' + 12 'of Batman. When you stop to think about how Batman had anything to do ' + 13 'with this, you would get nowhere fast.';
-
6.4 程序化生成字符串时,使用模板字符串代替字符串连接。
为什么?模板字符串更为简洁,更具可读性。
1// bad 2function sayHi(name) { 3 return 'How are you, ' + name + '?'; 4} 5 6// bad 7function sayHi(name) { 8 return ['How are you, ', name, '?'].join(); 9} 10 11// good 12function sayHi(name) { 13 return `How are you, ${name}?`; 14}
函数
-
7.1 使用函数声明代替函数表达式。
为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升(hoisted),而函数表达式只会把函数的引用变量名提升。这条规则使得箭头函数可以取代函数表达式。
1// bad 2const foo = function () { 3}; 4 5// good 6function foo() { 7}
-
7.2 函数表达式:
1// 立即调用的函数表达式 (IIFE) 2(() => { 3 console.log('Welcome to the Internet. Please follow me.'); 4})();
-
7.3 永远不要在一个非函数代码块(
if
、while
等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 -
7.4 注意: ECMA-262 把
block
定义为一组语句。函数声明不是语句。阅读 ECMA-262 关于这个问题的说明。1// bad 2if (currentUser) { 3 function test() { 4 console.log('Nope.'); 5 } 6} 7 8// good 9let test; 10if (currentUser) { 11 test = () => { 12 console.log('Yup.'); 13 }; 14}
-
7.5 永远不要把参数命名为
arguments
。这将取代原来函数作用域内的arguments
对象。1// bad 2function nope(name, options, arguments) { 3 // ...stuff... 4} 5 6// good 7function yup(name, options, args) { 8 // ...stuff... 9}
-
7.6 不要使用
arguments
。可以选择 rest 语法...
替代。为什么?使用
...
能明确你要传入的参数。另外 rest 参数是一个真正的数组,而arguments
是一个类数组。1// bad 2function concatenateAll() { 3 const args = Array.prototype.slice.call(arguments); 4 return args.join(''); 5} 6 7// good 8function concatenateAll(...args) { 9 return args.join(''); 10}
-
7.7 直接给函数的参数指定默认值,不要使用一个变化的函数参数。
1// really bad 2function handleThings(opts) { 3 // 不!我们不应该改变函数参数。 4 // 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。 5 // 但这样的写法会造成一些 Bugs。 6 //(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。) 7 opts = opts || {}; 8 // ... 9} 10 11// still bad 12function handleThings(opts) { 13 if (opts === void 0) { 14 opts = {}; 15 } 16 // ... 17} 18 19// good 20function handleThings(opts = {}) { 21 // ... 22}
-
7.8 直接给函数参数赋值时需要避免副作用。
为什么?因为这样的写法让人感到很困惑。
1var b = 1;
2// bad
3function count(a = b++) {
4 console.log(a);
5}
6count(); // 1
7count(); // 2
8count(3); // 3
9count(); // 3
箭头函数
-
8.1 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。
为什么?因为箭头函数创造了新的一个
this
执行环境(译注:参考 Arrow functions - JavaScript | MDN 和 ES6 arrow functions, syntax and lexical scoping),通常情况下都能满足你的需求,而且这样的写法更为简洁。为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。
1// bad 2[1, 2, 3].map(function (x) { 3 const y = x + 1; 4 return x * y; 5}); 6 7// good 8[1, 2, 3].map((x) => { 9 const y = x + 1; 10 return x * y; 11});
-
8.2 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和
return
都省略掉。如果不是,那就不要省略。为什么?语法糖。在链式调用中可读性很高。
为什么不?当你打算回传一个对象的时候。
1// good 2[1, 2, 3].map(x => x * x); 3 4// good 5[1, 2, 3].reduce((total, n) => { 6 return total + n; 7}, 0);
构造器
-
9.1 总是使用
class
。避免直接操作prototype
。为什么? 因为
class
语法更为简洁更易读。1// bad 2function Queue(contents = []) { 3 this._queue = [...contents]; 4} 5Queue.prototype.pop = function() { 6 const value = this._queue[0]; 7 this._queue.splice(0, 1); 8 return value; 9} 10 11 12// good 13class Queue { 14 constructor(contents = []) { 15 this._queue = [...contents]; 16 } 17 pop() { 18 const value = this._queue[0]; 19 this._queue.splice(0, 1); 20 return value; 21 } 22}
-
9.2 使用
extends
继承。为什么?因为
extends
是一个内建的原型继承方法并且不会破坏instanceof
。1// bad 2const inherits = require('inherits'); 3function PeekableQueue(contents) { 4 Queue.apply(this, contents); 5} 6inherits(PeekableQueue, Queue); 7PeekableQueue.prototype.peek = function() { 8 return this._queue[0]; 9} 10 11// good 12class PeekableQueue extends Queue { 13 peek() { 14 return this._queue[0]; 15 } 16}
-
9.3 方法可以返回
this
来帮助链式调用。1// bad 2Jedi.prototype.jump = function() { 3 this.jumping = true; 4 return true; 5}; 6 7Jedi.prototype.setHeight = function(height) { 8 this.height = height; 9}; 10 11const luke = new Jedi(); 12luke.jump(); // => true 13luke.setHeight(20); // => undefined 14 15// good 16class Jedi { 17 jump() { 18 this.jumping = true; 19 return this; 20 } 21 22 setHeight(height) { 23 this.height = height; 24 return this; 25 } 26} 27 28const luke = new Jedi(); 29 30luke.jump() 31 .setHeight(20);
-
9.4 可以写一个自定义的
toString()
方法,但要确保它能正常运行并且不会引起副作用。1class Jedi { 2 constructor(options = {}) { 3 this.name = options.name || 'no name'; 4 } 5 6 getName() { 7 return this.name; 8 } 9 10 toString() { 11 return `Jedi - ${this.getName()}`; 12 } 13}
模块
-
10.1 总是使用模组 (
import
/export
) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。为什么?模块就是未来,让我们开始迈向未来吧。
1// bad 2const AirbnbStyleGuide = require('./AirbnbStyleGuide'); 3module.exports = AirbnbStyleGuide.es6; 4 5// ok 6import AirbnbStyleGuide from './AirbnbStyleGuide'; 7export default AirbnbStyleGuide.es6; 8 9// best 10import { es6 } from './AirbnbStyleGuide'; 11export default es6;
-
10.2 不要使用通配符 import。
为什么?这样能确保你只有一个默认 export。
1// bad 2import * as AirbnbStyleGuide from './AirbnbStyleGuide'; 3 4// good 5import AirbnbStyleGuide from './AirbnbStyleGuide';
-
10.3 不要从 import 中直接 export。
为什么?虽然一行代码简洁明了,但让 import 和 export 各司其职让事情能保持一致。
1// bad 2// filename es6.js 3export { es6 as default } from './airbnbStyleGuide'; 4 5// good 6// filename es6.js 7import { es6 } from './AirbnbStyleGuide'; 8export default es6;
Iterators and Generators
-
11.1 不要使用 iterators。使用高阶函数例如
map()
和reduce()
替代for-of
。为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。
1const numbers = [1, 2, 3, 4, 5]; 2 3// bad 4let sum = 0; 5for (let num of numbers) { 6 sum += num; 7} 8 9sum === 15; 10 11// good 12let sum = 0; 13numbers.forEach((num) => sum += num); 14sum === 15; 15 16// best (use the functional force) 17const sum = numbers.reduce((total, num) => total + num, 0); 18sum === 15;
-
11.2 现在还不要使用 generators。
为什么?因为它们现在还没法很好地编译到 ES5。 (译者注:目前(2016/03) Chrome 和 Node.js 的稳定版本都已支持 generators)
属性
-
12.1 使用
.
来访问对象的属性。1const luke = { 2 jedi: true, 3 age: 28, 4}; 5 6// bad 7const isJedi = luke['jedi']; 8 9// good 10const isJedi = luke.jedi;
-
12.2 当通过变量访问属性时使用中括号
[]
。1const luke = { 2 jedi: true, 3 age: 28, 4}; 5 6function getProp(prop) { 7 return luke[prop]; 8} 9 10const isJedi = getProp('jedi');
变量
-
13.1 一直使用
const
来声明变量,如果不这样做就会产生全局变量。我们需要避免全局命名空间的污染。地球队长已经警告过我们了。(译注:全局,global 亦有全球的意思。地球队长的责任是保卫地球环境,所以他警告我们不要造成「全球」污染。)1// bad 2superPower = new SuperPower(); 3 4// good 5const superPower = new SuperPower();
-
13.2 使用
const
声明每一个变量。为什么?增加新变量将变的更加容易,而且你永远不用再担心调换错
;
跟,
。1// bad 2const items = getItems(), 3 goSportsTeam = true, 4 dragonball = 'z'; 5 6// bad 7// (compare to above, and try to spot the mistake) 8const items = getItems(), 9 goSportsTeam = true; 10 dragonball = 'z'; 11 12// good 13const items = getItems(); 14const goSportsTeam = true; 15const dragonball = 'z';
-
13.3 将所有的
const
和let
分组为什么?当你需要把已赋值变量赋值给未赋值变量时非常有用。
1// bad 2let i, len, dragonball, 3 items = getItems(), 4 goSportsTeam = true; 5 6// bad 7let i; 8const items = getItems(); 9let dragonball; 10const goSportsTeam = true; 11let len; 12 13// good 14const goSportsTeam = true; 15const items = getItems(); 16let dragonball; 17let i; 18let length;
-
13.4 在你需要的地方给变量赋值,但请把它们放在一个合理的位置。
为什么?
let
和const
是块级作用域而不是函数作用域。1// good 2function() { 3 test(); 4 console.log('doing stuff..'); 5 6 //..other stuff.. 7 8 const name = getName(); 9 10 if (name === 'test') { 11 return false; 12 } 13 14 return name; 15} 16 17// bad - unnecessary function call 18function(hasName) { 19 const name = getName(); 20 21 if (!hasName) { 22 return false; 23 } 24 25 this.setFirstName(name); 26 27 return true; 28} 29 30// good 31function(hasName) { 32 if (!hasName) { 33 return false; 34 } 35 36 const name = getName(); 37 this.setFirstName(name); 38 39 return true; 40}
Hoisting
-
14.1
var
声明会被提升至该作用域的顶部,但它们赋值不会提升。let
和const
被赋予了一种称为「暂时性死区(Temporal Dead Zones, TDZ)」的概念。这对于了解为什么 type of 不再安全相当重要。1// 我们知道这样运行不了 2// (假设 notDefined 不是全局变量) 3function example() { 4 console.log(notDefined); // => throws a ReferenceError 5} 6 7// 由于变量提升的原因, 8// 在引用变量后再声明变量是可以运行的。 9// 注:变量的赋值 `true` 不会被提升。 10function example() { 11 console.log(declaredButNotAssigned); // => undefined 12 var declaredButNotAssigned = true; 13} 14 15// 编译器会把函数声明提升到作用域的顶层, 16// 这意味着我们的例子可以改写成这样: 17function example() { 18 let declaredButNotAssigned; 19 console.log(declaredButNotAssigned); // => undefined 20 declaredButNotAssigned = true; 21} 22 23// 使用 const 和 let 24function example() { 25 console.log(declaredButNotAssigned); // => throws a ReferenceError 26 console.log(typeof declaredButNotAssigned); // => throws a ReferenceError 27 const declaredButNotAssigned = true; 28}
-
14.2 匿名函数表达式的变量名会被提升,但函数内容并不会。
1function example() { 2 console.log(anonymous); // => undefined 3 4 anonymous(); // => TypeError anonymous is not a function 5 6 var anonymous = function() { 7 console.log('anonymous function expression'); 8 }; 9}
-
14.3 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。
1function example() { 2 console.log(named); // => undefined 3 4 named(); // => TypeError named is not a function 5 6 superPower(); // => ReferenceError superPower is not defined 7 8 var named = function superPower() { 9 console.log('Flying'); 10 }; 11} 12 13// the same is true when the function name 14// is the same as the variable name. 15function example() { 16 console.log(named); // => undefined 17 18 named(); // => TypeError named is not a function 19 20 var named = function named() { 21 console.log('named'); 22 } 23}
-
14.4 函数声明的名称和函数体都会被提升。
1function example() { 2 superPower(); // => Flying 3 4 function superPower() { 5 console.log('Flying'); 6 } 7}
-
想了解更多信息,参考 Ben Cherry 的 JavaScript Scoping & Hoisting。
比较运算符和等号
-
15.1 优先使用
===
和!==
而不是==
和!=
. -
15.2 条件表达式例如
if
语句通过抽象方法ToBoolean
强制计算它们的表达式并且总是遵守下面的规则:- 对象 被计算为 true
- Undefined 被计算为 false
- Null 被计算为 false
- 布尔值 被计算为 布尔的值
- 数字 如果是 +0、-0、或 NaN 被计算为 false, 否则为 true
- 字符串 如果是空字符串
''
被计算为 false,否则为 true
1if ([0]) { 2 // true 3 // An array is an object, objects evaluate to true 4}
-
15.3 使用简写。
1// bad 2if (name !== '') { 3 // ...stuff... 4} 5 6// good 7if (name) { 8 // ...stuff... 9} 10 11// bad 12if (collection.length > 0) { 13 // ...stuff... 14} 15 16// good 17if (collection.length) { 18 // ...stuff... 19}
-
15.4 想了解更多信息,参考 Angus Croll 的 Truth Equality and JavaScript。
代码块
-
16.1 使用大括号包裹所有的多行代码块。
1// bad 2if (test) 3 return false; 4 5// good 6if (test) return false; 7 8// good 9if (test) { 10 return false; 11} 12 13// bad 14function() { return false; } 15 16// good 17function() { 18 return false; 19}
-
16.2 如果通过
if
和else
使用多行代码块,把else
放在if
代码块关闭括号的同一行。1// bad 2if (test) { 3 thing1(); 4 thing2(); 5} 6else { 7 thing3(); 8} 9 10// good 11if (test) { 12 thing1(); 13 thing2(); 14} else { 15 thing3(); 16}
注释
-
17.1 使用
/** ... */
作为多行注释。包含描述、指定所有参数和返回值的类型和值。1// bad 2// make() returns a new element 3// based on the passed in tag name 4// 5// @param {String} tag 6// @return {Element} element 7function make(tag) { 8 9 // ...stuff... 10 11 return element; 12} 13 14// good 15/** 16 * make() returns a new element 17 * based on the passed in tag name 18 * 19 * @param {String} tag 20 * @return {Element} element 21 */ 22function make(tag) { 23 24 // ...stuff... 25 26 return element; 27}
-
17.2 使用
//
作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。1// bad 2const active = true; // is current tab 3 4// good 5// is current tab 6const active = true; 7 8// bad 9function getType() { 10 console.log('fetching type...'); 11 // set the default type to 'no type' 12 const type = this._type || 'no type'; 13 14 return type; 15} 16 17// good 18function getType() { 19 console.log('fetching type...'); 20 21 // set the default type to 'no type' 22 const type = this._type || 'no type'; 23 24 return type; 25}
-
17.3 给注释增加
FIXME
或TODO
的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用FIXME -- need to figure this out
或者TODO -- need to implement
。 -
17.4 使用
// FIXME
: 标注问题。1class Calculator { 2 constructor() { 3 // FIXME: shouldn't use a global here 4 total = 0; 5 } 6}
-
17.5 使用
// TODO
: 标注问题的解决方式。1class Calculator { 2 constructor() { 3 // TODO: total should be configurable by an options param 4 this.total = 0; 5 } 6}
空白
-
18.1 使用 2 个空格作为缩进。
1// bad 2function() { 3∙∙∙∙const name; 4} 5 6// bad 7function() { 8∙const name; 9} 10 11// good 12function() { 13∙∙const name; 14}
-
18.2 在花括号前放一个空格。
1// bad 2function test(){ 3 console.log('test'); 4} 5 6// good 7function test() { 8 console.log('test'); 9} 10 11// bad 12dog.set('attr',{ 13 age: '1 year', 14 breed: 'Bernese Mountain Dog', 15}); 16 17// good 18dog.set('attr', { 19 age: '1 year', 20 breed: 'Bernese Mountain Dog', 21});
-
18.3 在控制语句(
if
、while
等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。1// bad 2if(isJedi) { 3 fight (); 4} 5 6// good 7if (isJedi) { 8 fight(); 9} 10 11// bad 12function fight () { 13 console.log ('Swooosh!'); 14} 15 16// good 17function fight() { 18 console.log('Swooosh!'); 19}
-
18.4 使用空格把运算符隔开。
1// bad 2const x=y+5; 3 4// good 5const x = y + 5;
-
18.5 在文件末尾插入一个空行。
1// bad 2(function(global) { 3 // ...stuff... 4})(this);
1// bad 2(function(global) { 3 // ...stuff... 4})(this);↵ 5↵
1// good 2(function(global) { 3 // ...stuff... 4})(this);↵
-
18.5 在使用长方法链时进行缩进。使用前面的点
.
强调这是方法调用而不是新语句。1// bad 2$('#items').find('.selected').highlight().end().find('.open').updateCount(); 3 4// bad 5$('#items'). 6 find('.selected'). 7 highlight(). 8 end(). 9 find('.open'). 10 updateCount(); 11 12// good 13$('#items') 14 .find('.selected') 15 .highlight() 16 .end() 17 .find('.open') 18 .updateCount(); 19 20// bad 21const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) 22 .attr('width', (radius + margin) * 2).append('svg:g') 23 .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 24 .call(tron.led); 25 26// good 27const leds = stage.selectAll('.led') 28 .data(data) 29 .enter().append('svg:svg') 30 .classed('led', true) 31 .attr('width', (radius + margin) * 2) 32 .append('svg:g') 33 .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 34 .call(tron.led);
-
18.6 在块末和新语句前插入空行。
1// bad 2if (foo) { 3 return bar; 4} 5return baz; 6 7// good 8if (foo) { 9 return bar; 10} 11 12return baz; 13 14// bad 15const obj = { 16 foo() { 17 }, 18 bar() { 19 }, 20}; 21return obj; 22 23// good 24const obj = { 25 foo() { 26 }, 27 28 bar() { 29 }, 30}; 31 32return obj;
逗号
-
19.1 行首逗号:不需要。
1// bad 2const story = [ 3 once 4 , upon 5 , aTime 6]; 7 8// good 9const story = [ 10 once, 11 upon, 12 aTime, 13]; 14 15// bad 16const hero = { 17 firstName: 'Ada' 18 , lastName: 'Lovelace' 19 , birthYear: 1815 20 , superPower: 'computers' 21}; 22 23// good 24const hero = { 25 firstName: 'Ada', 26 lastName: 'Lovelace', 27 birthYear: 1815, 28 superPower: 'computers', 29};
-
19.2 增加结尾的逗号: 需要。
为什么? 这会让 git diffs 更干净。另外,像 babel 这样的转译器会移除结尾多余的逗号,也就是说你不必担心老旧浏览器的尾逗号问题。
1// bad - git diff without trailing comma 2const hero = { 3 firstName: 'Florence', 4- lastName: 'Nightingale' 5+ lastName: 'Nightingale', 6+ inventorOf: ['coxcomb graph', 'modern nursing'] 7} 8 9// good - git diff with trailing comma 10const hero = { 11 firstName: 'Florence', 12 lastName: 'Nightingale', 13+ inventorOf: ['coxcomb chart', 'modern nursing'], 14} 15 16// bad 17const hero = { 18 firstName: 'Dana', 19 lastName: 'Scully' 20}; 21 22const heroes = [ 23 'Batman', 24 'Superman' 25]; 26 27// good 28const hero = { 29 firstName: 'Dana', 30 lastName: 'Scully', 31}; 32 33const heroes = [ 34 'Batman', 35 'Superman', 36];
分号
-
20.1 使用分号
1// bad 2(function() { 3 const name = 'Skywalker' 4 return name 5})() 6 7// good 8(() => { 9 const name = 'Skywalker'; 10 return name; 11})(); 12 13// good (防止函数在两个 IIFE 合并时被当成一个参数) 14;(() => { 15 const name = 'Skywalker'; 16 return name; 17})();
类型转换
-
21.1 在语句开始时执行类型转换。
-
21.2 字符串:
1// => this.reviewScore = 9; 2 3// bad 4const totalScore = this.reviewScore + ''; 5 6// good 7const totalScore = String(this.reviewScore);
-
21.3 对数字使用
parseInt
转换,并带上类型转换的基数。1const inputValue = '4'; 2 3// bad 4const val = new Number(inputValue); 5 6// bad 7const val = +inputValue; 8 9// bad 10const val = inputValue >> 0; 11 12// bad 13const val = parseInt(inputValue); 14 15// good 16const val = Number(inputValue); 17 18// good 19const val = parseInt(inputValue, 10);
-
21.4 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决性能问题时,留个注释说清楚原因和你的目的。
1// good 2/** 3 * 使用 parseInt 导致我的程序变慢, 4 * 改成使用位操作转换数字快多了。 5 */ 6const val = inputValue >> 0;
-
21.5 注: 小心使用位操作运算符。数字会被当成 64 位值,但是位操作运算符总是返回 32 位的整数(参考)。位操作处理大于 32 位的整数值时还会导致意料之外的行为。关于这个问题的讨论。最大的 32 位整数是 2,147,483,647:
12147483647 >> 0 //=> 2147483647 22147483648 >> 0 //=> -2147483648 32147483649 >> 0 //=> -2147483647
-
21.6 布尔:
1const age = 0; 2 3// bad 4const hasAge = new Boolean(age); 5 6// good 7const hasAge = Boolean(age); 8 9// good 10const hasAge = !!age;
命名规则
-
22.1 避免单字母命名。命名应具备描述性。
1// bad 2function q() { 3 // ...stuff... 4} 5 6// good 7function query() { 8 // ..stuff.. 9}
-
22.2 使用驼峰式命名对象、函数和实例。
1// bad 2const OBJEcttsssss = {}; 3const this_is_my_object = {}; 4function c() {} 5 6// good 7const thisIsMyObject = {}; 8function thisIsMyFunction() {}
-
22.3 使用帕斯卡式命名构造函数或类。
1// bad 2function user(options) { 3 this.name = options.name; 4} 5 6const bad = new user({ 7 name: 'nope', 8}); 9 10// good 11class User { 12 constructor(options) { 13 this.name = options.name; 14 } 15} 16 17const good = new User({ 18 name: 'yup', 19});
-
22.4 不要使用下划线
_
结尾或开头来命名属性和方法。1// bad 2this.__firstName__ = 'Panda'; 3this.firstName_ = 'Panda'; 4this._firstName = 'Panda'; 5 6// good 7this.firstName = 'Panda';
-
22.5 别保存
this
的引用。使用箭头函数或 Function#bind。1// bad 2function foo() { 3 const self = this; 4 return function() { 5 console.log(self); 6 }; 7} 8 9// bad 10function foo() { 11 const that = this; 12 return function() { 13 console.log(that); 14 }; 15} 16 17// good 18function foo() { 19 return () => { 20 console.log(this); 21 }; 22}
-
22.6 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。
1// file contents 2class CheckBox { 3 // ... 4} 5export default CheckBox; 6 7// in some other file 8// bad 9import CheckBox from './checkBox'; 10 11// bad 12import CheckBox from './check_box'; 13 14// good 15import CheckBox from './CheckBox';
-
22.7 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。
1function makeStyleGuide() { 2} 3 4export default makeStyleGuide;
-
22.8 当你导出单例、函数库、空对象时使用帕斯卡式命名。
1const AirbnbStyleGuide = { 2 es6: { 3 } 4}; 5 6export default AirbnbStyleGuide;
存取器
-
23.1 属性的存取函数不是必须的。
-
23.2 如果你需要存取函数时使用
getVal()
和setVal('hello')
。1// bad 2dragon.age(); 3 4// good 5dragon.getAge(); 6 7// bad 8dragon.age(25); 9 10// good 11dragon.setAge(25);
-
23.3 如果属性是布尔值,使用
isVal()
或hasVal()
。1// bad 2if (!dragon.age()) { 3 return false; 4} 5 6// good 7if (!dragon.hasAge()) { 8 return false; 9}
-
23.4 创建
get()
和set()
函数是可以的,但要保持一致。1class Jedi { 2 constructor(options = {}) { 3 const lightsaber = options.lightsaber || 'blue'; 4 this.set('lightsaber', lightsaber); 5 } 6 7 set(key, val) { 8 this[key] = val; 9 } 10 11 get(key) { 12 return this[key]; 13 } 14}
事件
-
24.1 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法:
1// bad 2$(this).trigger('listingUpdated', listing.id); 3 4... 5 6$(this).on('listingUpdated', function(e, listingId) { 7 // do something with listingId 8});
更好的写法:
1// good 2$(this).trigger('listingUpdated', { listingId : listing.id }); 3 4... 5 6$(this).on('listingUpdated', function(e, data) { 7 // do something with data.listingId 8});
jQuery
-
25.1 使用
$
作为存储 jQuery 对象的变量名前缀。1// bad 2const sidebar = $('.sidebar'); 3 4// good 5const $sidebar = $('.sidebar');
-
25.2 缓存 jQuery 查询。
1// bad 2function setSidebar() { 3 $('.sidebar').hide(); 4 5 // ...stuff... 6 7 $('.sidebar').css({ 8 'background-color': 'pink' 9 }); 10} 11 12// good 13function setSidebar() { 14 const $sidebar = $('.sidebar'); 15 $sidebar.hide(); 16 17 // ...stuff... 18 19 $sidebar.css({ 20 'background-color': 'pink' 21 }); 22}
-
25.3 对 DOM 查询使用层叠
$('.sidebar ul')
或 父元素 > 子元素$('.sidebar > ul')
。 jsPerf -
25.4 对有作用域的 jQuery 对象查询使用
find
。1// bad 2$('ul', '.sidebar').hide(); 3 4// bad 5$('.sidebar').find('ul').hide(); 6 7// good 8$('.sidebar ul').hide(); 9 10// good 11$('.sidebar > ul').hide(); 12 13// good 14$sidebar.find('ul').hide();