本文主要是介绍Airbnb JavaScript 编码规范(涵盖 ECMAScript 6+),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一份最合理的 JavasScript 编码规范。_ 翻译自:
https://github.com/airbnb/javascript
注意:本指南假定您使用的是Babel,并要求您使用 babel-preset-airbnb 或相当的预设插件。它还假定您正在您的应用程序中安装 shims/polyfills,实用airbnb-browser-shims或相当的插件。
目录
- 类型
- 引用
- 对象
- 数组
- 解构
- 字符串
- 函数
- 箭头函数
- 类 和 构造函数
- 模块
- 迭代器 和 生成器
- 属性
- 变量
- 提升
- 比较运算符 和 等号
- 代码块
- 控制语句
- 注释
- 空白
- 逗号
- 分号
- 类型转换
- 命名规则
- 存取器
- 事件
- jQuery
- ECMAScript 5 兼容性
- ECMAScript 6+ (ES 2015+) 编码风格
- 标准库
- 测试
- 性能
- 相关资源
类型
– 1.1 基本类型: 当您访问一个基本类型时,您将直接处理它的值。
string
number
boolean
null
undefined
symbol
const foo = 1;
let bar = foo;bar = 9;console.log(foo, bar); // => 1, 9
- Symbols 不能被完全 polyfill, 所以不应该在目标浏览器/环境不支持它们的情况下使用它们。
– 1.2 复合类型: 当你访问一个复合类型时,你需要引用它的值。
object
array
function
JavaScript 代码:1. const foo = [1, 2]; 2. const bar = foo; 3. 4. bar[0] = 9; 5. 6. console.log(foo[0], bar[0]); // => 9, 9
返回目录
引用 References
– 2.1 对所有的引用使用 const
;不要使用 var
。eslint: prefer-const
, no-const-assign
为什么? 这可以确保你无法对引用重新分配,重新分配可能会导致 bug 和难以理解的代码。
JavaScript 代码:1. // bad 2. var a = 1; 3. var b = 2; 4. 5. // good 6. const a = 1; 7. const b = 2;
– 2.2 如果你一定需要可变动的引用,使用 let
代替 var
。eslint: no-var
jscs: disallowVar
为什么?因为
let
是块级作用域,而var
是函数作用域。
JavaScript 代码:1. // bad 2. var count = 1; 3. if (true) { 4. count += 1; 5. } 6. 7. // good, 使用 let. 8. let count = 1; 9. if (true) { 10. count += 1; 11. }
JavaScript 代码:1. // const 和 let 只存在于定义它们的代码块内。 2. { 3. let a = 1; 4. const b = 1; 5. } 6. console.log(a); // ReferenceError,引用错误 7. console.log(b); // ReferenceError,引用错误
返回目录
对象 Objects
– 3.1 使用字面量语法创建对象。 eslint: no-new-object
JavaScript 代码:1. // bad 2. const item = new Object(); 3. 4. // good 5. const item = {};
– 3.2 当创建带有动态属性名称的对象时使用计算的属性名称。
为什么? 它们允许你在一个地方定义一个对象的所有属性。
JavaScript 代码:1. function getKey(k) { 2. return `a key named ${k}`; 3. } 4. 5. // bad 6. const obj = { 7. id: 5, 8. name: 'San Francisco', 9. }; 10. obj[getKey('enabled')] = true; 11. 12. // good 13. const obj = { 14. id: 5, 15. name: 'San Francisco', 16. [getKey('enabled')]: true, 17. };
– 3.3 使用对象方法速记语法。 eslint: object-shorthand
jscs: requireEnhancedObjectLiterals
JavaScript 代码:1. // bad 2. const atom = { 3. value: 1, 4. 5. addValue: function (value) { 6. return atom.value + value; 7. }, 8. }; 9. 10. // good 11. const atom = { 12. value: 1, 13. 14. addValue(value) { 15. return atom.value + value; 16. }, 17. };
– 3.4 使用对象属性速记语法。eslint: object-shorthand
jscs: requireEnhancedObjectLiterals
为什么?编写代码和描述更加简短。
JavaScript 代码:1. const lukeSkywalker = 'Luke Skywalker'; 2. 3. // bad 4. const obj = { 5. lukeSkywalker: lukeSkywalker, 6. }; 7. 8. // good 9. const obj = { 10. lukeSkywalker, 11. };
为什么?更容易看出哪些属性在使用速记语法。
JavaScript 代码:1. const anakinSkywalker = 'Anakin Skywalker'; 2. const lukeSkywalker = 'Luke Skywalker'; 3. 4. // bad 5. const obj = { 6. episodeOne: 1, 7. twoJediWalkIntoACantina: 2, 8. lukeSkywalker, 9. episodeThree: 3, 10. mayTheFourth: 4, 11. anakinSkywalker, 12. }; 13. 14. // good 15. const obj = { 16. lukeSkywalker, 17. anakinSkywalker, 18. episodeOne: 1, 19. twoJediWalkIntoACantina: 2, 20. episodeThree: 3, 21. mayTheFourth: 4, 22. };
– 3.6 只用引号引无效标识符的属性。 eslint: quote-props
jscs: disallowQuotedKeysInObjects
为什么?一般来说,我们认为比较容易阅读。它改进了语法高亮显示,并且更容易被许多JS引擎优化。
JavaScript 代码:1. // bad 2. const bad = { 3. 'foo': 3, 4. 'bar': 4, 5. 'data-blah': 5, 6. }; 7. 8. // good 9. const good = { 10. foo: 3, 11. bar: 4, 12. 'data-blah': 5, 13. };
– 3.7 不要直接调用 Object.prototype
的方法,比如 hasOwnProperty
, propertyIsEnumerable
, 和 isPrototypeOf
.
为什么?这些方法可能会被对象的属性所覆盖 – 比如
{ hasOwnProperty: false }
– 或者,对象可能是空(null
)对象(Object.create(null)
)。
JavaScript 代码:1. // bad 2. console.log(object.hasOwnProperty(key)); 3. 4. // good 5. console.log(Object.prototype.hasOwnProperty.call(object, key)); 6. 7. // best 8. const has = Object.prototype.hasOwnProperty; // 在模块作用域内,缓存查找一次。 9. /* or */ 10. import has from 'has'; 11. // ... 12. console.log(has.call(object, key));
– 3.8 用对象展开操作符浅复制对象,优先于Object.assign
。使用对象剩余操作符来获得一个省略某些属性的新对象。
JavaScript 代码:1. // very bad 2. const original = { a: 1, b: 2 }; 3. const copy = Object.assign(original, { c: 3 }); // `original` 是可变的 ಠ_ಠ 4. delete copy.a; // so does this 5. 6. // bad 7. const original = { a: 1, b: 2 }; 8. const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } 9. 10. // good 11. const original = { a: 1, b: 2 }; 12. const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } 13. 14. const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
返回目录
数组 Arrays
– 4.1 使用字面量创建数组。 eslint: no-array-constructor
JavaScript 代码:1. // bad 2. const items = new Array(); 3. 4. // good 5. const items = [];
– 4.2 在向数组添加元素时使用 Array#push 替代直接赋值。
JavaScript 代码:1. const someStack = []; 2. 3. // bad 4. someStack[someStack.length] = 'abracadabra'; 5. 6. // good 7. someStack.push('abracadabra');
JavaScript 代码:1. // bad 2. const len = items.length; 3. const itemsCopy = []; 4. let i; 5. 6. for (i = 0; i
解构 Destructuring
– 5.1 当访问和使用对象的多个属性时,请使用对象解构。eslint: prefer-destructuring
jscs: requireObjectDestructuring
为什么?解构可以在你建这些属性的临时引用时,为你节省时间。
JavaScript 代码:1. // bad 2. function getFullName(user) { 3. const firstName = user.firstName; 4. const lastName = user.lastName; 5. 6. return `${firstName} ${lastName}`; 7. } 8. 9. // good 10. function getFullName(user) { 11. const { firstName, lastName } = user; 12. return `${firstName} ${lastName}`; 13. } 14. 15. // best 16. function getFullName({ firstName, lastName }) { 17. return `${firstName} ${lastName}`; 18. }
– 5.2 使用数组解构。 eslint: prefer-destructuring
jscs: requireArrayDestructuring
JavaScript 代码:1. const arr = [1, 2, 3, 4]; 2. 3. // bad 4. const first = arr[0]; 5. const second = arr[1]; 6. 7. // good 8. const [first, second] = arr;
– 5.3 使用对象解构来实现多个返回值,而不是数组解构。jscs: disallowArrayDestructuringReturn
为什么? 您可以随着时间的推移添加新的属性或更改排序,而不会改变调用时的位置。
JavaScript 代码:1. // bad 2. function processInput(input) { 3. // 那么奇迹发生了 4. return [left, right, top, bottom]; 5. } 6. 7. // 调用者需要考虑返回数据的顺序 8. const [left, __, top] = processInput(input); 9. 10. // good 11. function processInput(input) { 12. // 那么奇迹发生了 13. return { left, right, top, bottom }; 14. } 15. 16. // 调用者只选择他们需要的数据 17. const { left, top } = processInput(input);
返回目录
字符串 Strings
– 6.1 字符串使用单引号 ''
。 eslint: quotes
jscs: validateQuoteMarks
JavaScript 代码:1. // bad 2. const name = "Capt. Janeway"; 3. 4. // bad - 模板字面量应该包含插值或换行符 5. const name = `Capt. Janeway`; 6. 7. // good 8. const name = 'Capt. Janeway';
– 6.2 超过100个字符,导致换行的字符串不应使用字符串连接符写成多行。
为什么? 连接字符串是痛苦的工作,而且使代码不易于搜索。
JavaScript 代码:1. // bad 2. const errorMessage = 'This is a super long error that was thrown because \ 3. of Batman. When you stop to think about how Batman had anything to do \ 4. with this, you would get nowhere \ 5. fast.'; 6. 7. // bad 8. const errorMessage = 'This is a super long error that was thrown because ' + 9. 'of Batman. When you stop to think about how Batman had anything to do ' + 10. 'with this, you would get nowhere fast.'; 11. 12. // good 13. const 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.3 以编程方式构建字符串时,请使用模板字符串而不是字符串连接。eslint: prefer-template
template-curly-spacing
jscs: requireTemplateStrings
为什么? 模板字符串为你提供了更好的可读性,简洁的语法,正确的换行符和字符串插值功能。
JavaScript 代码:1. // bad 2. function sayHi(name) { 3. return 'How are you, ' + name + '?'; 4. } 5. 6. // bad 7. function sayHi(name) { 8. return ['How are you, ', name, '?'].join(); 9. } 10. 11. // bad 12. function sayHi(name) { 13. return `How are you, ${ name }?`; 14. } 15. 16. // good 17. function sayHi(name) { 18. return `How are you, ${name}?`; 19. }
– 6.4 永远不要在字符串上使用 eval()
,它会打开太多的漏洞。 eslint: no-eval
– 6.5 不要转义字符串中不必要转义的字符。 eslint: no-useless-escape
为什么?反斜杠会破坏可读性,因此只有在必要时才转义。
JavaScript 代码:1. // bad 2. const foo = '\'this\' \i\s \"quoted\"'; 3. 4. // good 5. const foo = '\'this\' is "quoted"'; 6. const foo = `my name is '${name}'`;
返回目录
函数 Functions
– 7.1 使用命名函数表达式而不是函数声明。 eslint: func-style
jscs: disallowFunctionDeclarations
为什么? 函数声明很容易被提升(Hoisting),你可以在函数被定义之前引用该函数。这对可读性和可维护性来说都是不利的。如果你发现一个函数的定义很大或很复杂,以至于妨碍了解文件的其他部分,那么也许是时候把它提取到自己的模块中去!不要忘记显式地命名表达式,不管该名称是否从包含变量中推断出来的(在现代浏览器中,或在使用编译器如Babel 时经常出现这种情况)。这消除了关于Error的调用堆栈的任何假设。(讨论)
JavaScript 代码:1. // bad 2. function foo() { 3. // ... 4. } 5. 6. // bad 7. const foo = function () { 8. // ... 9. }; 10. 11. // good 12. // 用明显区别于变量引用调用的词汇命名 13. const short = function longUniqueMoreDescriptiveLexicalFoo() { 14. // ... 15. };
– 7.2 用圆括号包裹立即调用函数表达式 (IIFE)。 eslint: wrap-iife
jscs: requireParenthesesAroundIIFE
为什么?一个立即调用函数表达式是一个单独的单元 – 将函数表达式包裹在括号中,后面再跟一个调用括号,这看上去很赶紧。请注意,在模块的世界中,你几乎不需要 IIFE。
JavaScript 代码:1. // 立即调用函数表达式 (IIFE) 2. (function () { 3. console.log('Welcome to the Internet. Please follow me.'); 4. }());
– 7.3 永远不要在一个非函数代码块(if
、while
等)中声明一个函数,把那个函数赋给一个变量代替。浏览器允许你这么做,但是它们都以不同的方式解析。 eslint: no-loop-func
– 7.4 注意: ECMA-262 把 block
定义为一组语句。函数声明不是语句。
JavaScript 代码:1. // bad 2. if (currentUser) { 3. function test() { 4. console.log('Nope.'); 5. } 6. } 7. 8. // good 9. let test; 10. if (currentUser) { 11. test = () => { 12. console.log('Yup.'); 13. }; 14. }
– 7.5 永远不要把参数命名为 arguments
。这将会覆盖原来函数作用域内的 arguments
对象。
JavaScript 代码:1. // bad 2. function foo(name, options, arguments) { 3. // ... 4. } 5. 6. // good 7. function foo(name, options, args) { 8. // ... 9. }
– 7.6 不要使用 arguments
。可以选择 rest 语法 ...
替代。eslint: prefer-rest-params
为什么?使用
...
能明确你要传入的参数。另外 rest(剩余)参数是一个真正的数组,而arguments
是一个类数组(Array-like)。
JavaScript 代码:1. // bad 2. function concatenateAll() { 3. const args = Array.prototype.slice.call(arguments); 4. return args.join(''); 5. } 6. 7. // good 8. function concatenateAll(...args) { 9. return args.join(''); 10. }
– 7.7 使用默认参数语法,而不要使用一个变化的函数参数。
JavaScript 代码:1. // really bad 2. function handleThings(opts) { 3. // 不!我们不应该改变函数参数。 4. // 更加糟糕: 如果参数 opts 是 falsy(假值) 的话,它将被设置为一个对象, 5. // 这可能是你想要的,但它可以引起一些小的错误。 6. opts = opts || {}; 7. // ... 8. } 9. 10. // still bad 11. function handleThings(opts) { 12. if (opts === void 0) { 13. opts = {}; 14. } 15. // ... 16. } 17. 18. // good 19. function handleThings(opts = {}) { 20. // ... 21. }
为什么?因为这样写会让人感到很困惑。
JavaScript 代码:1. var b = 1; 2. // bad 3. function count(a = b++) { 4. console.log(a); 5. } 6. count(); // 1 7. count(); // 2 8. count(3); // 3 9. count(); // 3
JavaScript 代码:1. // bad 2. function handleThings(opts = {}, name) { 3. // ... 4. } 5. 6. // good 7. function handleThings(name, opts = {}) { 8. // ... 9. }
– 7.10 切勿使用 Function 构造函数来创建新函数。 eslint: no-new-func
为什么? 以这种方式创建一个函数,与 eval() 类似,会对字符串求值,这会打开漏洞。
JavaScript 代码:1. // bad 2. var add = new Function('a', 'b', 'return a + b'); 3. 4. // still bad 5. var subtract = Function('a', 'b', 'return a - b');
– 7.11 隔开函数签名,括号两边用空格隔开。 eslint: space-before-function-paren
space-before-blocks
为什么?这样做有益代码的一致性,添加或删除函数名时不需要添加或删除空格。
JavaScript 代码:1. // bad 2. const f = function(){}; 3. const g = function (){}; 4. const h = function() {}; 5. 6. // good 7. const x = function () {}; 8. const y = function a() {};
– 7.12 不要改变参数。 eslint: no-param-reassign
为什么?操作作为参数传入的对象,可能会在调用原始对象时造成不必要的变量副作用。(愚人码头注:对象是引用类型)
JavaScript 代码:1. // bad 2. function f1(obj) { 3. obj.key = 1; 4. } 5. 6. // good 7. function f2(obj) { 8. const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; 9. }
– 7.13 参数不要重新赋值。 eslint: no-param-reassign
为什么? 重新分配参数可能会导致意外的行为,特别是在访问
arguments
对象时。它也可能导性能化问题,特别是在V8中。
JavaScript 代码:1. // bad 2. function f1(a) { 3. a = 1; 4. // ... 5. } 6. 7. function f2(a) { 8. if (!a) { a = 1; } 9. // ... 10. } 11. 12. // good 13. function f3(a) { 14. const b = a || 1; 15. // ... 16. } 17. 18. function f4(a = 1) { 19. // ... 20. }
– 7.14 优先使用展开运算符 ...
来调用可变参数函数。 eslint: prefer-spread
为什么? 它更清洁,你不需要提供一个上下文,而且你不能轻易地实用
apply
和new
。
JavaScript 代码:1. // bad 2. const x = [1, 2, 3, 4, 5]; 3. console.log.apply(console, x); 4. 5. // good 6. const x = [1, 2, 3, 4, 5]; 7. console.log(...x); 8. 9. // bad 10. new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); 11. 12. // good 13. new Date(...[2016, 8, 5]);
– 7.15 具有多行签名或调用的函数,应该像本指南中的其他多行列表一样缩进:每一项都独占一行,最后一项上有一个尾逗号。
JavaScript 代码:1. // bad 2. function foo(bar, 3. baz, 4. quux) { 5. // ... 6. } 7. 8. // good 9. function foo( 10. bar, 11. baz, 12. quux, 13. ) { 14. // ... 15. } 16. 17. // bad 18. console.log(foo, 19. bar, 20. baz); 21. 22. // good 23. console.log( 24. foo, 25. bar, 26. baz, 27. );
返回目录
箭头函数 Arrow Functions
为什么? 它创建了一个在
this
上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。(愚人码头注:参考 Arrow functions – JavaScript | MDN 和 ES6 arrow functions, syntax and lexical scoping)为什么不? 如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个声明函数上。
JavaScript 代码: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 如果函数体由一个返回无副作用(side effect)的expression(表达式)的单行语句组成,那么可以省略大括号并使用隐式返回。否则,保留大括号并使用 return
语句。eslint: arrow-parens
, arrow-body-style
jscs: disallowParenthesesAroundArrowParam
, requireShorthandArrowFunctions
愚人码头注,什么是副作用(side effect)?一段代码,即在不需要的情况下,创建一个变量并在整个作用域内可用。
为什么? 语法糖。 当多个函数链式调用时,可读性更高。
JavaScript 代码:1. // bad 2. [1, 2, 3].map(number => { 3. const nextNumber = number + 1; 4. `A string containing the ${nextNumber}.`; 5. }); 6. 7. // good 8. [1, 2, 3].map(number => `A string containing the ${number}.`); 9. 10. // good 11. [1, 2, 3].map((number) => { 12. const nextNumber = number + 1; 13. return `A string containing the ${nextNumber}.`; 14. }); 15. 16. // good 17. [1, 2, 3].map((number, index) => ({ 18. [index]: number, 19. })); 20. 21. // No implicit return with side effects 22. function foo(callback) { 23. const val = callback(); 24. if (val === true) { 25. // Do something if callback returns true 26. } 27. } 28. 29. let bool = false; 30. 31. // bad 32. foo(() => bool = true); 33. 34. // good 35. foo(() => { 36. bool = true; 37. });
– 8.3 如果表达式跨多行,将其包裹在括号中,可以提高可读性。
为什么? 它清楚地显示了函数开始和结束的位置。
JavaScript 代码:1. // bad 2. ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( 3. httpMagicObjectWithAVeryLongName, 4. httpMethod, 5. ) 6. ); 7. 8. // good 9. ['get', 'post', 'put'].map(httpMethod => ( 10. Object.prototype.hasOwnProperty.call( 11. httpMagicObjectWithAVeryLongName, 12. httpMethod, 13. ) 14. ));
– 8.4 如果你的函数只有一个参数并且不使用大括号,则可以省略参数括号。否则,为了清晰和一致性,总是给参数加上括号。
注意:总是使用圆括号也是可以被lint工具接受的,在这种情况下 使用 eslint 的 “always” 选项,或者 jscs 中不要包含 disallowParenthesesAroundArrowParam
选项。 eslint: arrow-parens
jscs: disallowParenthesesAroundArrowParam
为什么? 不造成视觉上的混乱。
JavaScript 代码:1. // bad 2. [1, 2, 3].map((x) => x * x); 3. 4. // good 5. [1, 2, 3].map(x => x * x); 6. 7. // good 8. [1, 2, 3].map(number => ( 9. `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` 10. )); 11. 12. // bad 13. [1, 2, 3].map(x => { 14. const y = x + 1; 15. return x * y; 16. }); 17. 18. // good 19. [1, 2, 3].map((x) => { 20. const y = x + 1; 21. return x * y; 22. });
– 8.5 避免使用比较运算符(< =
, >=
)时,混淆箭头函数语法(=>
)。 eslint: no-confusing-arrow
JavaScript 代码:1. // bad 2. const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; 3. 4. // bad 5. const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; 6. 7. // good 8. const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); 9. 10. // good 11. const itemHeight = (item) => { 12. const { height, largeSize, smallSize } = item; 13. return height > 256 ? largeSize : smallSize; 14. };
返回目录
类 Classes & 构造函数 Constructors
– 9.1 总是使用 class
。避免直接操作 prototype
。
为什么? 因为
class
语法更为简洁更易读。
JavaScript 代码:1. // bad 2. function Queue(contents = []) { 3. this.queue = [...contents]; 4. } 5. Queue.prototype.pop = function () { 6. const value = this.queue[0]; 7. this.queue.splice(0, 1); 8. return value; 9. }; 10. 11. // good 12. class Queue { 13. constructor(contents = []) { 14. this.queue = [...contents]; 15. } 16. pop() { 17. const value = this.queue[0]; 18. this.queue.splice(0, 1); 19. return value; 20. } 21. }
为什么?因为
extends
是一个内置的原型继承方法并且不会破坏instanceof
。
JavaScript 代码:1. // bad 2. const inherits = require('inherits'); 3. function PeekableQueue(contents) { 4. Queue.apply(this, contents); 5. } 6. inherits(PeekableQueue, Queue); 7. PeekableQueue.prototype.peek = function () { 8. return this.queue[0]; 9. }; 10. 11. // good 12. class PeekableQueue extends Queue { 13. peek() { 14. return this.queue[0]; 15. } 16. }
JavaScript 代码:1. // bad 2. Jedi.prototype.jump = function () { 3. this.jumping = true; 4. return true; 5. }; 6. 7. Jedi.prototype.setHeight = function (height) { 8. this.height = height; 9. }; 10. 11. const luke = new Jedi(); 12. luke.jump(); // => true 13. luke.setHeight(20); // => undefined 14. 15. // good 16. class 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. 28. const luke = new Jedi(); 29. 30. luke.jump() 31. .setHeight(20);
– 9.4 可以写一个自定义的 toString() 方法,但要确保它能正常运行并且不会引起副作用。
JavaScript 代码:1. class 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. }
– 9.5 如果没有指定,类有一个默认的构造函数。一个空的构造函数或者只是委托给父类则不是必须的。 eslint: no-useless-constructor
JavaScript 代码:1. // bad 2. class Jedi { 3. constructor() {} 4. 5. getName() { 6. return this.name; 7. } 8. } 9. 10. // bad 11. class Rey extends Jedi { 12. constructor(...args) { 13. super(...args); 14. } 15. } 16. 17. // good 18. class Rey extends Jedi { 19. constructor(...args) { 20. super(...args); 21. this.name = 'Rey'; 22. } 23. }
– 9.6 避免重复类成员。 eslint: no-dupe-class-members
为什么? 重复类成员声明将默认使用最后一个 – 重复类成员几乎肯定是一个错误。
JavaScript 代码:1. // bad 2. class Foo { 3. bar() { return 1; } 4. bar() { return 2; } 5. } 6. 7. // good 8. class Foo { 9. bar() { return 1; } 10. } 11. 12. // good 13. class Foo { 14. bar() { return 2; } 15. }
返回目录
模块 Modules
– 10.1 总是使用模块 (import
/export
) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。
为什么?模块就是未来,让我们开始迈向未来吧。
JavaScript 代码:1. // bad 2. const AirbnbStyleGuide = require('./AirbnbStyleGuide'); 3. module.exports = AirbnbStyleGuide.es6; 4. 5. // ok 6. import AirbnbStyleGuide from './AirbnbStyleGuide'; 7. export default AirbnbStyleGuide.es6; 8. 9. // best 10. import { es6 } from './AirbnbStyleGuide'; 11. export default es6;
为什么?这样能确保你只有一个默认 export(导出)。
JavaScript 代码:1. // bad 2. import * as AirbnbStyleGuide from './AirbnbStyleGuide'; 3. 4. // good 5. import AirbnbStyleGuide from './AirbnbStyleGuide';
– 10.3 不要从 import(导入) 中直接 export(导出)。
为什么?虽然一行代码简洁明了,但有一个明确的 import(导入) 方法和一个明确的 export(导出) 方法,使事情能保持一致。
JavaScript 代码:1. // bad 2. // filename es6.js 3. export { es6 as default } from './AirbnbStyleGuide'; 4. 5. // good 6. // filename es6.js 7. import { es6 } from './AirbnbStyleGuide'; 8. export default es6;
– 10.4 一个地方只在一个路径中 import(导入) 。
eslint: no-duplicate-imports
为什么? 从同一路径 import(导入) 多个模块分散在多行代码中,可能会使代码难以维护。
JavaScript 代码:1. // bad 2. import foo from 'foo'; 3. // … 其他一些 imports … // 4. import { named1, named2 } from 'foo'; 5. 6. // good 7. import foo, { named1, named2 } from 'foo'; 8. 9. // good 10. import foo, { 11. named1, 12. named2, 13. } from 'foo';
– 10.5 不要 export(导出) 可变绑定。eslint: import/no-mutable-exports
为什么? 一般应该避免可变性,特别是在导出可变绑定时。虽然一些特殊情况下,可能需要这种技术,但是一般而言,只应该导出常量引用。
JavaScript 代码:1. // bad 2. let foo = 3; 3. export { foo }; 4. 5. // good 6. const foo = 3; 7. export { foo };
– 10.6 在只有单个导出的模块中,默认 export(导出) 优于命名 export(导出)。eslint: import/prefer-default-export
为什么?为了鼓励更多的文件只有一个 export(导出),这有利于模块的可读性和可维护性。
JavaScript 代码:1. // bad 2. export function foo() {} 3. 4. // good 5. export default function foo() {}
– 10.7 将所有 import
导入放在非导入语句的上面。eslint: import/first
由于
import
被提升,保持他们在顶部,防止意外的行为。
JavaScript 代码:1. // bad 2. import foo from 'foo'; 3. foo.init(); 4. 5. import bar from 'bar'; 6. 7. // good 8. import foo from 'foo'; 9. import bar from 'bar'; 10. 11. foo.init();
– 10.8 多行导入应该像多行数组和对象字面量一样进行缩进。
为什么? 花括号应遵循与编码风格指南中的每个其他花括号相同的缩进规则,末尾的逗号也一样。
JavaScript 代码:1. // bad 2. import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; 3. 4. // good 5. import { 6. longNameA, 7. longNameB, 8. longNameC, 9. longNameD, 10. longNameE, 11. } from 'path';
– 10.9 禁止在模块 import(导入) 语句中使用 Webpack 加载器语法。
eslint: import/no-webpack-loader-syntax
为什么?由于在 import(导入) 中使用 Webpack 语法会将代码耦合到模块打包器。 首选在
webpack.config.js
中使用加载器语法。
JavaScript 代码:1. // bad 2. import fooSass from 'css!sass!foo.scss'; 3. import barCss from 'style!css!bar.css'; 4. 5. // good 6. import fooSass from 'foo.scss'; 7. import barCss from 'bar.css';
返回目录
迭代器 Iterators 和 生成器 Generators
为什么? 这是强制执行我们不变性的规则。 处理返回值的纯函数比 Side Effects(副作用) 更容易推理。
使用
map()
/every()
/filter()
/find()
/findIndex()
/reduce()
/some()
/ … 来迭代数组, 使用Object.keys()
/Object.values()
/Object.entries()
来生成数组,以便可以迭代对象。
JavaScript 代码:1. const numbers = [1, 2, 3, 4, 5]; 2. 3. // bad 4. let sum = 0; 5. for (let num of numbers) { 6. sum += num; 7. } 8. sum === 15; 9. 10. // good 11. let sum = 0; 12. numbers.forEach((num) => { 13. sum += num; 14. }); 15. sum === 15; 16. 17. // best (use the functional force) 18. const sum = numbers.reduce((total, num) => total + num, 0); 19. sum === 15; 20. 21. // bad 22. const increasedByOne = []; 23. for (let i = 0; i
属性 Properties
– 12.1 使用 点语法(.
) 来访问对象的属性。 eslint: dot-notation
jscs: requireDotNotation
JavaScript 代码:1. const luke = { 2. jedi: true, 3. age: 28, 4. }; 5. 6. // bad 7. const isJedi = luke['jedi']; 8. 9. // good 10. const isJedi = luke.jedi;
JavaScript 代码:1. const luke = { 2. jedi: true, 3. age: 28, 4. }; 5. 6. function getProp(prop) { 7. return luke[prop]; 8. } 9. 10. const isJedi = getProp('jedi');
– 12.3 求幂时使用求幂运算符 **
。eslint: no-restricted-properties
.
JavaScript 代码:1. // bad 2. const binary = Math.pow(2, 10); 3. 4. // good 5. const binary = 2 ** 10;
返回目录
变量 Variables
– 13.1 总是使用 const
或 let
来声明变量。 不这样做会导致产生全局变量。 我们希望避免污染全局命名空间。 eslint: no-undef
prefer-const
JavaScript 代码:1. // bad 2. superPower = new SuperPower(); 3. 4. // good 5. const superPower = new SuperPower();
– 13.2 使用 const
或 let
声明每个变量。 eslint: one-var
jscs: disallowMultipleVarDecl
为什么? 以这种方式添加新的变量声明更容易,你永远不必担心是否需要将
,
换成;
,或引入标点符号差异。您也可以在调试器中遍历每个声明,而不是一次跳过所有的变量。
JavaScript 代码:1. // bad 2. const items = getItems(), 3. goSportsTeam = true, 4. dragonball = 'z'; 5. 6. // bad 7. // (与上面的比较,并尝试找出错误) 8. const items = getItems(), 9. goSportsTeam = true; 10. dragonball = 'z'; 11. 12. // good 13. const items = getItems(); 14. const goSportsTeam = true; 15. const dragonball = 'z';
为什么?当你需要把已分配的变量分配给一个变量时非常有用。
JavaScript 代码:1. // bad 2. let i, len, dragonball, 3. items = getItems(), 4. goSportsTeam = true; 5. 6. // bad 7. let i; 8. const items = getItems(); 9. let dragonball; 10. const goSportsTeam = true; 11. let len; 12. 13. // good 14. const goSportsTeam = true; 15. const items = getItems(); 16. let dragonball; 17. let i; 18. let length;
– 13.4 在你需要的地方分配变量,但请把它们放在一个合理的位置。
为什么?
let
和const
是块级作用域而不是函数作用域。
JavaScript 代码:1. // bad - 不必要的函数调用 2. function checkName(hasName) { 3. const name = getName(); 4. 5. if (hasName === 'test') { 6. return false; 7. } 8. 9. if (name === 'test') { 10. this.setName(''); 11. return false; 12. } 13. 14. return name; 15. } 16. 17. // good 18. function checkName(hasName) { 19. if (hasName === 'test') { 20. return false; 21. } 22. 23. const name = getName(); 24. 25. if (name === 'test') { 26. this.setName(''); 27. return false; 28. } 29. 30. return name; 31. }
– 13.5 变量不要链式赋值。eslint: no-multi-assign
为什么? 链接变量赋值会创建隐式全局变量。
JavaScript 代码:1. // bad 2. (function example() { 3. // JavaScript 将其解析为 4. // let a = ( b = ( c = 1 ) ); 5. // let关键字只适用于变量a; 6. // 变量b和c变成了全局变量。 7. let a = b = c = 1; 8. }()); 9. 10. console.log(a); // 抛出 ReferenceError(引用错误) 11. console.log(b); // 1 12. console.log(c); // 1 13. 14. // good 15. (function example() { 16. let a = 1; 17. let b = a; 18. let c = a; 19. }()); 20. 21. console.log(a); // 抛出 ReferenceError(引用错误) 22. console.log(b); // 抛出 ReferenceError(引用错误) 23. console.log(c); // 抛出 ReferenceError(引用错误) 24. 25. // 同样适用于 `const`
– 13.6 避免使用一元递增和递减运算符(++
, --
)。 eslint no-plusplus
为什么? 根据 eslint 文档,一元递增和递减语句会受到自动插入分号的影响,并可能导致应用程序中的值递增或递减,从而导致无提示错误。使用像
num += 1
而不是num++
或num ++
这样的语句来改变你的值也更具有表现力。不允许一元递增和递减语句也会阻止您无意中预先递增/递减值,这也会导致程序中的意外行为。
JavaScript 代码:1. // bad 2. 3. const array = [1, 2, 3]; 4. let num = 1; 5. num++; 6. --num; 7. 8. let sum = 0; 9. let truthyCount = 0; 10. for (let i = 0; i
Hoisting
– 14.1 var
声明会被提升至他们作用域的顶部,但它们赋值不会提升。let
和 const
声明被赋予了一种称为「暂时性死区(Temporal Dead Zones, TDZ)」的概念。这对于了解为什么 type of 不再安全相当重要。
JavaScript 代码:1. // 我们知道这样运行不了 2. // (假设没有 notDefined 全局变量) 3. function example() { 4. console.log(notDefined); // => 抛出一个 ReferenceError(引用错误) 5. } 6. 7. // 在引用变量后创建变量声明 8. // 将因变量提升而起作用。 9. // 注意:赋值的 `true`没有被提升。 10. function example() { 11. console.log(declaredButNotAssigned); // => undefined 12. var declaredButNotAssigned = true; 13. } 14. 15. // 解析器将变量声明提升到作用域的顶部, 16. // 这意味着我们的例子可以被重写为: 17. function example() { 18. let declaredButNotAssigned; 19. console.log(declaredButNotAssigned); // => undefined 20. declaredButNotAssigned = true; 21. } 22. 23. // 使用 const 和 let 24. function example() { 25. console.log(declaredButNotAssigned); // => 抛出一个 ReferenceError(引用错误) 26. console.log(typeof declaredButNotAssigned); // => 抛出一个 ReferenceError(引用错误) 27. const declaredButNotAssigned = true; 28. }
– 14.2 匿名函数表达式的变量名会被提升,但函数分配不会。
JavaScript 代码:1. function example() { 2. console.log(anonymous); // => undefined 3. 4. anonymous(); // => TypeError anonymous is not a function 输入错误,anonymous 不是一个函数 5. 6. var anonymous = function () { 7. console.log('anonymous function expression'); 8. }; 9. }
– 14.3 命名的函数表达式的变量名会被提升,但函数名和函数体并不会。
JavaScript 代码:1. function example() { 2. console.log(named); // => undefined 3. 4. named(); // => TypeError named is not a function,输入错误,named 不是一个函数 5. 6. superPower(); // => ReferenceError superPower is not defined, ReferenceError(引用错误)superPower 未定义 7. 8. var named = function superPower() { 9. console.log('Flying'); 10. }; 11. } 12. 13. // 当函数名称与变量名称相同时 14. // 也是如此。 15. function example() { 16. console.log(named); // => undefined 17. 18. named(); // => TypeError named is not a function,输入错误,named 不是一个函数 19. 20. var named = function named() { 21. console.log('named'); 22. }; 23. }
JavaScript 代码:1. function example() { 2. superPower(); // => Flying 3. 4. function superPower() { 5. console.log('Flying'); 6. } 7. }
- 想了解更多信息,参考 Ben Cherry 的 JavaScript Scoping & Hoisting 。
返回目录
比较运算符 Comparison Operators 和 等号 Equality
– 15.1 使用 ===
和 !==
优先于 ==
和 !=
。 eslint: eqeqeq
– 15.2 诸如 if
语句之类的条件语句使用 ToBoolean
抽象方法来强制求值它们的表达式,并始终遵循以下简单规则:
- Objects 求值为 true
- Undefined 求值为 false
- Null 求值为 false
- Booleans 求值为 布尔值
- Numbers 如果是 +0、-0、或 NaN 求值为 false , 否则为 true
- Strings 如果是空字符串
''
求值为 false , 否则为 true
JavaScript 代码:1. if ([0] && []) { 2. // true 3. // 一个数组 (即使是一个空数组) 是一个 object, objects 被求值为 true 4. }
– 15.3 对于布尔值使用简写,但对于字符串和数字使用显式比较。
JavaScript 代码:1. // bad 2. if (isValid === true) { 3. // ... 4. } 5. 6. // good 7. if (isValid) { 8. // ... 9. } 10. 11. // bad 12. if (name) { 13. // ... 14. } 15. 16. // good 17. if (name !== '') { 18. // ... 19. } 20. 21. // bad 22. if (collection.length) { 23. // ... 24. } 25. 26. // good 27. if (collection.length > 0) { 28. // ... 29. }
– 15.4 想了解更多信息,参考 Angus Croll 的 Truth Equality and JavaScript。
– 15.5 在 case
和 default
子句中,使用大括号来创建包含词法声明的语句块(例如 let
, const
, function
, 和 class
). eslint: no-case-declarations
为什么? 词法声明在整个
switch
语句块中都是可见的,但是只有在分配时才被初始化,这只有当它到达case
时才会发生。这在多个case
子句试图定义相同的变量时会导致问题。
JavaScript 代码:1. // bad 2. switch (foo) { 3. case 1: 4. let x = 1; 5. break; 6. case 2: 7. const y = 2; 8. break; 9. case 3: 10. function f() { 11. // ... 12. } 13. break; 14. default: 15. class C {} 16. } 17. 18. // good 19. switch (foo) { 20. case 1: { 21. let x = 1; 22. break; 23. } 24. case 2: { 25. const y = 2; 26. break; 27. } 28. case 3: { 29. function f() { 30. // ... 31. } 32. break; 33. } 34. case 4: 35. bar(); 36. break; 37. default: { 38. class C {} 39. } 40. }
– 15.6 三元表达式不应该嵌套,通常写成单行表达式。 eslint: no-nested-ternary
JavaScript 代码:1. // bad 2. const foo = maybe1 > maybe2 3. ? "bar" 4. : value1 > value2 ? "baz" : null; 5. 6. // 拆分成2个分离的三元表达式 7. const maybeNull = value1 > value2 ? 'baz' : null; 8. 9. // better 10. const foo = maybe1 > maybe2 11. ? 'bar' 12. : maybeNull; 13. 14. // best 15. const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
– 15.7 避免不必要的三元表达式语句。 eslint: no-unneeded-ternary
JavaScript 代码:1. // bad 2. const foo = a ? a : b; 3. const bar = c ? true : false; 4. const baz = c ? false : true; 5. 6. // good 7. const foo = a || b; 8. const bar = !!c; 9. const baz = !c;
– 15.8 当运算符混合在一个语句中时,请将其放在括号内。混合算术运算符时,不要将 **
和 %
与 +
, -
,*
,/
混合在一起。eslint: no-mixed-operators
为什么? 这可以提高可读性,并清晰展现开发者的意图。
JavaScript 代码:1. // bad 2. const foo = a && b
代码块 Blocks
– 16.1 使用大括号包裹所有的多行代码块。 eslint: nonblock-statement-body-position
JavaScript 代码:1. // bad 2. if (test) 3. return false; 4. 5. // good 6. if (test) return false; 7. 8. // good 9. if (test) { 10. return false; 11. } 12. 13. // bad 14. function foo() { return false; } 15. 16. // good 17. function bar() { 18. return false; 19. }
– 16.2 如果通过 if
和 else
使用多行代码块,把 else
放在 if
代码块闭合括号的同一行。eslint: brace-style
jscs: disallowNewlineBeforeBlockStatements
JavaScript 代码:1. // bad 2. if (test) { 3. thing1(); 4. thing2(); 5. } 6. else { 7. thing3(); 8. } 9. 10. // good 11. if (test) { 12. thing1(); 13. thing2(); 14. } else { 15. thing3(); 16. }
– 16.3 如果一个 if
块总是执行一个 return
语句,后面的 else
块是不必要的。在 else if
块中的 return
,可以分成多个 if
块来 return
。eslint: no-else-return
JavaScript 代码:1. // bad 2. function foo() { 3. if (x) { 4. return x; 5. } else { 6. return y; 7. } 8. } 9. 10. // bad 11. function cats() { 12. if (x) { 13. return x; 14. } else if (y) { 15. return y; 16. } 17. } 18. 19. // bad 20. function dogs() { 21. if (x) { 22. return x; 23. } else { 24. if (y) { 25. return y; 26. } 27. } 28. } 29. 30. // good 31. function foo() { 32. if (x) { 33. return x; 34. } 35. 36. return y; 37. } 38. 39. // good 40. function cats() { 41. if (x) { 42. return x; 43. } 44. 45. if (y) { 46. return y; 47. } 48. } 49. 50. //good 51. function dogs(x) { 52. if (x) { 53. if (z) { 54. return y; 55. } 56. } else { 57. return z; 58. } 59. }
返回目录
控制语句 Control Statements
- 17.1 如果您的控制语句(
if
,while
的)太长或超过最大行长度,那么每个(分组)条件可以放单独一行。逻辑运算符应该放在每行起始处。
为什么? 在每行起始处要求运算符可以使运算符保持一致,并遵循与方法链式调用类似的模式。这样可以使复杂逻辑更易于查看,以提高可读性。
JavaScript 代码:1. // bad 2. if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { 3. thing1(); 4. } 5. 6. // bad 7. if (foo === 123 && 8. bar === 'abc') { 9. thing1(); 10. } 11. 12. // bad 13. if (foo === 123 14. && bar === 'abc') { 15. thing1(); 16. } 17. 18. // bad 19. if ( 20. foo === 123 && 21. bar === 'abc' 22. ) { 23. thing1(); 24. } 25. 26. // good 27. if ( 28. foo === 123 29. && bar === 'abc' 30. ) { 31. thing1(); 32. } 33. 34. // good 35. if ( 36. (foo === 123 || bar === "abc") 37. && doesItLookGoodWhenItBecomesThatLong() 38. && isThisReallyHappening() 39. ) { 40. thing1(); 41. } 42. 43. // good 44. if (foo === 123 && bar === 'abc') { 45. thing1(); 46. }
返回目录
注释 Comments
JavaScript 代码: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 7. function make(tag) { 8. 9. // ... 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. function make(tag) { 20. 21. // ... 22. 23. return element; 24. }
– 18.2 单行注释使用 //
。将单行注释放在续注释的语句上方。在注释之前放置一个空行,除非它位于代码块的第一行。
JavaScript 代码:1. // bad 2. const active = true; // is current tab 3. 4. // good 5. // is current tab 6. const active = true; 7. 8. // bad 9. function 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 18. function 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. } 26. 27. // also good 28. function getType() { 29. // set the default type to 'no type' 30. const type = this.type || 'no type'; 31. 32. return type; 33. }
– 18.3 所有注释符和注释内容用一个空格隔开,让它更容易阅读。 eslint: spaced-comment
JavaScript 代码:1. // bad 2. //is current tab 3. const active = true; 4. 5. // good 6. // is current tab 7. const active = true; 8. 9. // bad 10. /** 11. *make() returns a new element 12. *based on the passed-in tag name 13. */ 14. function make(tag) { 15. 16. // ... 17. 18. return element; 19. } 20. 21. // good 22. /** 23. * make() returns a new element 24. * based on the passed-in tag name 25. */ 26. function make(tag) { 27. 28. // ... 29. 30. return element; 31. }
– 18.4 给注释增加 FIXME
或 TODO
的前缀,可以帮助其他开发者快速了解这个是否是一个需要重新复查的问题,或是你正在为需要解决的问题提出解决方案。这将有别于常规注释,因为它们是可操作的。使用 FIXME -- need to figure this out
或者 TODO -- need to implement
。
– 18.5 使用 // FIXME:
来标识需要修正的问题。愚人码头注:如果代码中有该标识,说明标识处代码需要修正,甚至代码是错误的,不能工作,需要修复,如何修正会在说明中简略说明。
JavaScript 代码:1. class Calculator extends Abacus { 2. constructor() { 3. super(); 4. 5. // FIXME: shouldn’t use a global here 6. total = 0; 7. } 8. }
– 18.6 使用 // TODO:
来标识需要实现的问题。愚人码头注:如果代码中有该标识,说明在标识处有功能代码待编写,待实现的功能在说明中会简略说明。
JavaScript 代码:1. class Calculator extends Abacus { 2. constructor() { 3. super(); 4. 5. // TODO: total should be configurable by an options param 6. this.total = 0; 7. } 8. }
愚人码头注:还有 // XXX:
注释,如果代码中有该标识,说明标识处代码虽然实现了功能,但是实现的方法有待商榷,希望将来能改进,要改进的地方会在说明中简略说明。部分 IDE 有这些注释的收集视图,例如任务(task)视图,TODO视图等,在项目发布前,检查一下任务视图是一个很好的习惯。
返回目录
空白 Whitespace
– 19.1 使用 2 个空格作为缩进。 eslint: indent
jscs: validateIndentation
JavaScript 代码:1. // bad 2. function foo() { 3. ∙∙∙∙let name; 4. } 5. 6. // bad 7. function bar() { 8. ∙let name; 9. } 10. 11. // good 12. function baz() { 13. ∙∙let name; 14. }
– 19.2 在大括号前放置 1 个空格。eslint: space-before-blocks
jscs: requireSpaceBeforeBlockStatements
JavaScript 代码:1. // bad 2. function test(){ 3. console.log('test'); 4. } 5. 6. // good 7. function test() { 8. console.log('test'); 9. } 10. 11. // bad 12. dog.set('attr',{ 13. age: '1 year', 14. breed: 'Bernese Mountain Dog', 15. }); 16. 17. // good 18. dog.set('attr', { 19. age: '1 year', 20. breed: 'Bernese Mountain Dog', 21. });
– 19.3 在控制语句(if
、while
等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 eslint: keyword-spacing
jscs: requireSpaceAfterKeywords
JavaScript 代码:1. // bad 2. if(isJedi) { 3. fight (); 4. } 5. 6. // good 7. if (isJedi) { 8. fight(); 9. } 10. 11. // bad 12. function fight () { 13. console.log ('Swooosh!'); 14. } 15. 16. // good 17. function fight() { 18. console.log('Swooosh!'); 19. }
– 19.4 使用空格把运算符隔开。 eslint: space-infix-ops
jscs: requireSpaceBeforeBinaryOperators
, requireSpaceAfterBinaryOperators
JavaScript 代码:1. // bad 2. const x=y+5; 3. 4. // good 5. const x = y + 5;
– 19.5 在文件末尾插入一个空行。 eslint: eol-last
JavaScript 代码:1. // bad 2. import { es6 } from './AirbnbStyleGuide'; 3. // ... 4. export default es6;
JavaScript 代码:1. // bad 2. import { es6 } from './AirbnbStyleGuide'; 3. // ... 4. export default es6;↵ 5. ↵
JavaScript 代码:1. // good 2. import { es6 } from './AirbnbStyleGuide'; 3. // ... 4. export default es6;↵
– 19.6 长方法链式调用时使用缩进(2个以上的方法链式调用)。使用一个点 .
开头,强调该行是一个方法调用,不是一个新的声明。eslint: newline-per-chained-call
no-whitespace-before-property
JavaScript 代码: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 21. const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('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 27. const 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); 35. 36. // good 37. const leds = stage.selectAll('.led').data(data);
– 19.7 在语句块后和下条语句前留一个空行。jscs: requirePaddingNewLinesAfterBlocks
JavaScript 代码:1. // bad 2. if (foo) { 3. return bar; 4. } 5. return baz; 6. 7. // good 8. if (foo) { 9. return bar; 10. } 11. 12. return baz; 13. 14. // bad 15. const obj = { 16. foo() { 17. }, 18. bar() { 19. }, 20. }; 21. return obj; 22. 23. // good 24. const obj = { 25. foo() { 26. }, 27. 28. bar() { 29. }, 30. }; 31. 32. return obj; 33. 34. // bad 35. const arr = [ 36. function foo() { 37. }, 38. function bar() { 39. }, 40. ]; 41. return arr; 42. 43. // good 44. const arr = [ 45. function foo() { 46. }, 47. 48. function bar() { 49. }, 50. ]; 51. 52. return arr;
– 19.8 不要用空行来填充块。 eslint: padded-blocks
jscs: disallowPaddingNewlinesInBlocks
JavaScript 代码:1. // bad 2. function bar() { 3. 4. console.log(foo); 5. 6. } 7. 8. // bad 9. if (baz) { 10. 11. console.log(qux); 12. } else { 13. console.log(foo); 14. 15. } 16. 17. // bad 18. class Foo { 19. 20. constructor(bar) { 21. this.bar = bar; 22. } 23. } 24. 25. // good 26. function bar() { 27. console.log(foo); 28. } 29. 30. // good 31. if (baz) { 32. console.log(qux); 33. } else { 34. console.log(foo); 35. }
– 19.9 不要在圆括号内加空格。 eslint: space-in-parens
jscs: disallowSpacesInsideParentheses
JavaScript 代码:1. // bad 2. function bar( foo ) { 3. return foo; 4. } 5. 6. // good 7. function bar(foo) { 8. return foo; 9. } 10. 11. // bad 12. if ( foo ) { 13. console.log(foo); 14. } 15. 16. // good 17. if (foo) { 18. console.log(foo); 19. }
– 19.10 不要在中括号内添加空格。 eslint: array-bracket-spacing
jscs: disallowSpacesInsideArrayBrackets
JavaScript 代码:1. // bad 2. const foo = [ 1, 2, 3 ]; 3. console.log(foo[ 0 ]); 4. 5. // good 6. const foo = [1, 2, 3]; 7. console.log(foo[0]);
– 19.11 在大括号内添加空格。 eslint: object-curly-spacing
jscs: requireSpacesInsideObjectBrackets
JavaScript 代码:1. // bad 2. const foo = {clark: 'kent'}; 3. 4. // good 5. const foo = { clark: 'kent' };
– 19.12 避免有超过100个字符(包括空格)的代码行。注意:根据上面的规则,长字符串可以免除这个规则,不应该被破坏。eslint: max-len
jscs: maximumLineLength
为什么? 这可以确保可读性和可维护性。
JavaScript 代码:1. // bad 2. const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; 3. 4. // bad 5. $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); 6. 7. // good 8. const foo = jsonData 9. && jsonData.foo 10. && jsonData.foo.bar 11. && jsonData.foo.bar.baz 12. && jsonData.foo.bar.baz.quux 13. && jsonData.foo.bar.baz.quux.xyzzy; 14. 15. // good 16. $.ajax({ 17. method: 'POST', 18. url: 'https://airbnb.com/', 19. data: { name: 'John' }, 20. }) 21. .done(() => console.log('Congratulations!')) 22. .fail(() => console.log('You have failed this city.'));
返回目录
逗号 Commas
– 20.1 行开头处不要实用使用逗号。 eslint: comma-style
jscs: requireCommaBeforeLineBreak
JavaScript 代码:1. // bad 2. const story = [ 3. once 4. , upon 5. , aTime 6. ]; 7. 8. // good 9. const story = [ 10. once, 11. upon, 12. aTime, 13. ]; 14. 15. // bad 16. const hero = { 17. firstName: 'Ada' 18. , lastName: 'Lovelace' 19. , birthYear: 1815 20. , superPower: 'computers' 21. }; 22. 23. // good 24. const hero = { 25. firstName: 'Ada', 26. lastName: 'Lovelace', 27. birthYear: 1815, 28. superPower: 'computers', 29. };
– 20.2 添加结尾的逗号。 eslint: comma-dangle
jscs: requireTrailingComma
为什么?这会让 git diff(差异比较) 更干净。另外,像Babel这样的转译器会删除转译后代码中的结尾逗号,这意味着您不必担心传统浏览器中的结尾逗号问题。
JavaScript 代码:1. // bad - 没有结尾逗号的 git diff 差异比较 2. const hero = { 3. firstName: 'Florence', 4. - lastName: 'Nightingale' 5. + lastName: 'Nightingale', 6. + inventorOf: ['coxcomb chart', 'modern nursing'] 7. }; 8. 9. // good - 有结尾逗号的 git diff 差异比较 10. const hero = { 11. firstName: 'Florence', 12. lastName: 'Nightingale', 13. + inventorOf: ['coxcomb chart', 'modern nursing'], 14. };
JavaScript 代码:1. // bad 2. const hero = { 3. firstName: 'Dana', 4. lastName: 'Scully' 5. }; 6. 7. const heroes = [ 8. 'Batman', 9. 'Superman' 10. ]; 11. 12. // good 13. const hero = { 14. firstName: 'Dana', 15. lastName: 'Scully', 16. }; 17. 18. const heroes = [ 19. 'Batman', 20. 'Superman', 21. ]; 22. 23. // bad 24. function createHero( 25. firstName, 26. lastName, 27. inventorOf 28. ) { 29. // does nothing 30. } 31. 32. // good 33. function createHero( 34. firstName, 35. lastName, 36. inventorOf, 37. ) { 38. // does nothing 39. } 40. 41. // good (请注意,逗号不能出现在 “rest” 元素的后面) 42. function createHero( 43. firstName, 44. lastName, 45. inventorOf, 46. ...heroArgs 47. ) { 48. // does nothing 49. } 50. 51. // bad 52. createHero( 53. firstName, 54. lastName, 55. inventorOf 56. ); 57. 58. // good 59. createHero( 60. firstName, 61. lastName, 62. inventorOf, 63. ); 64. 65. // good (请注意,逗号不能出现在 “rest” 元素的后面) 66. createHero( 67. firstName, 68. lastName, 69. inventorOf, 70. ...heroArgs 71. );
返回目录
分号 Semicolons
– 21.1 当然要使用封号 eslint: semi
jscs: requireSemicolons
为什么? 当 JavaScript 遇到没有分号的换行符时,它使用一组称为自动分号插入的规则来确定是否应该将换行符视为语句的结尾,并且(顾名思义)如果被这样认为的话,在换行符前面自动插入一个分号。ASI(自动分号插入)包含了一些稀奇古怪的的行为,不过,如果 JavaScript 错误地解释了你的换行符,你的代码将会被中断执行。随着新功能成为 JavaScript 的一部分,这些规则将变得更加复杂。明确地结束你的语句并配置你的 linter 来捕获缺少的分号,将有助于防止遇到问题。
JavaScript 代码:1. // bad - 引发异常 2. const luke = {} 3. const leia = {} 4. [luke, leia].forEach(jedi => jedi.father = 'vader') 5. 6. // bad - 引发异常 7. const reaction = "No! That's impossible!" 8. (async function meanwhileOnTheFalcon(){ 9. // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` 10. // ... 11. }()) 12. 13. // bad - 返回`undefined`,而不是下一行的值 - 当 `return` 独占一行时,自动分号插入总是会发生。 14. function foo() { 15. return 16. 'search your feelings, you know it to be foo' 17. } 18. 19. // good 20. const luke = {}; 21. const leia = {}; 22. [luke, leia].forEach((jedi) => { 23. jedi.father = 'vader'; 24. }); 25. 26. // good 27. const reaction = "No! That's impossible!"; 28. (async function meanwhileOnTheFalcon(){ 29. // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` 30. // ... 31. }()); 32. 33. // good 34. function foo() { 35. return 'search your feelings, you know it to be foo'; 36. }
更多阅读.
返回目录
类型转换 Type Casting & Coercion
– 22.2 字符串: eslint: no-new-wrappers
JavaScript 代码:1. // => this.reviewScore = 9; 2. 3. // bad 4. const totalScore = new String(this.reviewScore); // typeof totalScore 是 "object" 而不是 "string" 5. 6. // bad 7. const totalScore = this.reviewScore + ''; // 调用 this.reviewScore.valueOf() 8. 9. // bad 10. const totalScore = this.reviewScore.toString(); // 不能保证返回一个字符串 11. 12. // good 13. const totalScore = String(this.reviewScore);
– 22.3 数字: 使用 Number
进行转换,而 parseInt
则始终以基数解析字串。 eslint: radix
no-new-wrappers
JavaScript 代码:1. const inputValue = '4'; 2. 3. // bad 4. const val = new Number(inputValue); 5. 6. // bad 7. const val = +inputValue; 8. 9. // bad 10. const val = inputValue >> 0; 11. 12. // bad 13. const val = parseInt(inputValue); 14. 15. // good 16. const val = Number(inputValue); 17. 18. // good 19. const val = parseInt(inputValue, 10);
– 22.4 如果你因为某个原因正在做些疯狂的事情,但是 parseInt
是你的瓶颈,所以你对于 性能方面的原因而必须使用位运算,请留下评论并解释为什么使用,及你做了哪些事情。
JavaScript 代码:1. // good 2. /** 3. * parseInt was the reason my code was slow. 4. * Bitshifting the String to coerce it to a 5. * Number made it a lot faster. 6. */ 7. const val = inputValue >> 0;
– 22.5 注意: 使用位运算请小心。 数字使用 64位值表示, 但是位运算只返回32位整数 (来源)。 小于32位整数的位运算会导致不可预期的行为. 讨论。最大的有符号整数是 2,147,483,647:
JavaScript 代码:1. 2147483647 >> 0; // => 2147483647 2. 2147483648 >> 0; // => -2147483648 3. 2147483649 >> 0; // => -2147483647
– 22.6 布尔值: eslint: no-new-wrappers
JavaScript 代码:1. const age = 0; 2. 3. // bad 4. const hasAge = new Boolean(age); 5. 6. // good 7. const hasAge = Boolean(age); 8. 9. // best 10. const hasAge = !!age;
返回目录
命名规则 Naming Conventions
– 23.1 避免使用单字母名称。使你的命名具有描述性。 eslint: id-length
JavaScript 代码:1. // bad 2. function q() { 3. // ... 4. } 5. 6. // good 7. function query() { 8. // ... 9. }
– 23.2 当命名对象,函数和实例时使用驼峰式命名。 eslint: camelcase
jscs: requireCamelCaseOrUpperCaseIdentifiers
JavaScript 代码:1. // bad 2. const OBJEcttsssss = {}; 3. const this_is_my_object = {}; 4. function c() {} 5. 6. // good 7. const thisIsMyObject = {}; 8. function thisIsMyFunction() {}
– 23.3 当命名构造函数或类的时候使用 PascalCase 式命名,(愚人码头注:即单词首字母大写)。 eslint: new-cap
jscs: requireCapitalizedConstructors
JavaScript 代码:1. // bad 2. function user(options) { 3. this.name = options.name; 4. } 5. 6. const bad = new user({ 7. name: 'nope', 8. }); 9. 10. // good 11. class User { 12. constructor(options) { 13. this.name = options.name; 14. } 15. } 16. 17. const good = new User({ 18. name: 'yup', 19. });
– 23.4 不要使用下划线开头或结尾。 eslint: no-underscore-dangle
jscs: disallowDanglingUnderscores
为什么? JavaScript 对于属性或方法而言并没有私有的概念。虽然下划线开头通常意味着 ‘private’(私有)是通用的惯例,事实上,这些属性是完全公开的,是公开API的一部分。 这个惯例可能会导致开发人员错误地认为这不重要或者测试也不必要。简而言之:如果你想让其 “private”, 必须使其不可见。
JavaScript 代码:1. // bad 2. this.__firstName__ = 'Panda'; 3. this.firstName_ = 'Panda'; 4. this._firstName = 'Panda'; 5. 6. // good 7. this.firstName = 'Panda';
– 23.5 不要存储 this
引用。请实用箭头函数或者 Function#bind。 jscs: disallowNodeTypes
JavaScript 代码:1. // bad 2. function foo() { 3. const self = this; 4. return function () { 5. console.log(self); 6. }; 7. } 8. 9. // bad 10. function foo() { 11. const that = this; 12. return function () { 13. console.log(that); 14. }; 15. } 16. 17. // good 18. function foo() { 19. return () => { 20. console.log(this); 21. }; 22. }
– 23.6 basename 应与其默认导出的名称正好匹配。(愚人码头注:basename 指的是文件名)
JavaScript 代码:1. // file 1 contents 2. class CheckBox { 3. // ... 4. } 5. export default CheckBox; 6. 7. // file 2 contents 8. export default function fortyTwo() { return 42; } 9. 10. // file 3 contents 11. export default function insideDirectory() {} 12. 13. // in some other file 14. // bad 15. import CheckBox from './checkBox'; // import/export 单词首字母大写命名 , filename 驼峰式命名 16. import FortyTwo from './FortyTwo'; // import/filename 单词首字母大写命名, export 驼峰式命名 17. import InsideDirectory from './InsideDirectory'; // import/filename 单词首字母大写命名, export 驼峰式命名 18. 19. // bad 20. import CheckBox from './check_box'; // import/export 单词首字母大写命名, filename 下划线命名 21. import forty_two from './forty_two'; // import/filename 下划线命名, export 驼峰式命名 22. import inside_directory from './inside_directory'; // import 下划线命名, export 驼峰式命名 23. import index from './inside_directory/index'; // 明确地 require 索引文件 24. import insideDirectory from './insideDirectory/index'; // 明确地 require 索引文件 25. 26. // good 27. import CheckBox from './CheckBox'; // export/import/filename 单词首字母大写命名 28. import fortyTwo from './fortyTwo'; // export/import/filename 驼峰式命名 29. import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" 30. // ^ supports both insideDirectory.js and insideDirectory/index.js
– 23.7 当 导出(export) 一个默认函数时使用驼峰式命名。你的文件名应该和你的函数的名字一致。
JavaScript 代码:1. function makeStyleGuide() { 2. // ... 3. } 4. 5. export default makeStyleGuide;
– 23.8 当导出一个 构造函数 / 类 / 单例 / 函数库 / 纯对象时使用 PascalCase 式命名,(愚人码头注:即单词首字母大写)。
JavaScript 代码:1. const AirbnbStyleGuide = { 2. es6: { 3. }, 4. }; 5. 6. export default AirbnbStyleGuide;
为什么? 名字是更具可读性,而不是为了满足计算机算法。
JavaScript 代码:1. // bad 2. import SmsContainer from './containers/SmsContainer'; 3. 4. // bad 5. const HttpRequests = [ 6. // ... 7. ]; 8. 9. // good 10. import SMSContainer from './containers/SMSContainer'; 11. 12. // good 13. const HTTPRequests = [ 14. // ... 15. ]; 16. 17. // also good 18. const httpRequests = [ 19. // ... 20. ]; 21. 22. // best 23. import TextMessageContainer from './containers/TextMessageContainer'; 24. 25. // best 26. const requests = [ 27. // ... 28. ];
返回目录
存取器 Accessors
– 24.2 別使用 JavaScript 的 getters/setters,因为它们会导致意想不到的副作用,而且很难测试,维护和理解。相反,如果要使用存取器函数,使用 getVal() 及 setVal(‘hello’)。
JavaScript 代码:1. // bad 2. class Dragon { 3. get age() { 4. // ... 5. } 6. 7. set age(value) { 8. // ... 9. } 10. } 11. 12. // good 13. class Dragon { 14. getAge() { 15. // ... 16. } 17. 18. setAge(value) { 19. // ... 20. } 21. }
– 24.3 如果属性/方法是一个 boolean
, 使用 isVal()
或 hasVal()
方法。
JavaScript 代码:1. // bad 2. if (!dragon.age()) { 3. return false; 4. } 5. 6. // good 7. if (!dragon.hasAge()) { 8. return false; 9. }
– 24.4 也可以创建 get() 和 set() 函数, 但要保持一致。
JavaScript 代码:1. class 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. }
返回目录
事件 Events
JavaScript 代码:1. // bad 2. $(this).trigger('listingUpdated', listing.id); 3. 4. // ... 5. 6. $(this).on('listingUpdated', (e, listingId) => { 7. // do something with listingId 8. });
prefer:
JavaScript 代码:1. // good 2. $(this).trigger('listingUpdated', { listingId: listing.id }); 3. 4. // ... 5. 6. $(this).on('listingUpdated', (e, data) => { 7. // do something with data.listingId 8. });
返回目录
jQuery
JavaScript 代码:1. // bad 2. const sidebar = $('.sidebar'); 3. 4. // good 5. const $sidebar</span> <span class="pun">=</span> <span class="pln">$('.sidebar'); 6. 7. // good 8. const $sidebarBtn</span> <span class="pun">=</span> <span class="pln">$('.sidebar-btn');
JavaScript 代码:1. // bad 2. function setSidebar() { 3. $('.sidebar').hide(); 4. 5. // ... 6. 7. $('.sidebar').css({ 8. 'background-color': 'pink', 9. }); 10. } 11. 12. // good 13. function setSidebar() { 14. const $sidebar</span> <span class="pun">=</span> <span class="pln">$('.sidebar'); 15. $sidebar.hide(); 16. 17. // ... 18. 19. $sidebar.css({ 20. 'background-color': 'pink', 21. }); 22. }
– 26.3 DOM 查询使用后代选择器 $('.sidebar ul')
或者 父类 > 子类 $('.sidebar > ul')
选择器。jsPerf
– 26.4 在某个 jQuery 对象范围内查询使用 find
。
JavaScript 代码: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();
返回目录
ECMAScript 5 兼容性 Compatibility
– 27.1 参考 Kangax 的 ES5 compatibility table.
返回目录
ECMAScript 6+ (ES 2015+) 编码风格
- Arrow Functions
- Classes
- Object Shorthand
- Object Concise
- Object Computed Properties
- Template Strings
- Destructuring
- Default Parameters
- Rest
- Array Spreads
- Let and Const
- Exponentiation Operator
- Iterators and Generators
- Modules
– 28.2 不要使用 TC39 proposals 还未实现的 stage3 的功能。
为什么?他们没有最终确定,他们可能会改变或完全撤回。我们想要使用JavaScript,而且建议性的提案还不是 JavaScript 。
返回目录
标准库 Standard Library
标准库包含有问题,但由于历史遗留问题而保留下来的功能。
– 29.1 使用 Number.isNaN
代替全局 isNaN
。eslint: no-restricted-globals
为什么?全局的
isNaN
方法会将非数字转换为数字, 任何被转换为 NaN 的东西都会返回 true 。
如果需要这种行为,请明确使用。
JavaScript 代码:1. // bad 2. isNaN('1.2'); // false 3. isNaN('1.2.3'); // true 4. 5. // good 6. Number.isNaN('1.2.3'); // false 7. Number.isNaN(Number('1.2.3')); // true
– 29.2 使用 Number.isFinite
代替全局 isFinite
。eslint: no-restricted-globals
为什么?全局的
isFinite
方法会将非数字转换为数字, 任何被转换为有限大的数字都会返回 true 。
如果需要这种行为,请明确使用。
JavaScript 代码:1. // bad 2. isFinite('2e3'); // true 3. 4. // good 5. Number.isFinite('2e3'); // false 6. Number.isFinite(parseInt('2e3', 10)); // true
返回目录
## 测试 – [30.1](#testing–yup) **是的。**– [30.2](#testing–for-real) **不要这么做,很严重**: – 不论你用哪一个测试框架,都应该写测试用例! – 尽力写一些简单的纯函数, 并尽量减可变性发生的地方。 – 谨慎的使用 stubs 和 mocks – 它们会使测试变得脆弱. – 我们主要在 Airbnb 中主要使用 [`mocha`](https://www.npmjs.com/package/mocha) 。 [`tape`](https://www.npmjs.com/package/tape) 也偶尔会用于小型独立模块。 – 100%的测试覆盖率是很好的追求目标,即使它并不总是实际可行的。 – 每当你修复一个 bug ,写一个回归测试。 未经回归测试的bug修复几乎会在将在再次出现. **[![⬆](https://s.w.org/images/core/emoji/2.3/svg/2b06.svg) 返回目录](#table-of-contents)**JavaScript 代码:1. function foo() { 2. return true; 3. }
性能 Performance
- 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
- Are Javascript functions like
map()
,reduce()
, andfilter()
optimized for traversing arrays? - Loading…
返回目录
这篇关于Airbnb JavaScript 编码规范(涵盖 ECMAScript 6+)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!