Gathering detailed insights and metrics for eslint-config-tencent
Gathering detailed insights and metrics for eslint-config-tencent
npm install eslint-config-tencent
Typescript
Module System
Node Version
NPM Version
53.2
Supply Chain
87.8
Quality
79.9
Maintenance
100
Vulnerability
95.3
License
Love this project? Help keep it running — sponsor us today! 🚀
Total Downloads
141,173
Last Day
106
Last Week
798
Last Month
1,906
Last Year
54,235
Minified
Minified + Gzipped
Latest Version
1.1.2
Package Id
eslint-config-tencent@1.1.2
Unpacked Size
187.15 kB
Size
46.71 kB
File Count
20
NPM Version
10.8.2
Node Version
20.17.0
Published on
Dec 19, 2024
Cumulative downloads
Total Downloads
Last Day
324%
106
Compared to previous day
Last Week
600%
798
Compared to previous week
Last Month
-81.5%
1,906
Compared to previous month
Last Year
53.9%
54,235
Compared to previous year
8
2
32
[TOC]
为前端开发提供良好的基础编码风格修行指南。
基于 Airbnb 编码规范。所有规范分为三个等级:必须、推荐、可选。
必须(Mandatory) 级别要求员工必须严格按照规范的编码格式编写,否则会在代码扫描和自动化构建中报错。对应 RFC 2119 中的 MUST
, MUST NOT
和 REQUIRED
级别。
推荐(Preferable) 级别希望员工尽量按照规范编写,但如有特殊情况,可以不采用。对应 RFC 2119 中的 SHOULD
, SHOULD NOT
级别。
可选(Optional) 级别并不对员工编码提出要求,一般是JavaScript常识以及ES6、ES7的新语法,但仍希望员工按参考规范编写。委员会将定期review编码规范,不断提高规范等级要求。对应 RFC 2119 中的 MAY
级别。
本文档中的代码为示例代码,出于演示目的,不一定完全符合本文档所规定的所有规则要求。
本文档中的示例代码中会有 Good 或 Best 的提示,表示这是遵守代码规范的一种写法。 需要说明的是,虽然 Good 写法符合这条规范,但不代表这是满足规范的唯一方式。
未尽事宜,应当首先以同一文件中的其他类似风格为准,若该文件中没有类似结构,则以项目风格为准。一致性是最重要的。
基于 MIT 协议开源。
查看 Notice 文件了解注明和鸣谢信息。
安装:
javascript
1tnpm install eslint @tencent/eslint-config-tencent --save-dev
.eslintrc.js:
1module.exports = { 2 extends: ['@tencent/eslint-config-tencent'], 3}
typescript 还需要安装依赖
1tnpm install @typescript-eslint/eslint-plugin @typescript-eslint/parser --save-dev
.eslintrc.js:
1module.exports = { 2 extends: ['@tencent/eslint-config-tencent', '@tencent/eslint-config-tencent/ts'], 3}
使用 prettier 的用户需要安装对应的依赖
1tnpm install prettier eslint-plugin-prettier --save-dev
.eslintrc.js:
1module.exports = { 2 extends: ['@tencent/eslint-config-tencent', '@tencent/eslint-config-tencent/ts', '@tencent/eslint-config-tencent/prettier'], 3}
注意:使用 prettier 规则之后,会自动禁用掉与之相冲突的格式相关的规则。由于prettier与ESLint存在冲突,开源扫描仍以ESLint扫描结果为准。
推荐使用 cjs 作为配置文件后缀,可兼容 type: module 和 type: commonjs 的项目。
eslint.config.cjs:
1const tencentEslintConfig = require('@tencent/eslint-config-tencent/flat'); 2module.exports = tencentEslintConfig({});
使用 TypeScript 的项目需要传入 tsconfig.json
所在的目录位置,
eslint.config.cjs:
1const tencentEslintConfig = require('@tencent/eslint-config-tencent/flat'); 2module.exports = tencentEslintConfig({ 3 tsconfigRootDir: __dirname, 4});
增量检查
1master: 2 merge_request: 3 - stages: 4 - name: make changelist 5 type: git:changeList 6 options: 7 changed: changed.txt 8 - name: git diff eslint-config-tencent 9 image: csighub.tencentyun.com/standards/eslint-config-tencent:latest 10 settings: 11 change_file: changed.txt
全量检查
1master: 2 merge_request: 3 - stages: 4 - name: git eslint-config-tencent 5 image: csighub.tencentyun.com/standards/eslint-config-tencent:latest
全量检查
1mr: 2 branches: 3 include: 4 - master 5stages: 6 - stage: 7 - job: 8 name: eslint 9 steps: 10 - taskType: dockerRun@latest 11 displayName: dockerRun 12 inputs: 13 image: csighub.tencentyun.com/standards/eslint-config-tencent:latest 14 cmd: ""
1.1 【可选】 基本类型: 当你访问一个基本类型时,直接操作它的值。
string
number
boolean
null
undefined
symbol
1const foo = 1; 2let bar = foo; 3 4bar = 9; 5 6console.log(foo, bar); // => 1, 9
1.2 【可选】 复杂类型: 当你访问一个复杂类型时,直接操作其值的引用。
object
array
function
1const foo = [1, 2]; 2const bar = foo; 3 4bar[0] = 9; 5 6console.log(foo[0], bar[0]); // => 9, 9
2.1 【必须】 使用 const
定义你的所有引用;避免使用 var
。 eslint: prefer-const
, no-const-assign
原因? 这样能够确保你不能重新赋值你的引用,否则可能导致错误或者产生难以理解的代码。
1// bad 2var a = 1; 3var b = 2; 4 5// good 6const a = 1; 7const b = 2;
2.2 【必须】 如果你必须重新赋值你的引用, 使用 let
代替 var
。 eslint: no-var
原因?
let
是块级作用域,而不像var
是函数作用域。
1// bad 2var count = 1; 3if (true) { 4 count += 1; 5} 6 7// good, use the let. 8let count = 1; 9if (true) { 10 count += 1; 11}
2.3 【可选】 注意,let 和 const 都是块级作用域。
1// const 和 let 只存在于他们定义的块级作用域中。 2{ 3 let a = 1; 4 const b = 1; 5} 6console.log(a); // ReferenceError 7console.log(b); // ReferenceError
3.1 【必须】 使用字面量语法创建对象。 eslint: no-new-object
1// bad 2const item = new Object(); 3 4// good 5const item = {};
3.2 【推荐】 在创建具有动态属性名称的对象时使用计算属性名。
原因? 它允许你在一个地方定义对象的所有属性。
1function getKey(k) { 2 return `a key named ${k}`; 3} 4 5// bad 6const obj = { 7 id: 5, 8 name: 'San Francisco', 9}; 10obj[getKey('enabled')] = true; 11 12// good 13const obj = { 14 id: 5, 15 name: 'San Francisco', 16 [getKey('enabled')]: true, 17};
3.3 【推荐】 用对象方法简写。 eslint: object-shorthand
1// bad 2const value = 1; 3const atom = { 4 value: value, 5 addValue: function (newValue) { 6 return atom.value + newValue; 7 }, 8}; 9 10// good 11const value = 1; 12const atom = { 13 value, 14 addValue(newValue) { 15 return atom.value + newValue; 16 }, 17};
3.4 【推荐】 用属性值简写。 eslint: object-shorthand
原因? 它更加简洁并更具描述性。
1const lukeSkywalker = 'Luke Skywalker'; 2 3// bad 4const obj = { 5 lukeSkywalker: lukeSkywalker, 6}; 7 8// good 9const obj = { 10 lukeSkywalker, 11};
3.5 【推荐】 声明对象时,将简写的属性放在前面。
原因? 这样更容易的判断哪些属性使用的简写。
1const anakinSkywalker = 'Anakin Skywalker'; 2const lukeSkywalker = 'Luke Skywalker'; 3 4// bad 5const obj = { 6 episodeOne: 1, 7 twoJediWalkIntoACantina: 2, 8 lukeSkywalker, 9 episodeThree: 3, 10 mayTheFourth: 4, 11 anakinSkywalker, 12}; 13 14// good 15const obj = { 16 lukeSkywalker, 17 anakinSkywalker, 18 episodeOne: 1, 19 twoJediWalkIntoACantina: 2, 20 episodeThree: 3, 21 mayTheFourth: 4, 22};
3.6 【必须】 只使用引号标注无效标识符的属性。 eslint: quote-props
原因? 一般来说,我们认为这样更容易阅读。 它能更好地适配语法高亮显示功能,并且更容易通过许多 JS 引擎进行优化。
1// bad 2const bad = { 3 'foo': 3, 4 'bar': 4, 5 'data-blah': 5, 6}; 7 8// good 9const good = { 10 foo: 3, 11 bar: 4, 12 'data-blah': 5, 13};
3.7 【推荐】 不能直接调用 Object.prototype
的方法,如: hasOwnProperty
、 propertyIsEnumerable
和 isPrototypeOf
。 eslint: no-prototype-builtins
原因? 这些方法可能被有问题的对象上的属性覆盖 - 如
{ hasOwnProperty: false }
- 或者,对象是一个空对象 (Object.create(null)
)。
1// bad 2console.log(object.hasOwnProperty(key)); 3 4// good 5console.log(Object.prototype.hasOwnProperty.call(object, key)); 6 7// best 8const has = Object.prototype.hasOwnProperty; // 在模块范围内的缓存中查找一次 9console.log(has.call(object, key)); 10 11/* or */ 12import has from 'has'; // https://www.npmjs.com/package/has 13console.log(has(object, key));
3.8 【推荐】 使用对象扩展操作符(spread operator)浅拷贝对象,而不是用 Object.assign
方法。 使用对象的剩余操作符(rest operator)来获得一个新对象,该对象省略了某些属性。 eslint: prefer-object-spread
1// very bad 2const original = { a: 1, b: 2 }; 3const copy = Object.assign(original, { c: 3 }); // 变异的 `original` ಠ_ಠ 4delete copy.a; // 这.... 5 6// bad 7const original = { a: 1, b: 2 }; 8const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } 9 10// good 11const original = { a: 1, b: 2 }; 12const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } 13 14const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
4.1 【必须】 使用字面量语法创建数组。 eslint: no-array-constructor
1// bad 2const items = new Array(); 3 4// good 5const items = [];
4.2 【必须】 使用 Array#push 代替直接赋值来给数组添加项。
1const someStack = []; 2 3// bad 4someStack[someStack.length] = 'abracadabra'; 5 6// good 7someStack.push('abracadabra');
4.3 【必须】 使用数组展开符 ...
来拷贝数组。
1// bad 2const len = items.length; 3const itemsCopy = []; 4let i; 5 6for (i = 0; i < len; i += 1) { 7 itemsCopy[i] = items[i]; 8} 9 10// good 11const itemsCopy = [...items];
4.4 【推荐】 使用展开符 ...
代替 Array.from
,将一个可迭代对象转换成一个数组。
1const foo = document.querySelectorAll('.foo'); 2 3// good 4const nodes = Array.from(foo); 5 6// best 7const nodes = [...foo];
4.5 【必须】 使用 Array.from 将一个类数组(array-like)对象转换成一个数组。
1const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }; 2 3// bad 4const arr = Array.prototype.slice.call(arrLike); 5 6// good 7const arr = Array.from(arrLike);
4.6 【必须】 使用 Array.from 代替展开符 ...
映射迭代器,因为它避免了创建一个中间数组。
1// bad 2const baz = [...foo].map(bar); 3 4// good 5const baz = Array.from(foo, bar);
4.7 【推荐】 在数组回调函数中使用 return 语句。 如果函数体由单个语句的返回表达式组成,并且无副作用,那么可以省略返回值, 具体查看 8.2。 eslint: array-callback-return
1// good 2[1, 2, 3].map((x) => { 3 const y = x + 1; 4 return x * y; 5}); 6 7// good 8[1, 2, 3].map(x => x + 1); 9 10// bad - 没有返回值,意味着在第一次迭代后 `acc` 没有被定义 11[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { 12 const flatten = acc.concat(item); 13 acc[index] = flatten; 14}); 15 16// good 17[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { 18 const flatten = acc.concat(item); 19 acc[index] = flatten; 20 return flatten; 21}, []); 22 23// bad 24inbox.filter((msg) => { 25 const { subject, author } = msg; 26 if (subject === 'Mockingbird') { 27 return author === 'Harper Lee'; 28 } else { 29 return false; 30 } 31}); 32 33// good 34inbox.filter((msg) => { 35 const { subject, author } = msg; 36 if (subject === 'Mockingbird') { 37 return author === 'Harper Lee'; 38 } 39 40 return false; 41});
4.8 【推荐】 如果数组有多行,则在数组开始括号 [
的时候换行,然后在数组结束括号 ]
的时候换行。 eslint: array-bracket-newline
1// bad 2const arr = [ 3 [0, 1], [2, 3], [4, 5], 4]; 5 6const objectInArray = [{ 7 id: 1, 8}, { 9 id: 2, 10}]; 11 12const numberInArray = [ 13 1, 2, 14]; 15 16// good 17const arr = [[0, 1], [2, 3], [4, 5]]; 18 19const objectInArray = [ 20 { 21 id: 1, 22 }, 23 { 24 id: 2, 25 }, 26]; 27 28const numberInArray = [ 29 1, 30 2, 31];
5.1 【推荐】 在访问和使用对象的多个属性时使用对象解构。 eslint: prefer-destructuring
原因? 解构可以避免为这些属性创建临时引用。
1// bad 2function getFullName(user) { 3 const firstName = user.firstName; 4 const lastName = user.lastName; 5 6 return `${firstName} ${lastName}`; 7} 8 9// good 10function getFullName(user) { 11 const { firstName, lastName } = user; 12 return `${firstName} ${lastName}`; 13} 14 15// best 16function getFullName({ firstName, lastName }) { 17 return `${firstName} ${lastName}`; 18}
5.2 【推荐】 使用数组解构。 eslint: prefer-destructuring
1const arr = [1, 2, 3, 4]; 2 3// bad 4const first = arr[0]; 5const second = arr[1]; 6 7// good 8const [first, second] = arr;
5.3 【必须】 在有多个返回值时, 使用对象解构,而不是数组解构。
原因? 你可以随时添加新的属性或者改变属性的顺序,而不用修改调用方。
1// bad 2function processInput(input) { 3 // 处理代码... 4 return [left, right, top, bottom]; 5} 6 7// 调用者需要考虑返回数据的顺序。 8const [left, __, top] = processInput(input); 9 10// good 11function processInput(input) { 12 // 处理代码... 13 return { left, right, top, bottom }; 14} 15 16// 调用者只选择他们需要的数据。 17const { left, top } = processInput(input);
6.1 【推荐】 使用单引号 ''
定义字符串。 eslint: quotes
1// bad 2const name = "Capt. Janeway"; 3 4// bad - 模板文字应该包含插值或换行。 5const name = `Capt. Janeway`; 6 7// good 8const name = 'Capt. Janeway';
6.2 【必须】 不应该用字符串跨行连接符的格式来跨行编写,这样会使当前行长度超过100个字符。
原因? 断开的字符串维护起来很痛苦,并且会提高索引难度。
1// bad 2const errorMessage = 'This is a super long error that was thrown because \ 3of Batman. When you stop to think about how Batman had anything to do \ 4with this, you would get nowhere \ 5fast.'; 6 7// bad 8const 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 13const 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
原因? 字符串模板为您提供了一种可读的、简洁的语法,具有正确的换行和字符串插值特性。
1// bad 2function sayHi(name) { 3 return 'How are you, ' + name + '?'; 4} 5 6// bad 7function sayHi(name) { 8 return ['How are you, ', name, '?'].join(); 9} 10 11// bad 12function sayHi(name) { 13 return `How are you, ${ name }?`; 14} 15 16// good 17function sayHi(name) { 18 return `How are you, ${name}?`; 19}
6.4 【必须】 永远不要使用 eval()
执行放在字符串中的代码,它导致了太多的漏洞。 eslint: no-eval
6.5 【必须】 不要在字符串中转义不必要的字符。 eslint: no-useless-escape
原因? 反斜杠损害了可读性,因此只有在必要的时候才可以出现。
1// bad 2const foo = '\'this\' \i\s \"quoted\"'; 3 4// good 5const foo = '\'this\' is "quoted"'; 6const foo = `my name is '${name}'`;
7.1 【可选】 使用命名的函数表达式代替函数声明。 eslint: func-style
原因? 函数声明时作用域被提前了,这意味着在一个文件里函数很容易(太容易了)在其定义之前被引用。这样伤害了代码可读性和可维护性。如果你发现一个函数又大又复杂,并且它干扰了对这个文件其他部分的理解,那么是时候把这个函数单独抽成一个模块了!别忘了给表达式显式的命名,不用管这个名字是不是由一个确定的变量推断出来的(这在现代浏览器和类似babel编译器中很常见)。这消除了由匿名函数在错误调用栈产生的所有假设。 * (Discussion)
1// bad 2function foo() { 3 // ... 4} 5 6// also good * 7const foo = function () { 8 // ... 9}; 10 11// good 12const short = function longUniqueMoreDescriptiveLexicalFoo() { 13 // ... 14};
7.2 【必须】 把立即执行函数包裹在圆括号里。 eslint: wrap-iife
原因? 立即调用的函数表达式是个独立的单元 - 将它和它的调用括号还有入参包装在一起可以非常清晰的表明这一点。请注意,在一个到处都是模块的世界中,您几乎用不到 IIFE。
1// immediately-invoked function expression (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
定义为语句列表。 而函数声明并不是语句。
1// bad 2if (currentUser) { 3 function test() { 4 console.log('Nope.'); 5 } 6} 7 8// good 9let test; 10if (currentUser) { 11 test = () => { 12 console.log('Yup.'); 13 }; 14}
7.5 【必须】 永远不要给一个参数命名为 arguments
。 这将会覆盖函数默认的 arguments
对象。 eslint: no-shadow-restricted-names
1// bad 2function foo(name, options, arguments) { 3 // ... 4} 5 6// good 7function foo(name, options, args) { 8 // ... 9}
7.6 【推荐】 使用 rest 语法 ...
代替 arguments
。 eslint: prefer-rest-params
原因?
...
明确了你想要拉取什么参数。 而且, rest 参数是一个真正的数组,而不仅仅是类数组的arguments
。
1// bad 2function concatenateAll() { 3 const args = Array.prototype.slice.call(arguments); 4 return args.join(''); 5} 6 7// good 8function concatenateAll(...args) { 9 return args.join(''); 10}
7.7 【推荐】 使用默认的参数语法,而不是改变函数参数。
1// really bad 2function handleThings(opts) { 3 // 不!我们不应该修改参数。 4 // 更加错误的是: 如果 opts 是一个 "非正值"(falsy)它将被设置成一个对象 5 // 这或许正是你想要的,但它可能会导致一些难以察觉的错误。 6 opts = opts || {}; 7 // ... 8} 9 10// still bad 11function handleThings(opts) { 12 if (opts === void 0) { 13 opts = {}; 14 } 15 // ... 16} 17 18// good 19function handleThings(opts = {}) { 20 // ... 21}
7.8 【必须】 使用默认参数时避免副作用。
原因? 他们很容易混淆。
1var b = 1; 2// bad 3function count(a = b++) { 4 console.log(a); 5} 6count(); // 1 7count(); // 2 8count(3); // 3 9count(); // 3
7.9 【推荐】 总是把默认参数放在最后。 eslint: default-param-last
1// bad 2function handleThings(opts = {}, name) { 3 // ... 4} 5 6// good 7function handleThings(name, opts = {}) { 8 // ... 9}
7.10 【推荐】 永远不要使用函数构造器来创建一个新函数。 eslint: no-new-func
原因? 以这种方式创建一个函数跟
eval()
差不多,将会导致漏洞。
1// bad 2var add = new Function('a', 'b', 'return a + b'); 3 4// still bad 5var subtract = Function('a', 'b', 'return a - b');
7.11 【必须】 函数声明语句中需要空格。 eslint: space-before-function-paren
space-before-blocks
原因? 一致性很好,在删除或添加名称时不需要添加或删除空格。
1// bad 2const f = function(){}; 3const g = function (){}; 4const h = function() {}; 5 6// good 7const x = function () {}; 8const y = function a() {};
7.12 【推荐】 不要改变入参。 eslint: no-param-reassign
原因? 操作入参对象会导致原始调用位置出现意想不到的副作用。
1// bad 2function f1(obj) { 3 obj.key = 1; 4} 5 6// good 7function f2(obj) { 8 const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; 9}
7.13 【推荐】 不要对入参重新赋值,也不要给入参的属性赋值。部分要求修改入参的常用库(如 Koa、Vuex)可以豁免。 eslint: no-param-reassign
原因? 重新赋值参数会导致意外的行为,尤其是在访问
arguments
对象的时候。 它还可能导致性能优化问题,尤其是在 V8 中。
1// bad 2function f1(a) { 3 a = 1; 4 // ... 5} 6 7function f2(a) { 8 if (!a) { a = 1; } 9 // ... 10} 11 12// good 13function f3(a) { 14 const b = a || 1; 15 // ... 16} 17 18function f4(a = 1) { 19 // ... 20}
7.14 【推荐】 优先使用扩展运算符 ...
来调用可变参数函数。 eslint: prefer-spread
原因? 它更加清晰,你不需要提供上下文,并且能比用
apply
来执行可变参数的new
操作更容易些。
1// bad 2const x = [1, 2, 3, 4, 5]; 3console.log.apply(console, x); 4 5// good 6const x = [1, 2, 3, 4, 5]; 7console.log(...x); 8 9// bad 10new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); 11 12// good 13new Date(...[2016, 8, 5]);
7.15 【推荐】 调用或者书写一个包含多个参数的函数应该像这个指南里的其他多行代码写法一样: 每行值包含一个参数,并且最后一行也要以逗号结尾。eslint: function-paren-newline
1// bad 2function foo(bar, 3 baz, 4 quux) { 5 // ... 6} 7 8// good 9function foo( 10 bar, 11 baz, 12 quux, 13) { 14 // ... 15} 16 17// bad 18console.log(foo, 19 bar, 20 baz); 21 22// good 23console.log( 24 foo, 25 bar, 26 baz, 27);
8.1 【推荐】 当你必须使用匿名函数时 (当传递内联函数时), 使用箭头函数。 eslint: prefer-arrow-callback
, arrow-spacing
原因? 它创建了一个在
this
上下文中执行的函数版本,它通常是你想要的,并且是一个更简洁的语法。
什么时候不适用? 如果你有一个相当复杂的函数,你可能会把这些逻辑转移到它自己的命名函数表达式里。
1// bad 2[1, 2, 3].map(function (x) { 3 const y = x + 1; 4 return x * y; 5}); 6 7// good 8[1, 2, 3].map((x) => { 9 const y = x + 1; 10 return x * y; 11});
8.2 【推荐】 如果函数体由一个没有副作用的 表达式 语句组成,删除大括号和 return
。否则,保留括号并继续使用 return
语句。 eslint: arrow-parens
, arrow-body-style
原因? 语法糖。 多个函数被链接在一起时,提高可读性。
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 + 1}.`); 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// 没有副作用的隐式返回 22function foo(callback) { 23 const val = callback(); 24 if (val === true) { 25 // 如果回调返回 true 执行 26 } 27} 28 29let bool = false; 30 31// bad 32foo(() => bool = true); 33 34// good 35foo(() => { 36 bool = true; 37});
8.3 【推荐】 如果表达式跨越多个行,用括号将其括起来,以获得更好的可读性。
原因? 它清楚地表明了函数的起点和终点。
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 【推荐】 如果你的函数只有一个参数并且函数体没有大括号,就删除圆括号。 否则,为了保证清晰和一致性,请给参数加上括号。 注意:总是使用括号是可以接受的,在这种情况下,我们使用 “always” option 来配置 eslint. eslint: arrow-parens
原因? 让代码看上去不那么乱。
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
1// bad 2const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; 3 4// bad 5const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; 6 7// good 8const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); 9 10// good 11const itemHeight = (item) => { 12 const { height, largeSize, smallSize } = item; 13 return height > 256 ? largeSize : smallSize; 14};
8.6 【推荐】 在箭头函数用隐式 return 时强制将函数体的位置约束在箭头后。 eslint: implicit-arrow-linebreak
1// bad 2(foo) => 3 bar; 4 5(foo) => 6 (bar); 7 8// good 9(foo) => bar; 10(foo) => (bar); 11(foo) => ( 12 bar 13);
9.1 【推荐】 尽量使用 class
. 避免直接操作 prototype
.
原因?
class
语法更简洁,更容易看懂。
1// bad 2function Queue(contents = []) { 3 this.queue = [...contents]; 4} 5Queue.prototype.pop = function () { 6 const value = this.queue[0]; 7 this.queue.splice(0, 1); 8 return value; 9}; 10 11// good 12class 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}
9.2 【推荐】 使用 extends
来实现继承。
原因? 它是一个内置的方法,可以在不破坏
instanceof
的情况下继承原型功能。
1// bad 2const inherits = require('inherits'); 3function PeekableQueue(contents) { 4 Queue.apply(this, contents); 5} 6inherits(PeekableQueue, Queue); 7PeekableQueue.prototype.peek = function () { 8 return this.queue[0]; 9}; 10 11// good 12class PeekableQueue extends Queue { 13 peek() { 14 return this.queue[0]; 15 } 16}
9.3 【可选】 类的成员方法,可以返回 this
, 来实现链式调用。
1// bad 2Jedi.prototype.jump = function () { 3 this.jumping = true; 4 return true; 5}; 6 7Jedi.prototype.setHeight = function (height) { 8 this.height = height; 9}; 10 11const luke = new Jedi(); 12luke.jump(); // => true 13luke.setHeight(20); // => undefined 14 15// good 16class Jedi { 17 jump() { 18 this.jumping = true; 19 return this; 20 } 21 22 setHeight(height) { 23 this.height = height; 24 return this; 25 } 26} 27 28const luke = new Jedi(); 29 30luke.jump() 31 .setHeight(20);
9.4 【可选】 只要在确保能正常工作并且不产生任何副作用的情况下,编写一个自定义的 toString()
方法也是可以的。
1class Jedi { 2 constructor(options = {}) { 3 this.name = options.name || 'no name'; 4 } 5 6 getName() { 7 return this.name; 8 } 9 10 toString() { 11 return `Jedi - ${this.getName()}`; 12 } 13}
9.5 【推荐】 如果没有具体说明,类有默认的构造方法。一个空的构造函数或只是代表父类的构造函数是不需要写的。 eslint: no-useless-constructor
1// bad 2class Jedi { 3 constructor() {} 4 5 getName() { 6 return this.name; 7 } 8} 9 10// bad 11class Rey extends Jedi { 12 // 这种构造函数是不需要写的 13 constructor(...args) { 14 super(...args); 15 } 16} 17 18// good 19class Rey extends Jedi { 20 constructor(...args) { 21 super(...args); 22 this.name = 'Rey'; 23 } 24}
9.6 【必须】 避免定义重复的类成员。 eslint: no-dupe-class-members
原因? 重复的类成员声明将会默认使用最后一个 - 具有重复的类成员可以说是一个bug。
1// bad 2class Foo { 3 bar() { return 1; } 4 bar() { return 2; } 5} 6 7// good 8class Foo { 9 bar() { return 1; } 10} 11
[9.7] 【推荐】 类成员要么引用 this
,要么声明为静态方法,除非一个外部库或框架需要使用某些非静态方法。当一个方法为非静态方法时,一般表明它在不同的实例上会表现得不同。
10.1 【可选】 使用ES6的模块 (import
/export
) 语法来定义模块。
1// bad 2const AirbnbStyleGuide = require('./AirbnbStyleGuide'); 3module.exports = AirbnbStyleGuide.es6; 4 5// ok 6import AirbnbStyleGuide from './AirbnbStyleGuide'; 7export default AirbnbStyleGuide.es6; 8 9// best 10import { es6 } from './AirbnbStyleGuide'; 11export default es6;
10.2 【推荐】 不要使用import * 通配符
原因? 这确保你有单个默认的导出。
1// bad 2import * as AirbnbStyleGuide from './AirbnbStyleGuide'; 3 4// good 5import AirbnbStyleGuide from './AirbnbStyleGuide';
10.3 【推荐】 不要在import语句中直接export。
原因? 虽然写在一行很简洁,但是有一个明确的导入和一个明确的导出能够保证一致性。
1// bad 2// filename es6.js 3export { es6 as default } from './AirbnbStyleGuide'; 4 5// good 6// filename es6.js 7import { es6 } from './AirbnbStyleGuide'; 8export default es6;
10.4 【必须】 对于同一个路径,只在一个地方引入所有需要的东西。
eslint: no-duplicates
原因? 对于同一个路径,如果存在多行引入,会使代码更难以维护。
1// bad 2import foo from 'foo'; 3// … 其他导入 … // 4import { named1, named2 } from 'foo'; 5 6// good 7import foo, { named1, named2 } from 'foo'; 8 9// good 10import foo, { 11 named1, 12 named2, 13} from 'foo';
10.5 【推荐】 不要导出可变的引用。
eslint: import/no-mutable-exports
原因? 在一般情况下,应该避免导出可变引用。虽然在某些特殊情况下,可能需要这样,但是一般情况下只需要导出常量引用。
1// bad 2let foo = 3; 3export { foo }; 4 5// good 6const foo = 3; 7export { foo };
10.6 【可选】 在只有单一导出的模块里,用 export default 更好。
eslint: import/prefer-default-export
原因? 鼓励更多的模块只做单一导出,会增强代码的可读性和可维护性。
1// bad 2export function foo() {} 3 4// good 5export default function foo() {}
10.7 【必须】 将所有的 import
s 语句放在其他语句之前。
eslint: import/first
原因? 将所有的
import
s 提到顶部,可以防止某些诡异行为的发生。
1// bad 2import foo from 'foo'; 3foo.init(); 4 5import bar from 'bar'; 6 7// good 8import foo from 'foo'; 9import bar from 'bar'; 10 11foo.init();
10.8 【可选】 多行引入应该像多行数组和对象字面量一样缩进。
原因? 这里的花括号和其他地方的花括号是一样的,遵循相同的缩进规则。末尾的逗号也是一样的。
1// bad 2import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; 3 4// good 5import { 6 longNameA, 7 longNameB, 8 longNameC, 9 longNameD, 10 longNameE, 11} from 'path';
10.9 【推荐】 在模块导入语句中禁止使用 Webpack 加载器语法。
eslint: import/no-webpack-loader-syntax
原因? 因为在导入语句中使用 webpack 语法,会将代码和打包工具耦合在一起。应该在
webpack.config.js
中使用加载器语法。
1// bad 2import fooSass from 'css!sass!foo.scss'; 3import barCss from 'style!css!bar.css'; 4 5// good 6import fooSass from 'foo.scss'; 7import barCss from 'bar.css';
11.1 【推荐】 不要使用迭代器。 推荐使用 JavaScript 的高阶函数代替 for-in
。 eslint: no-iterator
no-restricted-syntax
原因? 这有助于不可变性原则。 使用带有返回值的纯函数比使用那些带有副作用的方法,更具有可读性。
使用
map()
/every()
/filter()
/find()
/findIndex()
/reduce()
/some()
/ ... 遍历数组, 和使用Object.keys()
/Object.values()
/Object.entries()
迭代你的对象生成数组。
1const numbers = [1, 2, 3, 4, 5]; 2 3// bad 4let sum = 0; 5for (let num of numbers) { 6 sum += num; 7} 8sum === 15; 9 10// good 11let sum = 0; 12numbers.forEach((num) => { 13 sum += num; 14}); 15sum === 15; 16 17// best (use the functional force) 18const sum = numbers.reduce((total, num) => total + num, 0); 19sum === 15; 20 21// bad 22const increasedByOne = []; 23for (let i = 0; i < numbers.length; i++) { 24 increasedByOne.push(numbers[i] + 1); 25} 26 27// good 28const increasedByOne = []; 29numbers.forEach((num) => { 30 increasedByOne.push(num + 1); 31}); 32 33// best (keeping it functional) 34const increasedByOne = numbers.map(num => num + 1);
11.2 【可选】 现在不要使用generator。
原因? 它们不能很好的转译为 ES5。但可以在Nodejs中使用。*
11.3 【推荐】 如果你必须要使用generator,请确保正确使用空格。 eslint: generator-star-spacing
原因?
function
和*
是同一个概念关键字的一部分 -*
不是function
的修饰符,function*
是一个不同于function
的构造器。
1// bad 2function * foo() { 3 // ... 4} 5 6// bad 7const bar = function * () { 8 // ... 9}; 10 11// bad 12const baz = function *() { 13 // ... 14}; 15 16// bad 17const quux = function*() { 18 // ... 19}; 20 21// bad 22function*foo() { 23 // ... 24} 25 26// bad 27function *foo() { 28 // ... 29} 30 31// very bad 32function 33* 34foo() { 35 // ... 36} 37 38// very bad 39const wat = function 40* 41() { 42 // ... 43}; 44 45// good 46function* foo() { 47 // ... 48} 49 50// good 51const foo = function* () { 52 // ... 53};
12.1 【推荐】 访问属性时使用点符号。 eslint: dot-notation
1const luke = { 2 jedi: true, 3 age: 28, 4}; 5 6// bad 7const isJedi = luke['jedi']; 8 9// good 10const isJedi = luke.jedi;
12.2 【可选】 使用变量访问属性时,用 []
表示法。
1const luke = { 2 jedi: true, 3 age: 28, 4}; 5 6function getProp(prop) { 7 return luke[prop]; 8} 9 10const isJedi = getProp('jedi');
12.3 【推荐】 计算指数时,可以使用 **
运算符。 eslint: no-restricted-properties
.
1// bad 2const binary = Math.pow(2, 10); 3 4// good 5const binary = 2 ** 10;
13.1 【必须】 变量应先声明再使用,禁止引用任何未声明的变量,除非你明确知道引用的变量存在于当前作用域链上。禁止不带任何关键词定义变量,这样做将会创建一个全局变量,污染全局命名空间,造成程序意料之外的错误。 eslint: no-undef
prefer-const
1// bad, 这会创建一个全局变量 2superPower = new SuperPower(); 3 4// good 5const superPower = new SuperPower(); 6 7// bad, 容易污染外部变量 8let superPower = 'a'; 9(function() { 10 superPower = 'b'; 11})(); 12console.log(superPower); 13 14// good 15let superPower = 'a'; 16(function() { 17 let superPower = 'b'; 18})(); 19console.log(superPower); 20 21// bad, 更常见的情况是这样的,在 for 循环里的 i 将会污染外部的变量 i 22let i = 1; 23(function() { 24 for (i = 0; i < 10; i++) { 25 console.log('inside', i); 26 } 27 28 console.log('outside', i) 29})(); 30console.log('global', i); 31 32// good 33let i = 1; 34(function() { 35 // i 的作用域在 for 循环内 36 for (let i = 0; i < 10; i++) { 37 console.log('inside i', i); 38 } 39 40 // 如果真的需要在 for 循环外使用循环变量,应该先定义在外部 41 let j; 42 43 for (j = 0; j < 10; j++) { 44 console.log('inside j:', j); 45 } 46 47 console.log('outside j', j); 48})(); 49console.log('global', i);
13.2 【推荐】 声明多个变量应该分开声明,避免使用 ,
一次声明多个变量。 eslint: one-var
原因? 这样更容易添加新的变量声明,不必担心是使用
;
还是使用,
所带来的代码差异。使用版本管理工具如 git ,最后那行的;
就不会被标记为修改成,
。 并且可以通过 debugger 逐步查看每个声明,而不是立即跳过所有声明。
1// bad 2const items = getItems(), 3 goSportsTeam = true, 4 dragonball = 'z'; 5 6// bad 7const items = getItems(), 8 goSportsTeam = true; 9 // 定义成了全局变量 10 dragonball = 'z'; 11 12// good 13const items = getItems(); 14const goSportsTeam = true; 15const dragonball = 'z';
13.3 【推荐】 把 const
声明语句放在一起,把 let
声明语句放在一起。
原因? 这在后边如果需要根据前边的赋值变量指定一个变量时很有用,且更容易知道哪些变量是不希望被修改的。
1// bad 2let i; 3const items = getItems(); 4let dragonball; 5const goSportsTeam = true; 6let len; 7 8// good 9const goSportsTeam = true; 10const items = getItems(); 11let dragonball; 12let i; 13let length;
13.4 【推荐】 在你真正需要使用到变量的代码块内定义变量。
原因?
let
和const
是块级作用域而不是函数作用域,不存在变量提升的情况。
1// bad, 不必要的函数调用 2function 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 18function 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
原因? 链式变量赋值会创建隐式全局变量。
1// bad 2(function example() { 3 /* 4 * JavaScript 把它解释为 5 * let a = ( b = ( c = 1 ) ); 6 * let 关键词只适用于变量 a,变量 b 和变量 c 则变成了全局变量。 7 */ 8 let a = b = c = 1; 9}()); 10 11// throws ReferenceError 12console.log(a); 13// 1 14console.log(b); 15// 1 16console.log(c); 17 18// good 19(function example() { 20 let a = 1; 21 let b = a; 22 let c = a; 23}()); 24 25// throws ReferenceError 26console.log(a); 27// throws ReferenceError 28console.log(b); 29// throws ReferenceError 30console.log(c); 31 32// 对于 `const` 也一样
13.6 【必须】 避免使用不必要的递增和递减操作符 (++
, --
)。 eslint no-plusplus
原因? 在eslint文档中,一元操作符
++
和--
会自动添加分号,不同的空白可能会改变源代码的语义。建议使用num += 1
这样的语句来做递增和递减,而不是使用num++
或num ++
。同时++num
和num++
的差异也使代码的可读性变差。不必要的增量和减量语句会导致无法预先明确递增/预递减值,这可能会导致程序中的意外行为。
但目前依然允许在 for loop 中使用
++
、--
的语法,但依然建议尽快迁移到+= 1
、-= 1
的语法。 #22
1// bad, i = 11, j = 20 2let i = 10; 3let j = 20; 4i ++ 5j 6 7// bad, i = 10, j = 21 8let i = 10; 9let j = 20; 10i 11++ 12j 13 14// bad 15const array = [1, 2, 3]; 16let num = 1; 17num++; 18--num; 19 20// not good, just acceptable for upforward compatible. 21let sum = 0; 22let truthyCount = 0; 23for (let i = 0; i < array.length; i++) { 24 let value = array[i]; 25 sum += value; 26 if (value) { 27 truthyCount++; 28 } 29} 30 31// good 32const array = [1, 2, 3]; 33let num = 1; 34num += 1; 35num -= 1; 36 37// good 38const sum = array.reduce((a, b) => a + b, 0); 39const truthyCount = array.filter(Boolean).length;
13.7 【必须】 避免在赋值语句 =
前后换行。如果你的代码单行长度超过了 max-len
定义的长度而不得不换行,那么使用括号包裹。 eslint operator-linebreak
.
原因? 在
=
前后换行,可能混淆赋的值。
1// bad 2const foo 3 = 'superLongLongLongLongLongLongLongLongString'; 4 5// bad 6const bar = 7 superLongLongLongLongLongLongLongLongFunctionName(); 8 9// bad 10const fullHeight = borderTop + 11 innerHeight + 12 borderBottom; 13 14// bad 15const anotherHeight = borderTop + 16 innerHeight + 17 borderBottom; 18 19// bad 20const thirdHeight = ( 21 borderTop + 22 innerHeight + 23 borderBottom 24); 25 26// good - max-len 会忽略字符串,直接写后面即可。 27const foo = 'superLongLongLongLongLongLongLongLongString'; 28 29// good 30const bar = ( 31 superLongLongLongLongLongLongLongLongFunctionName() 32); 33 34// good 35const fullHeight = borderTop 36 + innerHeight 37 + borderBottom; 38 39// good 40const anotherHeight = borderTop 41 + innerHeight 42 + borderBottom; 43 44// good 45const thirdHeight = ( 46 borderTop 47 + innerHeight 48 + borderBottom 49); 50
13.8 【必须】 禁止定义了变量却不使用它。 eslint: no-unused-vars
原因? 在代码里到处定义变量却没有使用它,不完整的代码结构看起来像是个代码错误。即使没有使用,但是定义变量仍然需要消耗资源,并且对阅读代码的人也会造成困惑,不知道这些变量是要做什么的。
1// bad 2let some_unused_var = 42; 3 4// bad,定义了变量不意味着就是使用了 5let y = 10; 6y = 5; 7 8// bad,对自身的操作并不意味着使用了 9let z = 0; 10z = z + 1; 11 12// bad, 未使用的函数参数 13function getX(x, y) { 14 return x; 15} 16 17// good 18function getXPlusY(x, y) { 19 return x + y; 20} 21 22let x = 1; 23let y = a + 2; 24 25alert(getXPlusY(x, y)); 26 27/* 28 * 有时候我们想要提取某个对象排除了某个属性外的其他属性,会用 rest 参数解构对象 29 * 这时候 type 虽然未使用,但是仍然被定义和赋值,这也是一种空间的浪费 30 * type 的值是 'a' 31 * coords 的值是 data 对象,但是没有 type 属性 { example1: 'b', example2: 'c' } 32 */ 33let data = { type: 'a', example1: 'b', example2: 'c' } 34let { type, ...coords } = data; 35 36/* 37 * 有时候如果定义了一个基类或者接口(Typescript) 38 * 基类或接口中该方法需要定义这个参数,但消费参数是由子类自行实现的 39 * 可以通过在变量名前加下划线的方式来表示该变量是未使用的 40 */ 41export class Model { 42 toList(_isCompact) { 43 // 待子类实现 44 } 45} 46 47export class MyModel extends Model { 48 data = []; 49 toList(isCompact) { 50 return isCompact ? this.data.flat() : this.data; 51 } 52}
14.1 【可选】 var
定义的变量会被提升到函数作用域范围内的最顶部,但是对它的赋值是不会被提升的,因此在函数顶部相当于定义了变量,但是值是 undefined
。const
和 let
声明的变量受到一个称之为 "暂时性死区" (Temporal Dead Zones ,简称 TDZ) 的新概念保护,因此在 "暂时性死区" 内部的 const
和 let
变量,都需要先声明再使用,否则会报错。详情可以阅读 typeof 不再安全 这篇文章。
1// notDefined 未定义 (假设没有定义的同名全局变量) 2function example() { 3 // => throws a ReferenceError 4 console.log(notDefined); 5} 6 7/* 8* 函数体内部因存在 var 变量声明,因此在引用变量的语句之前,变量提升就已经起作用了 9* 注意: 真正的值 `true` 不会被提升。 10*/ 11function example() { 12 // => undefined 13 console.log(declaredButNotAssigned); 14 var declaredButNotAssigned = true; 15} 16 17/* 18* 解释器将变量提升到函数的顶部 19* 这意味着我们可以将上边的例子重写为: 20*/ 21function example() { 22 let declaredButNotAssigned; 23 // => undefined 24 console.log(declaredButNotAssigned); 25 declaredButNotAssigned = true; 26} 27 28// 使用 const 和 let 29function example() { 30 // => throws a ReferenceError 31 console.log(declaredButNotAssigned); 32 // => throws a ReferenceError 33 console.log(typeof declaredButNotAssigned); 34 const declaredButNotAssigned = true; 35}
14.2 【可选】 匿名函数赋值表达式提升变量名,而不是函数赋值。
1function example() { 2 // => undefined 3 console.log(anonymous); 4 5 // => TypeError anonymous is not a function 6 anonymous(); 7 8 var anonymous = function () { 9 console.log('anonymous function expression'); 10 }; 11}
14.3 【可选】 命名函数表达式提升的是变量名,而不是函数名或者函数体。
1function example() { 2 // => undefined 3 console.log(named); 4 5 // => TypeError named is not a function 6 named(); 7 8 // => ReferenceError superPower is not defined 9 superPower(); 10 11 var named = function superPower() { 12 console.log('Flying'); 13 }; 14} 15 16// 当函数名和变量名相同时也是如此。 17function example() { 18 // => undefined 19 console.log(named); 20 21 // => TypeError named is not a function 22 named(); 23 24 var named = function named() { 25 console.log('named'); 26 }; 27}
14.4 【可选】 函数声明提升其名称和函数体。
1function example() { 2 // => Flying 3 superPower(); 4 5 function superPower() { 6 console.log('Flying'); 7 } 8}
15.1 【推荐】 使用 ===
和 !==
而不是 ==
和 !=
。 eslint: eqeqeq
原因?
==
和!=
存在类型转换,会得到和===
、!==
不一样的结果
1// bad, true 2undefined == null 3// good, false 4undefined === null 5 6// bad, true 7'0' == 0 8// good, false 9'0' === 0 10 11// bad, true 120 == false 13// good, false 140 === false 15 16// bad, true 17'' == false 18// good, false 19'' === false
15.2 【可选】 条件语句,例如 if
语句使用 ToBoolean
的抽象方法来计算表达式的结果,并始终遵循以下简单的规则:
''
值为 false 否则为 true1if ([0] && []) { 2 // true, 数组(即使是空的)是一个对象,对象的取值为 true 3}
15.3 【推荐】 对于布尔值(在明确知道是布尔值的情况下)使用简写,但是对于字符串和数字进行显式比较。
1// bad 2if (isValid === true) { 3 // ... 4} 5 6// good 7if (isValid) { 8 // ... 9} 10 11// bad 12if (name) { 13 // ... 14} 15 16// good 17if (name !== '') { 18 // ... 19} 20 21// bad 22if (collection.length) { 23 // ... 24} 25 26// good 27if (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
语句定义相同的变量时,就会导致变量覆盖的问题。
1// bad 2switch (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 19switch (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
1// bad 2const foo = maybe1 > maybe2 3 ? "bar" 4 : value1 > value2 ? "baz" : null; 5 6// 分离为两个三元表达式 7const maybeNull = value1 > value2 ? 'baz' : null; 8 9// better 10const foo = maybe1 > maybe2 11 ? 'bar' 12 : maybeNull; 13 14// best 15const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
15.7 【推荐】 避免不必要的三元表达式。 eslint: no-unneeded-ternary
1// bad 2const foo = a ? a : b; 3const bar = c ? true : false; 4const baz = c ? false : true; 5 6// good 7const foo = a || b; 8const bar = !!c; 9const baz = !c;
15.8 【必须】 使用混合运算符时,使用小括号括起来需要一起计算的部分,只要觉得有必要,那么尽可能地用括号让代码的优先级更明显。大家都能理解的运算符 +
、-
、**
不要求添加括号。但我们建议在 *
、/
之间添加括号,因为乘除运算符混写写的比较长的时候容易产生歧义。 eslint: no-mixed-operators
原因? 这能提高可读性并且表明开发人员的意图。
1// bad 2const foo = a && b < 0 || c > 0 || d + 1 === 0; 3 4// bad 5const bar = a ** b - 5 % d; 6 7// bad, 可能陷入一种 (a || b) && c 的思考 8if (a || b && c) { 9 return d; 10} 11 12// bad 13const bar1 = a + b / c * (d / e); 14 15// good 16const foo = (a && b < 0) || (c > 0) || (d + 1 === 0); 17 18// good 19const bar = (a ** b) - (5 % d); 20 21// good 22if (a || (b && c)) { 23 return d; 24} 25 26// good 27const bar1 = a + (b / c) * (d / e);
16.1 【必须】 当有多行代码块的时候,应使用大括号包裹。 eslint: nonblock-statement-body-position
1// bad 2if (test) 3 return false; 4 5// bad 6let condition = true; 7let test = 1; 8// 在缩进不规范的时候,容易造成误解 9if (condition) 10 condition = false; 11 test = 2; 12 13// good 14if (test) return false; 15 16// good 17if (test) { 18 return false; 19} 20 21// bad 22function foo() { return false; } 23 24// good 25function bar() { 26 return false; 27}
16.2 【必须】 如果你使用的是 if
和 else
的多行代码块,则将 else
语句放在 if
块闭括号同一行的位置。 eslint: brace-style
1// bad 2if (test) { 3 thing1(); 4 thing2(); 5} 6else { 7 thing3(); 8} 9 10// good 11if (test) { 12 thing1(); 13 thing2(); 14} else { 15 thing3(); 16}
16.3 【推荐】 如果一个 if
块总是会执行 return 语句,那么接下来的 else
块就没有必要了。 如果一个包含 return
语句的 else if
块,在一个包含了 return
语句的 if
块之后,那么可以拆成多个 if
块。 eslint: no-else-return
1// bad 2function foo() { 3 if (x) { 4 return x; 5 } else { 6 return y; 7 } 8} 9 10// bad 11function cats() { 12 if (x) { 13 return x; 14 } else if (y) { 15 return y; 16 } 17} 18 19// bad 20function dogs() { 21 if (x) { 22 return x; 23 } else { 24 if (y) { 25 return y; 26 } 27 } 28} 29 30// good 31function foo() { 32 if (x) { 33 return x; 34 } 35 36 return y; 37} 38 39// good 40function cats() { 41 if (x) { 42 return x; 43 } 44 45 if (y) { 46 return y; 47 } 48} 49 50// good 51function dogs(x) { 52 if (x) { 53 if (z) { 54 return y; 55 } 56 } else { 57 return z; 58 } 59}
17.1 【推荐】 如果你的控制语句 (if
, while
等) 太长或者超过了一行最大长度的限制,则可以将每个条件(或组)放入一个新的行。 逻辑运算符应该在行的开始。
原因? 在行的开头要求运算符保持对齐,并遵循类似于方法链的模式。这提高了可读性,并且使更复杂的逻辑更容易直观的被理解。
1// bad 2if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { 3 thing1(); 4} 5 6// bad 7if (foo === 123 && 8 bar === 'abc') { 9 thing1(); 10} 11 12// bad 13if (foo === 123 14 && bar === 'abc') { 15 thing1(); 16} 17 18// bad 19if ( 20 foo === 123 && 21 bar === 'abc' 22) { 23 thing1(); 24} 25 26// good 27if ( 28 foo === 123 29 && bar === 'abc' 30) { 31 thing1(); 32} 33 34// good 35if ( 36 (foo === 123 || bar === 'abc') 37 && doesItLookGoodWhenItBecomesThatLong() 38 && isThisReallyHappening() 39) { 40 thing1(); 41} 42 43// good 44if (foo === 123 && bar === 'abc') { 45 thing1(); 46}
17.2 【必须】 不要使用选择操作符代替控制语句。
1// bad 2!isRunning && startRunning(); 3 4// good 5if (!isRunning) { 6 startRunning(); 7}
18.1 【必须】 使用 /* ... */
来进行多行注释。
1// bad 2// make() returns a new element 3// based on the passed in tag name 4// 5// @param {String} tag 6// @return {Element} element 7function make(tag) { 8 9 // ... 10 11 return element; 12} 13 14// good 15/* 16 * make() returns a new element 17 * based on the passed-in tag name 18 */ 19function make(tag) { 20 21 // ... 22 23 return element; 24}
18.2 【推荐】 使用 //
进行单行注释。 将单行注释放在需要注释的行的上方新行。 建议在注释之前放一个空行,除非它在块的第一行。但如果一段代码每行都包含注释,允许不加分行。
1// bad 2const active = true; // is current tab 3 4// good 5// is current tab 6const active = true; 7 8// bad 9function getType() { 10 console.log('fetching type...'); 11 // set the default type to 'no type' 12 const type = this.type || 'no type'; 13 14 return type; 15} 16 17// good 18function getType() { 19 console.log('fetching type...'); 20 21 // set the default type to 'no type' 22 const type = this.type || 'no type'; 23 24 return type; 25} 26 27// also good 28function 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
1// bad 2//is current tab 3const active = true; 4 5// good 6// is current tab 7const active = true; 8 9// bad 10/** 11 *make() returns a new element 12 *based on the passed-in tag name 13 */ 14function 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 */ 26function make(tag) { 27 28 // ... 29 30 return element; 31}
18.4 【推荐】 使用 FIXME
或者 TODO
开始你的注释可以帮助其他开发人员快速了解相应代码,如果你提出了一个需要重新讨论的问题,或者你对需要解决的问题提出的解决方案。 这些不同于其他普通注释,因为它们是可操作的。 这些操作是 FIXME: -- 需要解决这个问题
或者 TODO: -- 需要被实现
。
18.5 【推荐】 使用 // FIXME:
注释问题。
1class Calculator extends Abacus { 2 constructor() { 3 super(); 4 5 // FIXME: 这里不应该使用全局变量 6 total = 0; 7 } 8}
18.6 【推荐】 使用 // TODO:
注释解决问题的方法。
1class Calculator extends Abacus { 2 constructor() { 3 super(); 4 5 // TODO: total 应该由一个 param 的选项配置 6 this.total = 0; 7 } 8}
18.7 【必须】 /** ... */
风格(首行有两个 *)的块级多行注释仅能被用于 JSDoc。
1class Calculator { 2 /** 3 * 用于将多个数字相加求和 4 * 5 * @param {number[]} nums 一系列用于相加的数字 6 * @returns {number} 最终的和 7 */ 8 add(...nums) { 9 return nums.reduce((res, num) => res + num, 0); 10 } 11}
19.1 【推荐】 使用 tabs (空格字符) 设置为2个空格。 eslint: indent
1// bad 2function foo() { 3∙∙∙∙let name; 4} 5 6// bad 7function bar() { 8∙let name; 9} 10 11// good 12function baz() { 13∙∙let name; 14}
19.2 【必须】 在花括号前放置一个空格。 eslint: space-before-blocks
1// bad 2function test(){ 3 console.log('test'); 4} 5 6// good 7function test() { 8 console.log('test'); 9} 10 11// bad 12dog.set('attr',{ 13 age: '1 year', 14 breed: 'Bernese Mountain Dog', 15}); 16 17// good 18dog.set('attr', { 19 age: '1 year', 20 breed: 'Bernese Mountain Dog', 21});
19.3 【必须】 在控制语句中的左括号前放置1个空格(if,while等)。在函数调用和声明中,参数列表和函数名之间不能留空格。keyword-spacing
1// bad 2if(isJedi) { 3 fight (); 4} 5 6// good 7if (isJedi) { 8 fight(); 9} 10 11// bad 12function fight () { 13 console.log ('Swooosh!'); 14} 15 16// good 17function fight() { 18 console.log('Swooosh!'); 19}
19.4 【必须】 运算符左右设置各设置一个空格 eslint: space-infix-ops
1// bad 2const x=y+5; 3 4// good 5const x = y + 5;
19.5 【必须】 在文件的结尾需要保留一个空行 eslint: eol-last
1// bad 2import { es6 } from './AirbnbStyleGuide'; 3// ... 4export default es6;
1// bad 2import { es6 } from './AirbnbStyleGuide'; 3// ... 4export default es6;↵ 5↵
1// good 2import { es6 } from './AirbnbStyleGuide'; 3// ... 4export default es6;↵
19.6 【推荐】 在编写多个方法链式调用(超过两个方法链式调用)时。 使用前导点,强调这行是一个方法调用,而不是一个语句。
eslint: newline-per-chained-call
no-whitespace-before-property
1// bad 2$('#items').find('.selected').highlight().end().find('.open').updateCount(); 3 4// bad 5$('#items'). 6 find('.selected'). 7 highlight(). 8 end(). 9 find('.open'). 10 updateCount(); 11 12// good 13$('#items') 14 .find('.selected') 15 .highlight() 16 .end() 17 .find('.open') 18 .updateCount(); 19 20// bad 21const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').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 27const leds = stage.selectAll('.led') 28 .data(data) 29 .enter().append('svg:svg') 30 .classed('led', true) 31 .attr('width', (radius + margin) * 2) 32 .append('svg:g') 33 .attr('transform', `translate(${radius + margin},${radius + margin})`) 34 .call(tron.led); 35 36// good 37const leds = stage.selectAll('.led').data(data);
19.7 【推荐】 在块和下一个语句之前留下一空白行。
1// bad 2if (foo) { 3 return bar; 4} 5return baz; 6 7// good 8if (foo) { 9 return bar; 10} 11 12return baz; 13 14// bad 15const obj = { 16 foo() { 17 }, 18 bar() { 19 }, 20}; 21return obj; 22 23// good 24const obj = { 25 foo() { 26 }, 27 28 bar() { 29 }, 30}; 31 32return obj; 33 34// bad 35const arr = [ 36 function foo() { 37 }, 38 function bar() { 39 }, 40]; 41return arr; 42 43// good 44const arr = [ 45 function foo() { 46 }, 47 48 function bar() { 49 }, 50]; 51 52return arr;
19.8 【必须】 不要在块的开头使用空白行。 eslint: padded-blocks
1// bad 2function bar() { 3 4 console.log(foo); 5 6} 7 8// bad 9if (baz) { 10 11 console.log(qux); 12} else { 13 console.log(foo); 14 15} 16 17// bad 18class Foo { 19 20 constructor(bar) { 21 this.bar = bar; 22 } 23} 24 25// good 26function bar() { 27 console.log(foo); 28} 29 30// good 31if (baz) { 32 console.log(qux); 33} else { 34 console.log(foo); 35}
19.9 【必须】 不要使用多个空行填充代码。 eslint: no-multiple-empty-lines
1// bad 2class Person { 3 constructor(fullName, email, birthday) { 4 this.fullName = fullName; 5 6 7 this.email = email; 8 9 10 this.setAge(birthday); 11 } 12 13 14 setAge(birthday) { 15 const today = new Date(); 16 17 18 const age = this.getAge(today, birthday); 19 20 21 this.age = age; 22 } 23 24 25 getAge(today, birthday) { 26 // .. 27 } 28} 29 30// good 31class Person { 32 constructor(fullName, email, birthday) { 33 this.fullName = fullName; 34 this.email = email; 35 this.setAge(birthday); 36 } 37 38 setAge(birthday) { 39 const today = new Date(); 40 const age = getAge(today, birthday); 41 this.age = age; 42 } 43 44 getAge(today, birthday) { 45 // .. 46 } 47}
19.10 【必须】 不要在括号内添加空格。 eslint: space-in-parens
1// bad 2function bar( foo ) { 3 return foo; 4} 5 6// good 7function bar(foo) { 8 return foo; 9} 10 11// bad 12if ( foo ) { 13 console.log(foo); 14} 15 16// good 17if (foo) { 18 console.log(foo); 19}
19.11 【必须】 不要在中括号中添加空格。 eslint: array-bracket-spacing
1// bad 2const foo = [ 1, 2, 3 ]; 3console.log(foo[ 0 ]); 4 5// good 6const foo = [1, 2, 3]; 7console.log(foo[0]);
19.12 【推荐】 在花括号内添加空格。 eslint: object-curly-spacing
1// bad 2const foo = {clark: 'kent'}; 3 4// good 5const foo = { clark: 'kent' };
19.13 【可选】 避免让你的代码行超过120个字符(包括空格)。 注意:根据上边的规则,长字符串编写可不受该规则约束,不应该被分解。 eslint: max-len
原因? 这样能够提升代码可读性和可维护性。
1// bad 2const 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 8const 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// better 16const foo = jsonData 17 ?.foo 18 ?.bar 19 ?.baz 20 ?.quux 21 ?.xyzzy; 22 23// good 24$.ajax({ 25 method: 'POST', 26 url: 'https://airbnb.com/', 27 data: { name: 'John' }, 28}) 29 .done(() => console.log('Congratulations!')) 30 .fail(() => console.log('You have failed this city.'));
19.14 【必须】 要求打开的块标志和同一行上的标志拥有一致的间距。此规则还会在同一行关闭的块标记和前边的标记强制实施一致的间距。 eslint: block-spacing
1// bad 2function foo() {return true;} 3if (foo) { bar = 0;} 4 5// good 6function foo() { return true; } 7if (foo) { bar = 0; }
19.15 【必须】 逗号之前避免使用空格,逗号之后需要使用空格。eslint: comma-spacing
1// bad 2const arr = [1 , 2]; 3 4// good 5const arr = [1, 2];
19.16 【推荐】 不要在计算属性括号内插入空格。eslint: computed-property-spacing
1// bad 2obj[foo ] 3obj[ 'foo'] 4var x = {[ b ]: a} 5obj[foo[ bar ]] 6 7// good 8obj[foo] 9obj['foo'] 10var x = { [b]: a } 11obj[foo[bar]]
19.17 【必须】 避免在函数名及其入参括号之间插入空格。 eslint: func-call-spacing
1// bad 2func (); 3 4func 5(); 6 7// good 8func();
19.18 【必须】 在对象的属性和值之间的冒号前不加空格,冒号后加空格。 eslint: key-spacing
1// bad 2var obj = { foo : 42 }; 3var obj2 = { foo:42 }; 4 5// good 6var obj = { foo: 42 };
19.19 【必须】 避免在行尾添加空格。 eslint: no-trailing-spaces
19.20 【必须】 在代码开始处不允许存在空行,行间避免出现多个空行,而结尾处必须保留一个空行。 eslint: no-multiple-empty-lines
1// bad 2const x = 1; 3 4 5 6const y = 2; 7 8// good 9const x = 1; 10 11const y = 2;
19.21 【推荐】 推荐使用 Unix 的 LF 作为换行符,而不是 Windows 的 CRLF,这样可以统一文件的换行符,避免因为换行符导致的格式混乱。
20.1 【必须】 逗号不能前置 eslint: comma-style
1// bad 2const story = [ 3 once 4 , upon 5 , aTime 6]; 7 8// good 9const story = [ 10 once, 11 upon, 12 aTime, 13]; 14 15// bad 16const hero = { 17 firstName: 'Ada' 18 , lastName: 'Lovelace' 19 , birthYear: 1815 20 , superPower: 'computers' 21}; 22 23// good 24const hero = { 25 firstName: 'Ada', 26 lastName: 'Lovelace', 27 birthYear: 1815, 28 superPower: 'computers', 29};
20.2 【推荐】 添加尾随逗号: 可以 eslint: comma-dangle
原因? 在 git diff 时能够更加清晰地查看改动。 另外,像Babel这样的编译器,会在转译时删除代码中的尾逗号,这意味着你不必担心旧版浏览器中的尾随逗号问题 。
1// bad - 没有尾随逗号的 git 差异 2const hero = { 3 firstName: 'Florence', 4- lastName: 'Nightingale' 5+ lastName: 'Nightingale', 6+ inventorOf: ['coxcomb chart', 'modern nursing'] 7}; 8 9// good - 有尾随逗号的 git 差异 10const hero = { 11 firstName: 'Florence', 12 lastName: 'Nightingale', 13+ inventorOf: ['coxcomb chart', 'modern nursing'], 14};
1// bad 2const hero = { 3 firstName: 'Dana', 4 lastName: 'Scully' 5}; 6 7const heroes = [ 8 'Batman', 9 'Superman' 10]; 11 12// good 13const hero = { 14 firstName: 'Dana', 15 lastName: 'Scully', 16}; 17 18const heroes = [ 19 'Batman', 20 'Superman', 21]; 22 23// bad 24function createHero( 25 firstName, 26 lastName, 27 inventorOf 28) { 29 // does nothing 30} 31 32// good 33function createHero( 34 firstName, 35 lastName, 36 inventorOf, 37) { 38 // does nothing 39} 40 41// good (注意逗号不能出现在 "rest" 元素后边) 42function createHero( 43 firstName, 44 lastName, 45 inventorOf, 46 ...heroArgs 47) { 48 // does nothing 49} 50 51// bad 52createHero( 53 firstName, 54 lastName, 55 inventorOf 56); 57 58// good 59createHero( 60 firstName, 61 lastName, 62 inventorOf, 63); 64 65// good (注意逗号不能出现在 "rest" 元素后边) 66createHero( 67 firstName, 68 lastName, 69 inventorOf, 70 ...heroArgs 71);
21.1 【推荐】 对于 jQuery 对象一律使用 $
符作为前缀。
1// bad 2const sidebar = $('.sidebar'); 3 4// good 5const $sidebar = $('.sidebar'); 6 7// good 8const $sidebarBtn = $('.sidebar-btn');
21.2 【推荐】 缓存 jQuery 查询,节省 DOM 查询开销。
1// bad 2function setSidebar() { 3 $('.sidebar').hide(); 4 5 // ... 6 7 $('.sidebar').css({ 8 'background-color': 'pink', 9 }); 10} 11 12// good 13function setSidebar() { 14 const $sidebar = $('.sidebar'); 15 $sidebar.hide(); 16 17 // ... 18 19 $sidebar.css({ 20 'background-color': 'pink', 21 }); 22}
21.3 【推荐】 能通过一次调用查找到的,不要分多次;在已有对象内查询,使用 find
函数,减少重复查询。
1// bad 2$('ul', '.sidebar').hide(); 3 4// bad 5$('.sidebar').find('ul').hide(); 6 7// good 8$('.sidebar ul').hide(); 9 10// good 11$('.sidebar > ul')
No vulnerabilities found.
No security vulnerabilities found.