1.1 使用let,const 命名变量
- const 防止变量被篡改,let和const有块级作用域
2.1 使用字面量创建对象 使用对象方法的缩写
const newObj = {}
const obj = {
fun () {
// js
}
}
复制代码
2.2 使用对象属性的缩写
const prop = 'change js'
const obj = {
prop,
}
复制代码
2.3 在对象声明的时候将简写的属性进行分组
const prop = 'change js'
const props = 'change us'
const obj = {
prop, // 写在一起
props,
a:1,
b:2
}
复制代码
2.4 不能直接调用 Object.prototype
的方法,如: hasOwnProperty
、 propertyIsEnumerable
和 isPrototypeOf
const has = Object.prototype.hasOwnProperty; // 在模块范围内的缓存中查找一次
复制代码
2.5 更喜欢对象扩展操作符,而不是用 [Object.assign
]浅拷贝一个对象。 使用对象的 rest 操作符来获得一个具有某些属性的新对象。
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 }
复制代码
3.1 字面语法创建数组 使用 [Array#push]取代直接赋值来给数组添加项
const items = [];
items.push(1)
复制代码
3.2 使用数组展开方法 ...
来拷贝数组
const items = [1, 2, 3];
const itemsCopy = [...items];
复制代码
3.3 将一个类数组对象转换成一个数组, 使用展开方法 ...
代替 [Array.from
]
const foo = document.querySelectorAll('.foo');
const nodes = [...foo];
复制代码
3.4 对于对迭代器的映射,使用 [Array.from]替代展开方法 ...
, 因为它避免了创建中间数组
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
复制代码
3.5 在数组回调方法中使用 return 语句。 如果函数体由一个返回无副作用的表达式的单个语句组成,那么可以省略返回值
[1, 2, 3].map(x => x + 1);
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[index] = flatten;
return flatten;
});
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
}
return false;
});
复制代码
3.6 如果数组有多行,则在开始的时候换行,然后在结束的时候换行。
const arr = [[0, 1], [2, 3], [4, 5]];
const objectInArray = [
{
id: 1,
},
{
id: 2,
},
];
const numberInArray = [
1,
2,
];
复制代码
4.1 在访问和使用对象的多个属性的时候使用对象的解构
function getFullName({ firstName, lastName }) { // 先解构
return `${firstName} ${lastName}`;
}
复制代码
4.2 使用数组解构
const [first, second] = arr;
复制代码
4.3 对于多个返回值使用对象解构,而不是数组解构。
// good
function processInput(input) {
// 处理代码...
return { left, right, top, bottom };
}
// 调用者只选择他们需要的数据。 而不需要关注顺序
const { left, top } = processInput(input);
复制代码
5.1 使用单引号 ''
定义字符串
const name = 'Capt. Janeway';
复制代码
5.2 使行超过100个字符的字符串不应使用字符串连接跨多行写入
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
复制代码
5.3 当以编程模式构建字符串时,使用字符串模板代替字符串拼接
function sayHi(name) {
return `How are you, ${name}?`;
}
复制代码
5.4 不要在字符串上使用 eval()
,它打开了太多漏洞
5.5 不要转义字符串中不必要的字符
// bad
const foo = ''this' \i\s "quoted"';
// good
const foo = ''this' is "quoted"';
const foo = `my name is '${name}'`;
复制代码
6.1 使用命名的函数表达式代替函数声明
为什么? 函数声明是挂起的,这意味着在它在文件中定义之前,很容易引用函数。这会损害可读性和可维护性。如果您发现函数的定义是大的或复杂的,以至于它干扰了对文件的其余部分的理解,那么也许是时候将它提取到它自己的模块中了!不要忘记显式地命名这个表达式,不管它的名称是否从包含变量(在现代浏览器中经常是这样,或者在使用诸如Babel之类的编译器时)。这消除了对错误的调用堆栈的任何假设
// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
// 从变量引用调用中区分的词汇名称
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
复制代码
6.2 Wrap立即调用函数表达式 IIFE
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
复制代码
6.3 切记不要在非功能块中声明函数 (if
, while
, 等)。 将函数赋值给变量。 浏览器允许你这样做,但是他们都有不同的解释,这是个坏消息
6.4 注意: ECMA-262 将 block
定义为语句列表。 函数声明不是语句
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
复制代码
6.5 永远不要定义一个参数为 arguments
。 这将会优先于每个函数给定范围的 arguments
对象
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
复制代码
6.6 不要使用 arguments
, 选择使用 rest 语法 ...
代替
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
复制代码
6.7 使用默认的参数语法,而不是改变函数参数
function handleThings(opts = {}) {
// ...
}
复制代码
6.8 总是把默认参数放在最后
function handleThings(name, opts = {}) {
// ...
}
复制代码
6.9 函数签名中的间距
// good
const x = function () {};
const y = function a() {};
复制代码
6.10 用变异参数
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
复制代码
6.11 要再赋值参数
// good
function f3(a) {
const b = a || 1;
// ...
}
function f4(a = 1) {
// ...
}
复制代码
6.12 优先使用扩展运算符 ...
来调用可变参数函数
// good
const x = [1, 2, 3, 4, 5];
console.log(...x);
new Date(...[2016, 8, 5]);
复制代码
6.13 具有多行签名或者调用的函数应该像本指南中的其他多行列表一样缩进:在一行上只有一个条目,并且每个条目最后加上逗号
function foo(
bar,
baz,
quux,
) {
// ...
}
console.log(
foo,
bar,
baz,
);
复制代码
7.1 你必须使用匿名函数时 (当传递内联函数时), 使用箭头函数
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
复制代码
7.2 如果函数体包含一个单独的语句,返回一个没有副作用的 [expression] , 省略括号并使用隐式返回。否则,保留括号并使用 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}.`);
// 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,
}));
// 没有副作用的隐式返回
function foo(callback) {
const val = callback();
if (val === true) {
// 如果回调返回 true 执行
}
}
let bool = false;
// bad
foo(() => bool = true);
// good
foo(() => {
bool = true;
});
复制代码
7.3 如果表达式跨越多个行,用括号将其括起来,以获得更好的可读性
// good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
复制代码
7.4 如果你的函数接收一个参数,则可以不用括号,省略括号。 否则,为了保证清晰和一致性,需要在参数周围加上括号。 注意:总是使用括号是可以接受的,在这种情况下,我们使用 [“always” option]来配置 eslint. eslint: [arrow-parens
]
[1, 2, 3].map(x => x * x);
复制代码
7.4 避免箭头函数符号 (=>
) 和比较运算符 (<=
, >=
) 的混淆
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
复制代码
8.1 使用 class
. 避免直接操作 prototype
// 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;
}
}
复制代码
8.1 使用 extends
来扩展继承。
为什么? 它是一个内置的方法,可以在不破坏
instanceof
的情况下继承原型功能。
// good
class PeekableQueue extends Queue {
peek() {
return this.queue[0];
}
}
复制代码
8.2 如果没有指定类,则类具有默认的构造器。 一个空的构造器或是一个代表父类的函数是没有必要的。
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
复制代码
9.1 你可能经常使用模块 (import
/export
) 在一些非标准模块的系统上。 你也可以在你喜欢的模块系统上相互转换。
为什么? 模块是未来的趋势,让我们拥抱未来。
import { es6 } from './AirbnbStyleGuide';
export default es6;
复制代码
9.2 不要使用通配符导入。
为什么? 这确定你有一个单独的默认导出。
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
复制代码
9.3 不要直接从导入导出。
为什么? 虽然写在一行很简洁,但是有一个明确的导入和一个明确的导出能够保证一致性。
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
复制代码
9.4 只从一个路径导入所有需要的东西
// bad
import foo from 'foo';
// … 其他导入 … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// good
import foo, {
named1,
named2,
} from 'foo';
复制代码
9.5 不要导出可变的引用
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
复制代码
9.6 单个导出的模块中,选择默认模块而不是指定的导出。
为什么? 为了鼓励更多的文件只导出一件东西,这样可读性和可维护性更好。
9.7 将所有的 import
s 语句放在所有非导入语句的上边。 eslint: [import/first
]
为什么? 由于所有的
import
s 都被提前,保持他们在顶部是为了防止意外发生。
9.8 多行导入应该像多行数组和对象一样缩进。
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
复制代码
9.9 在模块导入语句中禁止使用 Webpack 加载器语法。 eslint: [import/no-webpack-loader-syntax
]
为什么? 因为在导入语句中使用 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';
复制代码
9.10计算指数时,可以使用 **
运算符
const binary = 2 ** 10;
复制代码
9.11不要链式变量赋值。
为什么? 链式变量赋值会创建隐式全局变量。
9.12避免使用不必要的递增和递减 (++
, --
)。 改使用 num += 1
9.13避免在赋值语句 =
前后换行。如果你的代码违反了 [max-len
], 使用括号包裹。
// good
const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
复制代码
比较运算符和等号
10.1 使用 ===
和 !==
而不是 ==
和 !=
10.2 条件语句,例如 if
语句使用 ToBoolean
的抽象方法来计算表达式的结果,并始终遵循以下简单的规则:
- Objects 的取值为: true
- Undefined 的取值为: false
- Null 的取值为: false
- Booleans 的取值为: 布尔值的取值
- Numbers 的取值为:如果为 +0, -0, or NaN 值为 false 否则为 true
- Strings 的取值为: 如果是一个空字符串
''
值为 false 否则为 true
10.3 对于布尔值使用简写,但是对于字符串和数字进行显式比较。
// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}
复制代码
10.4 在 case
和 default
的子句中,如果存在声明 (例如. let
, const
, function
, 和 class
),使用大括号来创建块 。
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 {}
}
}
复制代码
10.5 三目表达式不应该嵌套,通常是单行表达式
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// 分离为两个三目表达式
const maybeNull = value1 > value2 ? 'baz' : null;
// better
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
复制代码
10.6 避免不必要的三目表达式
// 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;
复制代码
10.7 使用该混合运算符时,使用括号括起来。 唯一例外的是标准算数运算符 (+
, -
, *
, & /
) 因为他们的优先级被广泛理解
// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad
const bar = a ** b - 5 % d;
// bad
// 可能陷入一种 (a || b) && c 的思考
if (a || b && c) {
return d;
}
// good
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
// good
const bar = (a ** b) - (5 % d);
// good
if (a || (b && c)) {
return d;
}
// good
const bar = a + b / c * d;
复制代码
块
11.1 当有多行代码块的时候,使用大括号包裹
if (test) return false;
function bar() {
return false;
}
复制代码
11.2 如果你使用的是 if
和 else
的多行代码块,则将 else
语句放在 if
块闭括号同一行的位置
if (test) {
thing1();
} else {
thing2();
}
复制代码
11.3 如果一个 if
块总是执行一个 return
语句,那么接下来的 else
块就没有必要了。 如果一个包含 return
语句的 else if
块,在一个包含了 return
语句的 if
块之后,那么可以拆成多个 if
块
// 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;
}
}
复制代码
控制语句
12.1 如果你的控制语句 (if
, while
等) 太长或者超过了一行最大长度的限制,则可以将每个条件(或组)放入一个新的行。 逻辑运算符应该在行的开始
// good
if (
foo === 123
&& bar === 'abc'
) {
thing1();
}
// good
if (
(foo === 123 || bar === 'abc')
&& doesItLookGoodWhenItBecomesThatLong()
&& isThisReallyHappening()
) {
thing1();
}
// good
if (foo === 123 && bar === 'abc') {
thing1();
}
复制代码
12.2 不要使用选择操作符代替控制语句
// bad
!isRunning && startRunning();
// good
if (!isRunning) {
startRunning();
}
复制代码
注释
用一个空格开始所有的注释,使它更容易阅读
13.1 使用 /** ... */
来进行多行注释。
// good
/**
* make() returns a new element
* based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
复制代码
13.2 使用 //
进行单行注释。 将单行注释放在需要注释的行的上方新行。 在注释之前放一个空行,除非它在块的第一行。
// good
// is current tab
const active = true;
// 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;
}
复制代码
13.3 使用 FIXME
或者 TODO
开始你的注释可以帮助其他开发人员快速了解,如果你提出了一个需要重新审视的问题,或者你对需要实现的问题提出的解决方案。 这些不同于其他评论,因为他们是可操作的。 这些行为是 FIXME: -- 需要解决这个问题
或者 TODO: -- 需要被实现
13.4 使用 // FIXME:
注释一个问题
class Calculator extends Abacus {
constructor() {
super();
// FIXME: 这里不应该使用全局变量
total = 0;
}
}
复制代码
13.5 使用 // TODO:
注释解决问题的方法。
class Calculator extends Abacus {
constructor() {
super();
// TODO: total 应该由一个 param 的选项配置
this.total = 0;
}
}
复制代码
空白
14.1 使用 tabs (空格字符) 设置为 2 个空格
14.2 在主体前放置一个空格
// 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',
});
复制代码
14.3 在控制语句(if
, while
等)开始括号之前放置一个空格。 在函数调用和是声明中,在参数列表和函数名之间没有空格。
// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
复制代码
14.4 用空格分离操作符
// bad
const x=y+5;
// good
const x = y + 5;
复制代码
14.5 使用单个换行符结束文件
// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵
↵
复制代码
// good
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵
复制代码
14.6 在使用链式方法调用的时候使用缩进(超过两个方法链)。 使用一个引导点,强调该行是方法调用,而不是新的语句。
// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
复制代码
14.7 在块和下一个语句之前留下一空白行
// good
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
复制代码
14.8 不要在块的开头使用空白行。
// bad
class Foo {
constructor(bar) {
this.bar = bar;
}
}
// good
function bar() {
console.log(foo);
}
复制代码
14.9 不要在括号内添加空格
// bad
function bar( foo ) {
return foo;
}
// good
function bar(foo) {
return foo;
}
复制代码
14.10 不要在中括号中添加空格
// good
const foo = [1, 2, 3];
console.log(foo[0]);
复制代码
14.11 在花括号内添加空格。
// good
const foo = { clark: 'kent' };
复制代码
14.12 避免让你的代码行超过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;
复制代码
14.13 要求打开的块标志和同一行上的标志拥有一致的间距。此规则还会在同一行关闭的块标记和前边的标记强制实施一致的间距
// bad
function foo() {return true;}
if (foo) { bar = 0;}
// good
function foo() { return true; }
if (foo) { bar = 0; }
复制代码
14.14 逗号之前避免使用空格,逗号之后需要使用空格。
// good
var foo = 1, bar = 2;
var arr = [1, 2];
复制代码
14.15 在计算属性之间强化间距。
// bad
obj[foo ]
obj[ 'foo']
var x = {[ b ]: a}
obj[foo[ bar ]]
// good
obj[foo]
obj['foo']
var x = { [b]: a }
obj[foo[bar]]
复制代码
14.16 在函数和它的调用之间强化间距
// good
func();
复制代码
14.17 在对象的属性和值之间强化间距。
// bad
var obj = { "foo" : 42 };
var obj2 = { "foo":42 };
// good
var obj = { "foo": 42 };
复制代码
14.18 在行的末尾避免使用空格
14.19 避免多个空行,并且只允许在文件末尾添加一个换行符
添加尾随逗号: 可以
var x = 1;
var y = 2;
// good
var x = 1;
var y = 2;
复制代码
逗号
15.1 逗号前置: 不行
// good
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers',
};
复制代码
分号
对 eslint: semi
为什么? 当 JavaScript 遇见一个没有分号的换行符时,它会使用一个叫做 Automatic Semicolon Insertion 的规则来确定是否应该以换行符视为语句的结束,并且如果认为如此,会在代码中断前插入一个分号到代码中。 但是,ASI 包含了一些奇怪的行为,如果 JavaScript 错误的解释了你的换行符,你的代码将会中断。 随着新特性成为 JavaScript 的一部分,这些规则将变得更加复杂。 明确地终止你的语句,并配置你的 linter 以捕获缺少的分号将有助于防止你遇到的问题。
类型转换和强制类型转换
17.1 在语句开始前进行类型转换。
17.2 字符类型
// 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);
复制代码
17.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);
复制代码
17.4 如果出于某种原因,你正在做一些疯狂的事情,而 parseInt
是你的瓶颈,并且出于 性能问题 需要使用位运算, 请写下注释,说明为什么这样做和你做了什么
**注意: 当你使用位运算的时候要小心。 数字总是被以 64-bit 值 的形式表示,但是位运算总是返回一个 32-bit 的整数 (来源)。 对于大于 32 位的整数值,位运算可能会导致意外行为。讨论。 最大的 32 位整数是: 2,147,483,647。
2147483647 >> 0; // => 2147483647
2147483648 >> 0; // => -2147483648
2147483649 >> 0; // => -2147483647
// good
/**
* parseInt 使我的代码变慢。
* 位运算将一个字符串转换成数字更快。
*/
const val = inputValue >> 0;
复制代码
17.5 布尔类型
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;
复制代码
命名规范
18.1 避免单字母的名字。用你的命名来描述功能。
// bad
function q() {
// ...
}
// good
function query() {
// ...
}
复制代码
18.2 在命名对象、函数和实例时使用驼峰命名法(camelCase)。
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
复制代码
18.3 只有在命名构造器或者类的时候才用帕斯卡拼命名法(PascalCase)
// good
class User {
constructor(options) {
this.name = options.name;
}
}
const good = new User({
name: 'yup',
});
复制代码
18.4 不要使用前置或者后置下划线。 eslint: no-underscore-dangle
为什么? JavaScript 在属性和方法方面没有隐私设置。 虽然前置的下划线是一种常见的惯例,意思是 “private” ,事实上,这些属性时公开的,因此,它们也是你公共 API 的一部分。 这种约定可能导致开发人员错误的认为更改不会被视为中断,或者不需要测试。建议:如果你想要什么东西是 “private” , 那就一定不能有明显的表现
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
this._firstName = 'Panda';
// good
this.firstName = 'Panda';
复制代码
18.5 不要保存 this
的引用。 使用箭头函数或者 函数#bind。
// good
function foo() {
return () => {
console.log(this);
};
}
复制代码
18.6 文件名应该和默认导出的名称完全匹配
18.7 当你导出默认函数时使用驼峰命名法。 你的文件名应该和方法名相同。
function makeStyleGuide() {
// ...
}
export default makeStyleGuide;
复制代码
18.8 当你导出一个构造器 / 类 / 单例 / 函数库 / 暴露的对象时应该使用帕斯卡命名法。
const AirbnbStyleGuide = {
es6: {
},
};
export default AirbnbStyleGuide;
复制代码
18.9 缩略词和缩写都必须是全部大写或者全部小写。
为什么? 名字是为了可读性,不是为了满足计算机算法。
// good
import SMSContainer from './containers/SMSContainer';
// best
import TextMessageContainer from './containers/TextMessageContainer';
// best
const requests = [
// ...
];
复制代码
18.9 你可以大写一个常量,如果它:(1)被导出,(2)使用 const
定义(不能被重新赋值),(3)程序员可以信任它(以及其嵌套的属性)是不变的。
为什么? 这是一个可以帮助程序员确定变量是否会发生变化的辅助工具。UPPERCASE_VARIABLES 可以让程序员知道他们可以相信变量(及其属性)不会改变。
// 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';
// ---
// 允许,但是不提供语义值
export const apiKey = 'SOMEKEY';
// 多数情况下,很好
export const API_KEY = 'SOMEKEY';
// ---
// bad - 不必要大写 key 没有增加语义值
export const MAPPING = {
KEY: 'value'
};
// good
export const MAPPING = {
key: 'value'
};
复制代码
存取器
19.1 对于属性的的存取函数不是必须的。
19.2 不要使用 JavaScript 的 getters/setters 方法,因为它们会导致意外的副作用,并且更加难以测试、维护和推敲。 相应的,如果你需要存取函数的时候使用 getVal()
和 setVal('hello')
。
// bad
class Dragon {
get age() {
// ...
}
set age(value) {
// ...
}
}
// good
class Dragon {
getAge() {
// ...
}
setAge(value) {
// ...
}
}
复制代码
19.3 如果属性/方法是一个 boolean
值,使用 isVal()
或者 hasVal()
。
// good
if (!dragon.hasAge()) {
return false;
}
复制代码
19.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];
}
}
复制代码
事件
20.1 当给事件(无论是 DOM 事件还是更加私有的事件)附加数据时,传入一个对象(通畅也叫做 “hash” ) 而不是原始值。 这样可以让后边的贡献者向事件数据添加更多的数据,而不用找出更新事件的每个处理器。 例如,不好的写法:
// good
$(this).trigger('listingUpdated', { listingID: listing.id });
// ...
$(this).on('listingUpdated', (e, data) => {
// do something with data.listingID
});
复制代码
标准库
- 使用
Number.isNaN
代替全局的isNaN
- 使用
Number.isFinite
代替全局的isFinite
为什么? 全局的
isFinite
强制非数字转化为数字,对任何强制转化为有限数字的东西都返回 true。
性能
- On Layout & Web Performance
- String vs Array Concat
- Try/Catch Cost In a Loop
- Bang Function
- jQuery Find vs Context, Selector
- innerHTML vs textContent for script text
- Long String Concatenation
- Are Javascript functions like
map()
,reduce()
, andfilter()
optimized for traversing arrays?
资源
学习 ES6+
读这个
工具
-
Code Style Linters
-
Neutrino preset – neutrino-preset-airbnb-base
其他编码规范
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
- StandardJS
其他风格
- Naming this in nested functions – Christian Johansen
- Conditional Callbacks – Ross Allen
- Popular JavaScript Coding Conventions on GitHub – JeongHoon Byun
- Multiple var statements in JavaScript, not superfluous – Ben Alman
进一步阅读
- Understanding JavaScript Closures – Angus Croll
- Basic JavaScript for the impatient programmer – Dr. Axel Rauschmayer
- You Might Not Need jQuery – Zack Bloom & Adam Schwartz
- ES6 Features – Luke Hoban
- Frontend Guidelines – Benjamin De Cock
书籍
- JavaScript: The Good Parts – Douglas Crockford
- JavaScript Patterns – Stoyan Stefanov
- Pro JavaScript Design Patterns – Ross Harmes and Dustin Diaz
- High Performance Web Sites: Essential Knowledge for Front-End Engineers – Steve Souders
- Maintainable JavaScript – Nicholas C. Zakas
- JavaScript Web Applications – Alex MacCaw
- Pro JavaScript Techniques – John Resig
- Smashing Node.js: JavaScript Everywhere – Guillermo Rauch
- Secrets of the JavaScript Ninja – John Resig and Bear Bibeault
- Human JavaScript – Henrik Joreteg
- Superhero.js – Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
- JSBooks – Julien Bouquillon
- Third Party JavaScript – Ben Vinegar and Anton Kovalyov
- Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript – David Herman
- Eloquent JavaScript – Marijn Haverbeke
- You Don’t Know JS: ES6 & Beyond – Kyle Simpson
博客
- JavaScript Weekly
- JavaScript, JavaScript…
- Bocoup Weblog
- Adequately Good
- NCZOnline
- Perfection Kills
- Ben Alman
- Dmitry Baranovskiy
- nettuts
播客