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