目录
以下规则说明均来自
airbnb
变量声明
prefer-const; no-const-assign
所有的赋值都用
const,避免使用var。eslint:prefer-const,no-const-assign为什么?因为这个能确保你不会改变你的初始值,重复引用会导致 bug 并且使代码变得难以理解。
javascript// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
no-var
- 如果你一定要对参数重新赋值,使用
let,而不是var。eslint:no-var
为什么?因为
let是块级作用域,而var是函数级作用域。
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}no-use-before-define
变量、类、函数都应该在使用前定义。 eslint: no-use-before-define
为什么? 当变量、类或者函数在使用处之后定义,这让阅读者很难想到这个函数引用自何处。 对于读者在遇到某个事物之前,如果能知道这个事物的来源(不论是在文件中定义还是从别的模块引用),理解起来都会清晰很多。
// 不好的
// 变量 a 使用出现在定义之前
console.log(a); // 这样会导致 undefined,虽然变量声明被提升了, 但 a 初始化复制却还没执行
var a = 10;
// 函数 fun 使用出现在定义之前
fun();
function fun() {}
// 类 A 使用出现在定义之前
new A(); // 引用错误: 无法在 A 初始化之前访问它
class A {}
// `let` 和 `const` 被提升, 但是他们没有初始化变量值
// 变量 a、 b 都被放在了 JavaScript 的暂时性死区 (Temporal Dead Zone, 指在变量被声明之前无法访问它的现象)。
console.log(a); // 引用错误: 无法在 a 初始化之前访问它
console.log(b); // 引用错误: 无法在 b 初始化之前访问它
let a = 10;
const b = 5;
// 好的
var a = 10;
console.log(a); // 10
function fun() {}
fun();
class A {}
new A();
let a = 10;
const b = 5;
console.log(a); // 10
console.log(b); // 5- 详情请见 JavaScript Scoping & Hoisting by Ben Cherry.
对象
no-new-object
使用字面值创建对象。eslint:
no-new-objectjavascript// bad const item = new Object(); // good const item = {};
object-shorthand
用对象方法简写。eslint: object-shorthand
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
// 对象的方法
addValue(value) {
return atom.value + value;
},
};object-shorthand
用属性值缩写。eslint: object-shorthand
为什么?这样写更简洁,且可读性更高。
const lukeSkywalker = "Luke Skywalker";
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};prefer-object-spread
对象浅拷贝时,更推荐使用扩展运算符(即 ... 运算符),而不是 Object.assign。获取对象指定的几个属性时,用对象的 rest 解构运算符(即 ... 运算符)更好。eslint: prefer-object-spread
- 这一段不太好翻译出来, 大家看下面的例子就懂了。^.^
// 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 es6 扩展运算符 ...
const original = { a: 1, b: 2 };
// 浅拷贝
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
// rest 解构运算符
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }数组
no-array-constructor
用字面量创建数组。eslint: no-array-constructor
// bad
const items = new Array();
// good
const items = [];arrays-callback-return
在数组方法的回调函数中使用 return 语句。如果函数体由一条返回一个表达式的语句组成,并且这个表达式没有副作用, 这个时候可以忽略 return。eslint: array-callback-return
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good 函数只有一个语句
[1, 2, 3].map((x) => x + 1);
// bad - 没有返回值, 因为在第一次迭代后 acc 就变成 undefined 了
[
[0, 1],
[2, 3],
[4, 5],
].reduce((acc, item, index) => {
const flatten = acc.concat(item);
});
// good
[
[0, 1],
[2, 3],
[4, 5],
].reduce((acc, item, index) => {
const flatten = acc.concat(item);
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;
});解构
prefer-destructuring
用对象的解构赋值来获取和使用对象某个或多个属性值。eslint: prefer-destructuring
为什么? 解构使您不必为这些属性创建临时引用,并且避免重复引用对象。重复引用对象将造成代码重复、增加阅读次数、提高犯错概率。在一个块级作用域里,解构对象可以在同一个地方给解构字段赋值,而不需要读整个的代码块看它到底用了哪些字段。
// 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}`;
}用数组解构。eslint: prefer-destructuring
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;字符串
prefer-template
当需要动态生成字符串时,使用模板字符串而不是字符串拼接。eslint: prefer-template template-curly-spacing
为什么?模板字符串更具可读性、多行语法更简洁以及更方便插入变量到字符串里头。
// 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}?`;
}no-eval
永远不要使用 eval(),该方法有太多漏洞。eslint: no-eval
函数
wrap-iife
把立即执行函数包裹在圆括号里。eslint: wrap-iife
立即执行函数:Immediately Invoked Function expression = IIFE。 为什么?一个立即调用的函数表达式是一个单元 - 把它和它的调用者(圆括号)包裹起来,使代码读起来更清晰。 另外,在模块化世界里,你几乎用不着 IIFE。
// immediately-invoked function expression (IIFE)
(function () {
console.log("Welcome to the Internet. Please follow me.");
})();no-loop-func
不要在非函数块(if、while 等)内声明函数。把这个函数分配给一个变量。浏览器会允许你这样做,但不同浏览器的解析方式不同,这是一个坏消息。eslint: no-loop-func
prefer-rest-params
不要使用 arguments,用收集参数语法 ... 代替。eslint: prefer-rest-params
为什么?
...明确你想用哪个参数。而且收集参数是真数组,而不是类似数组的arguments。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join("");
}
// good
function concatenateAll(...args) {
return args.join("");
}default-param-last
把默认参数赋值放在最后。eslint: default-param-last
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}no-new-func
不要用函数构造器创建函数。eslint: no-new-func
为什么?以这种方式创建函数将类似于字符串 eval(),存在漏洞。
// bad
const add = new Function("a", "b", "return a + b");
// still bad
const subtract = Function("a", "b", "return a - b");no-param-reassign
不要修改参数. 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;
}箭头函数
arrow-parens
如果函数体由一个没有副作用的 表达式 语句组成,删除大括号和 return。否则,使用大括号和 return 语句。 eslint: arrow-parens, arrow-body-style
为什么?语法糖,当多个函数链在一起的时候好读。
// bad map 没有 return
[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 + 1}.`);
// 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,
}));
// 没有明显的存在副作用的 return 语句
function foo(callback) {
const val = callback();
if (val === true) {
// 当 callback 返回 true 时在这里执行
}
}
let bool = false;
// bad
foo(() => (bool = true));
// good
foo(() => {
bool = true;
});模块
no-duplicate-imports
一个路径只 import 一次。eslint: no-duplicate-imports
为什么?多行导入同一路径将使代码变得难以维护。
// bad
import foo from "foo";
// … 其他导入 … //
import { named1, named2 } from "foo";
// good
import foo, { named1, named2 } from "foo";
// good
import foo, { named1, named2 } from "foo";import/no-mutable-exports
不要导出可变的东西。eslint: import/no-mutable-exports
为什么?变化通常都是需要避免,特别是当你要输出可变的绑定。虽然在某些场景下可能需要这种技术,但总的来说应该导出常量。
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };import/prefer-default-export
在一个单一导出模块里,用 export default 更好。eslint: import/prefer-default-export
为什么?鼓励使用更多文件,每个文件只导出一次,这样可读性和可维护性更好。
// bad
export function foo() {}
// good
export default function foo() {}import/first
把 import 放在其他所有语句之前。eslint: import/first
为什么?因为
import会被提升到代码最前面运行,因此将他们放在最前面以防止发生意外行为。
// bad
import foo from "foo";
foo.init();
import bar from "bar";
// good
import foo from "foo";
import bar from "bar";
foo.init();属性
dot-notation
访问属性时使用点符号。eslint: dot-notation
const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke["jedi"];
// good
const isJedi = luke.jedi;其他
no-unneeded-ternary
避免不必要的三元表达式。eslint rules: no-unneeded-ternary
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
const quux = a != null ? a : b;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;
const quux = a ?? b;brace-style
if 表达式的 else和 if 的右大括号在一行。eslint: brace-style
// bad
if (test) {
thing1();
thing2();
} else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}no-multiple-empty-lines
不要在代码之间使用多个空白行填充。eslint: no-multiple-empty-lines
// bad
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = this.getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
// good
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}max-len
避免一行代码超过 100 个字符(包含空格)。 eslint: max-len
为什么?这样确保可读性和可维护性。
// 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;
// better
const foo = 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."));命名规范
id-length
避免用一个字母命名,让你的命名有意义。eslint: id-length
// bad
function q() {
// ...
}
// good
function query() {
// ...
}camelcase
用小驼峰命名法来命名你的对象、函数、实例。eslint: camelcase
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}