前端代码规范
以下内容来源于airbnb,竹春翻译
数据类型
-
1.1 基本数据类型使用得时候直接进行赋值。
string
number
boolean
null
undefined
symbol
const foo = 1;
let bar = foo;
bar = 9;
conso.log(foo, bar); // => 1, 9
Symbol的支持性并不是很好,所以应当在支持他们的浏览器中使用。
-
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
声明来代替var
。
这样可以避免重复声明产生一些不可预知的错误。
const a = 1;
const b = 2;
- 2.2 如果必须重复声明,请使用
let
代替var
。
let
属于块级作用域比函数作用域中的var
更加合适。
let count = 1;
if (true) {
count += 1;
}
-
2.3
let
和const
都有自己的作用域。{
let a = 1;
const b = 1;
}
console.log(a); //ReferenceError 引用抛错
对象
-
3.1 使用字面量方式而非创建对象。
const item = {};
-
3.2 在创建具有动态属性名对象的时候应当在创建的时候赋予动态属性。
这样可以保证你在一个地方定义对象所有的属性。
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
}
-
3.3 对象的方法使用简写。
const atom = {
value: 1,addValue(value) { return atom.value + value; }
}
-
3.4 属性值使用简写。
简短且描述性更强。
const lukeSkywalker = 'Luke Skywalker';
const obj = {
lukeSkywalker,
}
- 3.5 在对象声明的时候应当将简写的属性放在最前。
为了更容易分辨哪一个是简写。
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkInt: 2,
three: 3,
}
- 3.6 只给非法的属性添加标示符。
通常是为了提高可读性。带有标示符之后高亮显示,提高JS性能。
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
}
- 3.7 不要直接使用
Object.prototype
(对象原型)方法,比如hasOwnProperty
,propertyIsEnumerable
,和isPrototypeOf
。
因为这些方法可能对对象产生影响。
const has = Object.prototype.hasOwnProperty; // in module scope.
import has from 'has'
console.log(has.call(object, key));
-
3.8 使用对象扩展符来代替
Object.assign
进行浅拷贝,同时可以省略不需要的属性。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 使用字面语法创建数组。
const items = [];
-
4.2 在数组中添加项请使用数组的
push
方法而非直接操作数组。const someStack = [];
smeStack.push(‘one’); -
4.3 拷贝数组请使用
...
。const items = [ 1, 2, 3 ];
const itemsCopy = […items]; // …只能进行浅拷贝 -
4.4 转换一个可迭代的对象请使用
...
而不是Array.from
。const foo = document.querySelectorAll(’.foo’);
const nodes = […foo]; -
4.5 转换类似于数组的对象请使用
Array.from
。const arrLike = { 0: ‘foo’, 1: ‘bar’ };
const arr = Array.from(arrLike); -
4.6 请使用
Array.from
代替...
进行操作,它避免了创建中间数组。const baz = Array.from(foo, bar); // 相当于 […foo].map(bar);
-
4.7 数组的回调方法请书写返回值,如果函数体是由一条表达式组成且无副作用可以省略。
// good
[1, 2, 3].map(x => x + 1);
// good
[[0, 1], [2, 3]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[index] = flatten;
return flatten;
})
// 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;
}); -
4.8 如果数组有多行,则在打开数组括号后和关闭数组括号前使用换行符。
// good
const arr = [[0, 1], [2, 3], [4, 5]];
const objectInArray = [
{
id: 1,
},
{
id: 2,
},
];
const numberInArray = [
1,
2,
];
结构赋值
- 5.1 在访问和使用对象的多个属性时可以使用结构赋值。
结构赋值可以避免创建临时性的引用。
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
-
5.2 数组使用结构赋值。
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr; -
5.3 多个返回值使用对象返回,而非数组。
为了在未来添加属性的时候不需要为了改变数组顺序而调整代码。
// bad
function processInput(input) {
return [left, right, top, bottom];
}
// good
function processInput(input) {
return { left, right, top, bottom };
}
const { left, top } = processInput(input);
字符串
-
6.1 字符串应该使用单引号。
// good
const name = ‘Capt. Janeway’; -
6.2 字符串不应该换行。
影响代码美观和不利于搜索。
// good
const str = 'ddd ddd ddd ddd ddd ddd ddd ddd ddd ddd';
- 6.3 当需要编译字符串的时候请使用字符串模板代替字符拼接。
字符串模板提供了更加简洁的语法,换行与插值。
// bad
function sayHi(name) {
return `How are you,${ name }?`;
}
// good
function sayHi(name) {
return `how are you,${name}?`;
}
- 6.4 不要给
eval()
传递一个字符串,会导致太多的漏洞。 - 6.5 不必要的字符串不用转义。
反斜杠会影响代码可读性,所以只在他们应该出现的时候使用。
函数
- 7.1 使用命名函数表达式而非声明函数。
函数声明会被预解析,所以很容易在这个函数还没有被赋值之前使用,这会影响代码的可读性和维护性。如果你发现一个函数的定义太大或者太复杂以至于影响到了理解文件的其余部分,可以将其提取到模块中去。不要忘记给函数命名,不管是否能够从局部变量中推断出该名称。这样做可以尽可能的减少错误。
// bad
const foo = function () {
// ...
}
// good
const short = function longUniquMoreDescriptiveLexicalFoo() {
// ...
}
- 7.2 将立即执行函数用圆括号包裹起来。
立即执行函数是一个独立的个体,用括号包裹起来可以清晰的看到调用。在一个到处都是模块的世界,你几乎不需要一个立即执行函数。
(function () {
console.log('Welcome to the Internet.Please follow me');
}())
-
7.3 不要把函数声明在一个功能模块中(
if
,while
,等等)。函数可以赋值给变量。浏览器允许你这样做,但是各种浏览器解析出来的结果都是不同的,所以这并不是一个好消息。 -
7.4 ECMA-262将一个代码块定义为一个语句列表,函数表达式不是语句列表。
// 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 不要使用
arguments
,使用...
代替。
因为
...
可以明确得到需要的参数,另外通过该语法得到的是真正的数组而非类数组。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
-
7.7 给参数设置默认值而不是改变函数的参数。
// bad
function handleThings(opts) {
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 永远不要用构造函数去创建一个新的函数。
以这种方式创建函数其中的某些参数类似于
eval()
,将会产生漏洞。
// bad
var add = new Function('a', 'b', 'return a + b');
// still bad
var subtract = Function('a', 'b', 'return a - b');
- 7.11 函数命名之前记得加空格。
当不需要一个函数命名或需要删除一个函数名字的时候仍然能够保持一致性。
// bad
const f = function(){}
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};
- 7.12 不要改变参数。
对参数的改变很可能造成对原始数据的改变,造成不必要的麻烦。
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj,'key')?obj.key:1;
}
- 7.13 永远不要给参数重新赋值。
重新给参数赋值可能会导致一些意想不到的结果,当访问
arguments
的时候。而且可能会影响性能,尤其是在v8引擎下。
// 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 使用
...
来代替函数改变this指向。
因为他相当简洁,不需要别的上下文,也不需要使用新的
apply
。
// bad
const x= [1, 2, 3, 4, 5];
console.log.apply(console, x);
// good
const x = [1, 2, 3, 4, 5];
console.log(...x);
// good
new Date(...[2016, 8, 5]);
箭头函数
- 8.1 当必须使用匿名函数的时候请使用箭头函数。
箭头函数创建了单独的作用域,语法上更加简洁。
// 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 如果一个函数体由一条语句组成且这条语句返回一个没有副作用的表达式,可以将大括号省略进行隐式返回,反之,保留大括号并使用
return
语句进行返回。
提高代码可读性。
// 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 + 1}.`);
- 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 如果一个函数只有一个参数且省略了函数体的大括号,那么参数的圆括号也省略。否则,请不要省略任何参数,保持清晰和一致性。注意:小括号不省略,也是可以的。
为了代码看起来更清晰。
// 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 避免混淆箭头函数
=>
和比较操作符(<=
,>=
)。// 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;
}; -
8.6 使用隐式返回的情况下请强调函数体的位置。
// bad
foo =>
// bar;
foo =>
(bar);
// good
foo => bar;
foo => (bar);
foo => (
bar
)
类和构造函数
- 9.1 尽可能多的使用
class
,避免直接操作原型。
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
代替继承。
这是一种内置的方法,可以在不破坏
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() {
returnJedi - ${this.getName()}
;
}
} -
9.5 如果没有指定默认构造函数,则类具有默认构造函数。空构造函数或只委托给父类的构造函数是不必要的。
// 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 避免重复的类。
重复的类成员声明会默默地选择最后一个——几乎可以肯定,重复是一个错误。
// 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 不要使用通配符导出。
除非确认只有一个默认导出。
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
- 10.3 不要直接从导入导出。
尽管一行代码很简洁,但是有一种清晰的导入和导出方式可以使事情保持一致。
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
- 10.4 如果需要同一个路径的多个方法,请从一个路径导出。
便于代码维护。
// bad
import foo from 'foo';
// … some other imports … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// good
import foo, {
named1,
named2,
} from 'foo';
- 10.5 不要导出变量。
一般情况下应该只导出常量,避免对其他造成影响。
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
- 10.6 如果只导出一个方法,请使用默认值。
只导出一个方法是利于可读性和可维护性的,也是值得提倡的。
// bad
export function foo() {}
// good
export default function foo() {}
- 10.7 将所有导入置顶。
都放在顶部可以防止意外情况。
// 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 模块语法中不允许webpack加载语法。
和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';
迭代器和Generators
- 11.1 不要使用迭代器。js中有更好的高阶函数可以代替
for in ,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;
// good --> 不改变原数组 如果都符合条件 返回true 有一个不符合就返回false
let arr = [14, 15, 16];
arr.every(item=>{
return item>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 如果你必须使用生成器或忽略我们的建议,请使用正确的标示。
因为
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 访问属性时使用点符号。
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 计算指数时使用指数运算符
**
。// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
变量
-
13.1 使用
const
或let
来声明变量。不这样做会导致全局变量,我们要尽可能避免全局污染。// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower(); -
13.2 对每个变量的声明使用单独的
const
或let
。
通过这种方式添加新的变量声明更容易,而且您永远不必担心替换掉一个
;
或,
或者引入只使用标点的困难。您还可以使用调试器单步执行每个声明,而不是一次跳过所有声明。
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
// (compare to above, and try to spot the mistake)
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 - unnecessary function call
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 不要使用链式变量赋值。
链式变量赋值会创建隐式全局变量。
// bad
(function example() {
// JavaScript interprets this as
// let a = ( b = ( c = 1 ) );
// The let keyword only applies to variable a; variables b and c become
// global variables.
let a = b = c = 1;
}());
console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
- 13.6 避免使用一元递增和递减(
++
,--
)。
根据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;
- 13.7 在赋值
=
之前或之后避免换行符。如果你的赋值违反了max-len
,就用()
把它括起来。
围绕
=
的换行符会混淆赋值的值。
// bad
const foo =
superLongLongLongLongLongLongLongLongFunctionName();
// bad
const foo
= 'superLongLongLongLongLongLongLongLongString';
// good
const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
// good
const foo = 'superLongLongLongLongLongLongLongLongString';
- 13.8 不允许声明未使用的变量。
声明了但在代码中没有使用的变量很可能是由于未完成重构而导致的错误。这样的变量会占用代码中的空间,并可能导致读者的混淆。
// bad
var some_unused_var = 42;
// 只写的变量不视为已使用。
var y = 10;
y = 5;
// 修改自身的操作不视为已使用
var z = 0;
z = z + 1;
// 未使用的函数参数
function getX(x, y) {
return x;
}
// good
function getXPlusY(x, y) {
return x + y;
}
var x = 1;
var y = a + 2;
alert(getXPlusY(x, y));
// 'type'未使用会被忽略,因为它有一个默认属性值。
// 这是一种提取\删除对象中指定键的形式。
var { type, ...coords } = data;
// 'coords'现在是没有type属性的data
提升
-
14.1
var
声明被提升到其最接近的封闭函数范围的顶部,它们的赋值则不会。const和let声明有了一个新的概念,称为‘暂时性死区’(TDZ)。这意味着typeof
不再是一个百分之百安全的操作。// 我们知道这样是行不通的,没有定义全局变量
function example() {
console.log(notDefined); // => throws a ReferenceError
}
// 在变量声明之前获取变量是可行的,由于变量提升的原因。但赋值并没有提升
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
//
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
//
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
} -
14.2 匿名函数可以给变量赋值,但不能使用函数声明。
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () {
console.log(‘anonymous function expression’);
};
} -
14.3 指定的函数表达式使用变量名,而不是函数体的函数名。
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
superPower(); // => ReferenceError superPower is not defined
var named = function superPower() {
console.log(‘Flying’);
};
}
// the same is true when the function name
// is the same as the variable name.
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() {
console.log(‘named’);
};
} -
14.4 函数声明会提升函数名和函数体。
function example() {
superPower(); // => Flying
function superPower() {
console.log(‘Flying’);
}
}
比较运算符和等式
-
15.1 使用
===
和!==
代替==
和!=
。 -
15.2 如果使用条件语句,例如
if
语句来抽象判断表达式的值,必须遵循以下尽量简单的原则:- Object的值为true
- Undefined值为false
- Null值为false
- Booleans值为布尔值
- 数字+0,-0,和NaN值为false,其他值为true
- 字符串
''
值为false,其他值为true
if ([0] && []) {
// true
// 空数组也是对象 对象值为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 更多信息请参考《Truth Equality and JavaScript》。
-
15.5 使用大括号创建包含词法声明的case和default子句(例如,let、const、function和class)。
为什么?声明语法在整个
switch
块中都是可见的,但只有在分配时才初始化,只有在满足条件时才会发生。当多个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 三目运算符不应该嵌套,通常是单行表达式。
// bad
const foo = maybe1 > maybe2
? “bar”
: value1 > value2 ? “baz” : null;
// split into 2 separated ternary expressions
const maybeNull = value1 > value2 ? ‘baz’ : null;
// better
const foo = maybe1 > maybe2
? ‘bar’
: maybeNull;
// best
const foo = maybe1 > maybe2 ? ‘bar’ : maybeNull; -
15.7 避免不必要的三目运算。
// 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 多个运算符混合使用用小括号将其包裹。唯一的例外是标准算术运算符:
+
、-
和**
,因为它们的优先级得到了广泛的理解。建议将/
和*
放在括号中,因为当它们混合在一起时,它们的优先级可能是不明确的。
能够提高可读性和清楚的表达开发人员的意图。
// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad
const bar = a ** b - 5 % d;
// bad
// one may be confused into thinking (a || b) && c
if (a || b && c) {
return d;
}
// bad
const bar = a + b / c * 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 对多行代码块用大括号包裹。
// 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
块的右括号相同的行上。// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
} -
16.3 如果一个
if
块总是执行一个return
语句,那么后面的else
块就没有必要了。在包含返回的if
块之后的else if
块中的返回可以分成多个if
块。// 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;
}
}
控制语句
- 17.1 如果控制语句(
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();
}
-
17.2 不要使用选择操作符来代替控制语句。
// bad
!isRunning && startRunning();
// good
if (!isRunning) {
startRunning();
}
注释
-
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 以一个空格开始所有的注释,使它更容易阅读。
// 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; }
}
空格
-
19.1 使用软制表符(空格字符)设置为2个空格。
// bad
function foo() {
∙∙∙∙let name;
}// bad
function bar() {
∙let name;
}// good
function baz() {
∙∙let name;
} -
19.2 在大括号前放置1个空间。
// 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等)。在函数调用和声明中,参数列表和函数名之间不应有空格。
// bad
if(isJedi) {
fight ();
}// good
if (isJedi) {
fight();
}// bad
function fight () {
console.log (‘Swooosh!’);
}// good
function fight() {
console.log(‘Swooosh!’);
} -
19.4 用空格分隔操作符。
// bad
const x=y+5;// good
const x = y + 5; -
19.5 用一个换行符结束文件。
// 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个方法链)。使用一个前导点,它强调该行是一个方法调用,而不是一个新语句。
// 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 在块之后和下一个语句之前留下空行。
// 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 不要用空行填充块。
// 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 不要使用多个空行来填充代码。
// 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) { // .. }
}
-
19.10 不要在小括号内添加空格。
// 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.11 不要在中括号内添加空格。
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);// good
const foo = [1, 2, 3];
console.log(foo[0]); -
19.12 在花括号内添加空格。
// bad
const foo = {clark: ‘kent’};// good
const foo = { clark: ‘kent’ }; -
19.13 避免代码行超过100个字符(包括空格)。注意:以上所述,长字符串不受此规则约束,不应被拆分。
确保代码的可读性和可维护性。
// 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.'));
-
19.14 要求打开的块与同一行上的下一个括号之间的间距一致。此规则还强制在关闭括号和前一个括号内的同一行上保持一致的间距。
// bad
function foo() {return true;}
if (foo) { bar = 0;}// good
function foo() { return true; }
if (foo) { bar = 0; } -
19.15 不要在逗号前加空格,而要在逗号后加空格。
// bad
var foo = 1,bar = 2;
var arr = [1 , 2];// good
var foo = 1, bar = 2;
var arr = [1, 2]; -
19.16 强制计算属性方括号内的间距。
// bad
obj[foo ]
obj[ ‘foo’]
var x = {[ b ]: a}
obj[foo[ bar ]]// good
obj[foo]
obj[‘foo’]
var x = { [b]: a }
obj[foo[bar]] -
19.17 避免在函数及其调用之间使用空格。
// bad
func ();func
();// good
func(); -
19.18 在对象属性中强制键和值之间的间距。
// bad
var obj = { “foo” : 42 };
var obj2 = { “foo”:42 };// good
var obj = { “foo”: 42 }; -
19.19 避免在行的末尾使用尾随空格。
-
19.20 避免多个空行,只允许在文件末尾有一个换行符,并避免在文件开头有一个换行符。
// bad - multiple empty lines
var x = 1;var y = 2;
// bad - 2+ newlines at end of file
var x = 1;
var y = 2;// bad - 1+ newline(s) at beginning of file
var x = 1;
var y = 2;// good
var x = 1;
var y = 2;
逗号
-
20.1 不允许逗号在行首。
// 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 行尾可以附加逗号。
会导致更干净的git差异。而且,像Babel这样的转置器将在转置代码中删除额外的尾随逗号,这意味着您不必担心遗留浏览器中的尾随逗号问题。
// bad - git diff without trailing comma
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing']
};
// good - git diff with trailing comma
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 (note that a comma must not appear after a "rest" element)
function createHero(
firstName,
lastName,
inventorOf,
...heroArgs
) {
// does nothing
}
// bad
createHero(
firstName,
lastName,
inventorOf
);
// good
createHero(
firstName,
lastName,
inventorOf,
);
// good (note that a comma must not appear after a "rest" element)
createHero(
firstName,
lastName,
inventorOf,
...heroArgs
);
分号
- 21.1 行尾加分号。
为什么?当JavaScript遇到换行符没有分号,它使用的一组规则称为自动分号来确定是否应将换行符结束的一份声明中,和(顾名思义)分号之前到你的代码换行是否这么认为。ASI包含一些古怪的行为,但是,如果JavaScript错误地解释了您的换行符,您的代码将会中断。随着新特性成为JavaScript的一部分,这些规则将变得更加复杂。显式地结束您的语句并加分号结尾。
// bad - raises exception
const luke = {}
const leia = {}
[luke, leia].forEach((jedi) => jedi.father = 'vader')
// bad - raises exception
const reaction = "No! That’s impossible!"
(async function meanwhileOnTheFalcon() {
// handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
// ...
}())
// bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI!
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 字符串转换。
// => this.reviewScore = 9;
// bad
const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
// bad
const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
// bad
const totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string
// good
const totalScore = String(this.reviewScore);
-
22.3 数字:使用
Number
进行类型转换,parseInt
始终使用基数来解析字符串。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是你的瓶颈,需要使用Bitshift的性能原因,留下一个注释解释为什么和你正在做什么。
// 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 注意:使用bitshift操作时要小心。数字表示为64位的值,但是位移位操作总是返回32位的整数(源)。位移位会导致大于32位的整数值出现意外行为。讨论。最大32位Int是2,147,483,647:
2147483647 >> 0; // => 2147483647
2147483648 >> 0; // => -2147483648
2147483649 >> 0; // => -2147483647 -
22.6 布尔值。
const age = 0;
// bad
const hasAge = new Boolean(age);// good
const hasAge = Boolean(age);// best
const hasAge = !!age;
命名规范
-
23.1 避免使用单个字母的名字。描述你的命名。
// bad
function q() {
// …
}// good
function query() {
// …
} -
23.2 在命名对象、函数和实例时使用驼峰命名。
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}// good
const thisIsMyObject = {};
function thisIsMyFunction() {} -
23.3 仅在命名构造函数或类时使用首字母大写。
// 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 不要在首尾使用下划线。
为什么?JavaScript在属性或方法方面没有隐私的概念。尽管前导下划线通常表示“私有”,但实际上,这些属性是完全公共的,因此也是公共API契约的一部分。这种约定可能导致开发人员错误地认为更改不会被视为破坏,或者不需要测试。如果你想要某样东西是“私人的”,它一定不能明显地存在。
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
this._firstName = 'Panda';
// good
this.firstName = 'Panda';
// good, in environments where WeakMaps are available
// see https://kangax.github.io/compat-table/es6/#test-WeakMap
const firstNames = new WeakMap();
firstNames.set(this, 'Panda');
-
23.5 不要保存对它的引用。使用箭头函数或函数的bind方法。
// 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 基本文件名应该与默认导出的名称完全匹配。
// 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’; // PascalCase import/export, camelCase filename
import FortyTwo from ‘./FortyTwo’; // PascalCase import/filename, camelCase export
import InsideDirectory from ‘./InsideDirectory’; // PascalCase import/filename, camelCase export// bad
import CheckBox from ‘./check_box’; // PascalCase import/export, snake_case filename
import forty_two from ‘./forty_two’; // snake_case import/filename, camelCase export
import inside_directory from ‘./inside_directory’; // snake_case import, camelCase export
import index from ‘./inside_directory/index’; // requiring the index file explicitly
import insideDirectory from ‘./insideDirectory/index’; // requiring the index file explicitly// good
import CheckBox from ‘./CheckBox’; // PascalCase export/import/filename
import fortyTwo from ‘./fortyTwo’; // camelCase export/import/filename
import insideDirectory from ‘./insideDirectory’; // camelCase export/import/directory name/implicit “index”
// ^ supports both insideDirectory.js and insideDirectory/index.js -
23.7 在导出默认函数时使用驼峰命名。您的文件名应该与函数名相同。
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 = [
// ...
];
- 23.10 只有在导出常量(1)、常量(2)是常量(不能重新赋值)、常量(3)是程序员可以信任常量(及其嵌套属性)不会改变的情况下,才可以选择大写常量。
这是一种额外的工具,用于在程序员不确定某个变量是否会更改的情况下提供帮助。UPPERCASE_VARIABLES让程序员知道他们可以相信变量(及其属性)不会改变。
-
那么所有的const变量呢?-这是不必要的,所以大写不应该用于文件中的常量。但是,它应该用于导出的常量。
-
导出的对象呢?-导出顶层的大写(例如:
EXPORTED_OBJECT.key
),并保持所有嵌套属性不变。// bad
const PRIVATE_VARIABLE = ‘should not be unnecessarily uppercased within a file’;// bad
export const THING_TO_BE_CHANGED = ‘should obviously not be uppercased’;// bad
export let REASSIGNABLE_VARIABLE = ‘do not use let with uppercase variables’;// —
// allowed but does not supply semantic value
export const apiKey = ‘SOMEKEY’;// better in most cases
export const API_KEY = ‘SOMEKEY’;// —
// bad - unnecessarily uppercases key while adding no semantic value
export const MAPPING = {
KEY: ‘value’
};// good
export const MAPPING = {
key: ‘value’
};
属性访问
-
24.1 属性的访问函数不是必需的。
-
24.2 不要使用JavaScript的getter /setter方法,因为它们会产生意想不到的副作用,而且很难测试、维护和推理。相反,如果您确实使用了存取函数,那么使用
getVal()
和setVal('hello')
。// bad
class Dragon {
get age() {
// …
}set age(value) { // ... }
}
// good
class Dragon {
getAge() {
// …
}setAge(value) { // ... }
}
-
24.3 如果属性/方法是布尔值,可以使用
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]; }
}
事件
-
25.1 当将数据有效负载附加到事件(无论是DOM事件还是更专有的东西,如主干事件)时,传递一个对象文字(也称为“散列”),而不是原始值。这允许后续参与者向事件有效负载添加更多数据,而无需查找和更新事件的每个处理程序。例如,
// bad
$(this).trigger(‘listingUpdated’, listing.id);// …
$(this).on(‘listingUpdated’, (e, listingID) => {
// do something with listingID
});
替换为:// good
$(this).trigger(‘listingUpdated’, { listingID: listing.id });// …
$(this).on(‘listingUpdated’, (e, data) => {
// do something with data.listingID
});
jQuery
-
26.1 用$作为jQuery对象变量的前缀。
// 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 使用
find
和限定范围的jQuery对象查询。// bad
$(‘ul’, ‘.sidebar’).hide();// bad
$(’.sidebar’).find(‘ul’).hide();// good
$(’.sidebar ul’).hide();// good
$(’.sidebar > ul’).hide();// good
$sidebar.find(‘ul’).hide();
ES5兼容性
- 27.1 参考Kangax的ES5兼容性表。
ES5(ES 2015+)新特性
- 28.1 不要使用尚未达到第三阶段的TC39提案。
为什么?这些文件尚未最后定稿,它们可能会改变或全部撤回。我们想使用JavaScript,而提案还不是JavaScript。
标准程序库
标准库包含一些实用程序,它们在功能上被破坏了,但由于遗留问题仍然存在。
- 29.1 使用
Number.isNaN
而不是全局的isNaN
。
全局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
。
全局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 必须认真:
- 无论您使用哪种测试框架,您都应该编写测试!
- 努力编写许多小的纯函数,尽量减少发生突变的地方。
- 小心静态方式和模拟数据——它们会使您的测试更加脆弱。
- 我们在Airbnb上主要用
mocha
和jest
。tape
有时也用于小的、单独的模块。 - 100%的测试覆盖率是一个值得争取的好目标,即使实现它并不总是实际的。
- 当您修复一个bug时,编写一个回归测试。如果不进行回归测试,修复的bug几乎肯定会在将来再次崩溃。