Airbnb JavaScript 代码规范() {
一种写JavaScript更合理的代码风格。
Note: 本指南假设你使用了 Babel, 并且要求你使用 babel-preset-airbnb 或者其他同等资源。 并且假设你在你的应用中安装了 shims/polyfills ,使用airbnb-browser-shims 或者相同功能。
目录
- 类型
- 引用
- 对象
- 数组
- 解构
- 字符
- 方法
- 箭头函数
- 类和构造器
- 模块
- 迭代器和发生器
- 属性
- 变量
- 提升
- 比较运算符和等号
- 块
- 控制语句
- 注释
- 空白
- 逗号
- 分号
- 类型转换和强制类型转换
- 命名规范
- 存取器
- 事件
- jQuery
- ECMAScript 5 兼容性
- ECMAScript 6+ (ES 2015+) 风格
- 标准库
- 测试
- 性能
- 资源
- JavaScript风格指南的指南
- 许可证
- 修正案
类型
-
1.1 原始值: 当你访问一个原始类型的时候,你可以直接使用它的值。
string
number
boolean
null
undefined
symbol
const foo = 1;let bar = foo;bar = 9;console; // => 1, 9- 标识符不能完全被支持,因此在针对不支持的浏览器或者环境时不应该使用它们。
-
1.2 复杂类型: 当你访问一个复杂类型的时候,你需要一个值得引用。
object
array
function
const foo = 1 2;const bar = foo;bar0 = 9;console; // => 9, 9
引用
-
2.1 使用
const
定义你的所有引用;避免使用var
。 eslint:prefer-const
,no-const-assign
为什么? 这样能够确保你不能重新赋值你的引用,否则可能导致错误或者产生难以理解的代码。.
// badvar a = 1;var b = 2;// goodconst a = 1;const b = 2;
-
2.2 如果你必须重新赋值你的引用, 使用
let
代替var
。 eslint:no-var
为什么?
let
是块级作用域,而不像var
是函数作用域.// badvar count = 1;if truecount += 1;// good, use the let.let count = 1;if truecount += 1;
-
2.3 注意,let 和 const 都是块级范围的。
// const 和 let 只存在于他们定义的块中。let a = 1;const b = 1;console; // ReferenceErrorconsole; // ReferenceError
对象
-
3.1 使用字面语法来创建对象。 eslint:
no-new-object
// badconst item = ;// goodconst item = {};
-
3.2 在创建具有动态属性名称的对象时使用计算属性名。
为什么? 它允许你在一个地方定义对象的所有属性。
{return `a key named `;}// badconst obj =id: 5name: 'San Francisco';obj = true;// goodconst obj =id: 5name: 'San Francisco': true;
-
3.3 使用对象方法的缩写。 eslint:
object-shorthand
// badconst atom =value: 1{return atomvalue + value;};// goodconst atom =value: 1{return atomvalue + value;};
-
3.4 使用属性值的缩写。 eslint:
object-shorthand
为什么? 它的写法和描述较短。
const lukeSkywalker = 'Luke Skywalker';// badconst obj =lukeSkywalker: lukeSkywalker;// goodconst obj =lukeSkywalker;
-
3.5 在对象声明的时候将简写的属性进行分组。
为什么? 这样更容易的判断哪些属性使用的简写。
const anakinSkywalker = 'Anakin Skywalker';const lukeSkywalker = 'Luke Skywalker';// badconst obj =episodeOne: 1twoJediWalkIntoACantina: 2lukeSkywalkerepisodeThree: 3mayTheFourth: 4anakinSkywalker;// goodconst obj =lukeSkywalkeranakinSkywalkerepisodeOne: 1twoJediWalkIntoACantina: 2episodeThree: 3mayTheFourth: 4;
-
3.6 只使用引号标注无效标识符的属性。 eslint:
quote-props
为什么? 总的来说,我们认为这样更容易阅读。 它提升了语法高亮显示,并且更容易通过许多 JS 引擎优化。
// badconst bad ='foo': 3'bar': 4'data-blah': 5;// goodconst good =foo: 3bar: 4'data-blah': 5;
-
3.7 不能直接调用
Object.prototype
的方法,如:hasOwnProperty
、propertyIsEnumerable
和isPrototypeOf
。为什么? 这些方法可能被以下问题对象的属性追踪 - 相应的有
{ hasOwnProperty: false }
- 或者,对象是一个空对象 (Object.create(null)
)。// badconsole;// goodconsole;// bestconst has = ObjectprototypehasOwnProperty; // 在模块范围内的缓存中查找一次/* or */; // https://www.npmjs.com/package/has// ...console;
-
3.8 更喜欢对象扩展操作符,而不是用
Object.assign
浅拷贝一个对象。 使用对象的 rest 操作符来获得一个具有某些属性的新对象。// very badconst original = a: 1 b: 2 ;const copy = Object; // 变异的 `original` ಠ_ಠdelete copya; // 这....// badconst original = a: 1 b: 2 ;const copy = Object; // copy => { a: 1, b: 2, c: 3 }// goodconst original = a: 1 b: 2 ;const copy = ...original c: 3 ; // copy => { a: 1, b: 2, c: 3 }const a ...noA = copy; // noA => { b: 2, c: 3 }
数组
-
4.1 使用字面语法创建数组。 eslint:
no-array-constructor
// badconst items = ;// goodconst items = ;
-
4.2 使用 Array#push 取代直接赋值来给数组添加项。
const someStack = ;// badsomeStacksomeStacklength = 'abracadabra';// goodsomeStack;
-
4.3 使用数组展开方法
...
来拷贝数组。// badconst len = itemslength;const itemsCopy = ;let i;for i = 0; i < len; i += 1itemsCopyi = itemsi;// goodconst itemsCopy = ...items;
-
4.4 将一个类数组对象转换成一个数组, 使用展开方法
...
代替Array.from
。const foo = document;// goodconst nodes = Array;// bestconst nodes = ...foo;
-
4.5 对于对迭代器的映射,使用 Array.from 替代展开方法
...
, 因为它避免了创建中间数组。// badconst baz = ...foo;// goodconst baz = Array;
-
4.6 在数组回调方法中使用 return 语句。 如果函数体由一个返回无副作用的表达式的单个语句组成,那么可以省略返回值, 具体查看 8.2。 eslint:
array-callback-return
// good1 2 3;// good1 2 3;// bad - 没有返回值,意味着在第一次迭代后 `acc` 没有被定义0 1 2 3 4 5;// good0 1 2 3 4 5;// badinbox;// goodinbox;
-
4.7 如果数组有多行,则在开始的时候换行,然后在结束的时候换行。
// badconst arr =0 1 2 3 4 5;const objectInArray =id: 1id: 2;const numberInArray =1 2;// goodconst arr = 0 1 2 3 4 5;const objectInArray =id: 1id: 2;const numberInArray =12;
解构
-
5.1 在访问和使用对象的多个属性的时候使用对象的解构。 eslint:
prefer-destructuring
为什么? 解构可以避免为这些属性创建临时引用。
// bad{const firstName = userfirstName;const lastName = userlastName;return ` `;}// good{const firstName lastName = user;return ` `;}// best{return ` `;}
-
5.2 使用数组解构。 eslint:
prefer-destructuring
const arr = 1 2 3 4;// badconst first = arr0;const second = arr1;// goodconst first second = arr;
-
5.3 对于多个返回值使用对象解构,而不是数组解构。
为什么? 你可以随时添加新的属性或者改变属性的顺序,而不用修改调用方。
// bad{// 处理代码...return left right top bottom;}// 调用者需要考虑返回数据的顺序。const left __ top = ;// good{// 处理代码...return left right top bottom ;}// 调用者只选择他们需要的数据。const left top = ;
字符
-
6.1 使用单引号
''
定义字符串。 eslint:quotes
// badconst name = "Capt. Janeway";// bad - 模板文字应该包含插值或换行。const name = `Capt. Janeway`;// goodconst name = 'Capt. Janeway';
-
6.2 使行超过100个字符的字符串不应使用字符串连接跨多行写入。
为什么? 断开的字符串更加难以工作,并且使代码搜索更加困难。
// badconst 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.';// badconst 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.';// goodconst 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
为什么? 字符串模板为您提供了一种可读的、简洁的语法,具有正确的换行和字符串插值特性。
// bad{return 'How are you, ' + name + '?';}// bad{return 'How are you, ' name '?';}// bad{return `How are you, ?`;}// good{return `How are you, ?`;}
-
6.5 不要转义字符串中不必要的字符。 eslint:
no-useless-escape
为什么? 反斜杠损害了可读性,因此只有在必要的时候才会出现。
// badconst foo = '\'this\' \i\s \"quoted\"';// goodconst foo = '\'this\' is "quoted"';const foo = `my name is ''`;
方法
-
7.1 使用命名的函数表达式代替函数声明。 eslint:
func-style
为什么? 函数声明是挂起的,这意味着在它在文件中定义之前,很容易引用函数。这会损害可读性和可维护性。如果您发现函数的定义是大的或复杂的,以至于它干扰了对文件的其余部分的理解,那么也许是时候将它提取到它自己的模块中了!不要忘记显式地命名这个表达式,不管它的名称是否从包含变量(在现代浏览器中经常是这样,或者在使用诸如Babel之类的编译器时)。这消除了对错误的调用堆栈的任何假设。 (Discussion)
// bad{// ...}// badconst foo = {// ...};// good// 从变量引用调用中区分的词汇名称const short = {// ...};
-
7.2 Wrap立即调用函数表达式。 eslint:
wrap-iife
为什么? 立即调用的函数表达式是单个单元 - 包装, 并且拥有括号调用, 在括号内, 清晰的表达式。 请注意,在一个到处都是模块的世界中,您几乎不需要一个 IIFE 。
// immediately-invoked function expression (IIFE) 立即调用的函数表达式{console;};
- 7.3 切记不要在非功能块中声明函数 (
if
,while
, 等)。 将函数赋值给变量。 浏览器允许你这样做,但是他们都有不同的解释,这是个坏消息。 eslint:no-loop-func
-
7.4 注意: ECMA-262 将
block
定义为语句列表。 函数声明不是语句。// badif currentUser{console;}// goodlet test;if currentUser{console;};
-
7.5 永远不要定义一个参数为
arguments
。 这将会优先于每个函数给定范围的arguments
对象。// bad{// ...}// good{// ...}
-
7.6 不要使用
arguments
, 选择使用 rest 语法...
代替。 eslint:prefer-rest-params
为什么?
...
明确了你想要拉取什么参数。 更甚, rest 参数是一个真正的数组,而不仅仅是类数组的arguments
。// bad{const args = Arrayprototypeslice;return args;}// good{return args;}
-
7.7 使用默认的参数语法,而不是改变函数参数。
// really bad{// No! We shouldn’t mutate function arguments.// Double bad: if opts is falsy it'll be set to an object which may// be what you want but it can introduce subtle bugs.opts = opts || {};// ...}// still bad{if opts === void 0opts = {};// ...}// good{// ...}
-
7.8 避免使用默认参数的副作用。
为什么? 他们很容易混淆。
var b = 1;// bad{console;}; // 1; // 2; // 3; // 3
-
7.9 总是把默认参数放在最后。
// bad{// ...}// good{// ...}
-
7.10 永远不要使用函数构造器来创建一个新函数。 eslint:
no-new-func
为什么? 以这种方式创建一个函数将对一个类似于
eval()
的字符串进行计算,这将打开漏洞。// badvar add = 'a' 'b' 'return a + b';// still badvar subtract = Function'a' 'b' 'return a - b';
-
7.11 函数签名中的间距。 eslint:
space-before-function-paren
space-before-blocks
为什么? 一致性很好,在删除或添加名称时不需要添加或删除空格。
// badconst f = {};const g = {};const h = {};// goodconst x = {};const y = {};
-
7.12 没用变异参数。 eslint:
no-param-reassign
为什么? 将传入的对象作为参数进行操作可能会在原始调用程序中造成不必要的变量副作用。
// bad{objkey = 1;}// good{const key = ObjectprototypehasOwnProperty ? objkey : 1;}
-
7.13 不要再赋值参数。 eslint:
no-param-reassign
为什么? 重新赋值参数会导致意外的行为,尤其是在访问
arguments
对象的时候。 它还可能导致性能优化问题,尤其是在 V8 中。// bad{a = 1;// ...}{if !a a = 1;// ...}// good{const b = a || 1;// ...}{// ...}
-
7.14 优先使用扩展运算符
...
来调用可变参数函数。 eslint:prefer-spread
为什么? 它更加干净,你不需要提供上下文,并且你不能轻易的使用
apply
来new
。// badconst x = 1 2 3 4 5;consolelog;// goodconst x = 1 2 3 4 5;console;// badnew Functionprototypebind;// good...2016 8 5;
-
7.15 具有多行签名或者调用的函数应该像本指南中的其他多行列表一样缩进:在一行上只有一个条目,并且每个条目最后加上逗号。 eslint:
function-paren-newline
// bad{// ...}// good{// ...}// badconsole;// goodconsole;
箭头函数
-
8.1 当你必须使用匿名函数时 (当传递内联函数时), 使用箭头函数。 eslint:
prefer-arrow-callback
,arrow-spacing
为什么? 它创建了一个在
this
上下文中执行的函数版本,它通常是你想要的,并且是一个更简洁的语法。为什么不? 如果你有一个相当复杂的函数,你可以把这个逻辑转移到它自己的命名函数表达式中。
// bad1 2 3;// good1 2 3;
-
8.2 如果函数体包含一个单独的语句,返回一个没有副作用的 expression , 省略括号并使用隐式返回。否则,保留括号并使用
return
语句。 eslint:arrow-parens
,arrow-body-style
为什么? 语法糖。 多个函数被链接在一起时,提高可读性。
// bad1 2 3;// good1 2 3;// good1 2 3;// good1 2 3;// 没有副作用的隐式返回{const val = ;if val === true// 如果回调返回 true 执行}let bool = false;// bad;// good;
-
8.3 如果表达式跨越多个行,用括号将其括起来,以获得更好的可读性。
为什么? 它清楚地显示了函数的起点和终点。
// bad'get' 'post' 'put';// good'get' 'post' 'put';
-
8.4 如果你的函数接收一个参数,则可以不用括号,省略括号。 否则,为了保证清晰和一致性,需要在参数周围加上括号。 注意:总是使用括号是可以接受的,在这种情况下,我们使用 “always” option 来配置 eslint. eslint:
arrow-parens
为什么? 减少视觉上的混乱。
// bad1 2 3;// good1 2 3;// good1 2 3;// bad1 2 3;// good1 2 3;
-
8.5 避免箭头函数符号 (
=>
) 和比较运算符 (<=
,>=
) 的混淆。 eslint:no-confusing-arrow
// badconst itemHeight = itemheight > 256 ? itemlargeSize : itemsmallSize;// badconst itemHeight = itemheight > 256 ? itemlargeSize : itemsmallSize;// goodconst itemHeight = itemheight > 256 ? itemlargeSize : itemsmallSize;// goodconst itemHeight = {const height largeSize smallSize = item;return height > 256 ? largeSize : smallSize;};
-
8.6 注意带有隐式返回的箭头函数函数体的位置。 eslint:
implicit-arrow-linebreak
// badbar;bar;// goodbar;bar;bar
类和构造器
-
9.1 尽量使用
class
. 避免直接操作prototype
.为什么?
class
语法更简洁,更容易推理。// bad{thisqueue = ...contents;}Queueprototype {const value = thisqueue0;thisqueue;return value;};// good{thisqueue = ...contents;}{const value = thisqueue0;thisqueue;return value;}
-
9.2 使用
extends
来扩展继承。为什么? 它是一个内置的方法,可以在不破坏
instanceof
的情况下继承原型功能。// badconst inherits = ;{Queue;};PeekableQueueprototype {return thisqueue0;};// good{return thisqueue0;}
-
9.3 方法返回了
this
来供其内部方法调用。// badJediprototype {thisjumping = true;return true;};Jediprototype {thisheight = height;};const luke = ;luke; // => trueluke; // => undefined// good{thisjumping = true;return this;}{thisheight = height;return this;}const luke = ;luke;
-
9.4 只要在确保能正常工作并且不产生任何副作用的情况下,编写一个自定义的
toString()
方法也是可以的。{thisname = optionsname || 'no name';}{return thisname;}{return `Jedi - `;}
-
9.5 如果没有指定类,则类具有默认的构造器。 一个空的构造器或是一个代表父类的函数是没有必要的。 eslint:
no-useless-constructor
// bad{}{return thisname;}// bad{super...args;}// good{super...args;thisname = 'Rey';}
-
9.6 避免定义重复的类成员。 eslint:
no-dupe-class-members
为什么? 重复的类成员声明将会默认倾向于最后一个 - 具有重复的类成员可以说是一个错误。
// bad{ return 1; }{ return 2; }// good{ return 1; }// good{ return 2; }
模块
-
10.1 你可能经常使用模块 (
import
/export
) 在一些非标准模块的系统上。 你也可以在你喜欢的模块系统上相互转换。为什么? 模块是未来的趋势,让我们拥抱未来。
// badconst AirbnbStyleGuide = ;moduleexports = AirbnbStyleGuidees6;// ok;es6;// best;;
-
10.2 不要使用通配符导入。
为什么? 这确定你有一个单独的默认导出。
// bad;// good;
-
10.3 不要直接从导入导出。
为什么? 虽然写在一行很简洁,但是有一个明确的导入和一个明确的导出能够保证一致性。
// bad// filename es6.js;// good// filename es6.js;;
-
10.4 只从一个路径导入所有需要的东西。 eslint:
no-duplicate-imports
为什么? 从同一个路径导入多个行,使代码更难以维护。
// bad;// … 其他导入 … //;// good;// good;
-
10.5 不要导出可变的引用。 eslint:
import/no-mutable-exports
为什么? 在一般情况下,应该避免发生突变,但是在导出可变引用时及其容易发生突变。虽然在某些特殊情况下,可能需要这样,但是一般情况下只需要导出常量引用。
// badlet foo = 3;;// goodconst foo = 3;;
-
10.6 在单个导出的模块中,选择默认模块而不是指定的导出。 eslint:
import/prefer-default-export
为什么? 为了鼓励更多的文件只导出一件东西,这样可读性和可维护性更好。
// bad{}// good{}
-
10.7 将所有的
import
s 语句放在所有非导入语句的上边。 eslint:import/first
为什么? 由于所有的
import
s 都被提前,保持他们在顶部是为了防止意外发生。// bad;foo;;// good;;foo;
-
10.8 多行导入应该像多行数组和对象一样缩进。
为什么? 花括号和其他规范一样,遵循相同的缩进规则,后边的都好一样。
// bad;// good;
-
10.9 在模块导入语句中禁止使用 Webpack 加载器语法。 eslint:
import/no-webpack-loader-syntax
为什么? 因为在导入语句中使用 webpack 语法,将代码和模块绑定在一起。应该在
webpack.config.js
中使用加载器语法。// bad;;// good;;
迭代器和发生器
-
11.1 不要使用迭代器。 你应该使用 JavaScript 的高阶函数代替
for-in
或者for-of
。 eslint:no-iterator
no-restricted-syntax
为什么? 这是我们强制的规则。 拥有返回值得纯函数比这个更容易解释。
使用
map()
/every()
/filter()
/find()
/findIndex()
/reduce()
/some()
/ ... 遍历数组, 和使用Object.keys()
/Object.values()
/Object.entries()
迭代你的对象生成数组。const numbers = 1 2 3 4 5;// badlet sum = 0;for let num of numberssum += num;sum === 15;// goodlet sum = 0;numbers;sum === 15;// best (use the functional force)const sum = numbers;sum === 15;// badconst increasedByOne = ;for let i = 0; i < numberslength; i++increasedByOne;// goodconst increasedByOne = ;numbers;// best (keeping it functional)const increasedByOne = numbers;
-
11.2 不要使用发生器。
为什么? 它们不能很好的适应 ES5。
-
11.3 如果你必须使用发生器或者无视 我们的建议,请确保他们的函数签名是正常的间隔。 eslint:
generator-star-spacing
为什么?
function
和*
是同一个概念关键字的一部分 -*
不是function
的修饰符,function*
是一个不同于function
的构造器。// bad{// ...}// badconst bar = {// ...};// badconst baz = {// ...};// badconst quux = {// ...};// bad{// ...}// bad{// ...}// very bad{// ...}// very badconst wat ={// ...};// good{// ...}// goodconst foo = {// ...};
属性
-
12.1 访问属性时使用点符号。 eslint:
dot-notation
const luke =jedi: trueage: 28;// badconst isJedi = luke'jedi';// goodconst isJedi = lukejedi;
-
12.2 使用变量访问属性时,使用
[]
表示法。const luke =jedi: trueage: 28;{return lukeprop;}const isJedi = ;
-
12.3 计算指数时,可以使用
**
运算符。 eslint:no-restricted-properties
.// badconst binary = Math;// goodconst binary = 2 ** 10;
变量
-
13.1 使用
const
或者let
来定义变量。 不这样做将创建一个全局变量。 我们希望避免污染全局命名空间。 Captain Planet 警告过我们。 eslint:no-undef
prefer-const
// badsuperPower = ;// goodconst superPower = ;
-
13.2 使用
const
或者let
声明每一个变量。 eslint:one-var
为什么? 这样更容易添加新的变量声明,而且你不必担心是使用
;
还是使用,
或引入标点符号的差别。 你可以通过 debugger 逐步查看每个声明,而不是立即跳过所有声明。// badconst items =goSportsTeam = truedragonball = 'z';// bad// (compare to above, and try to spot the mistake)const items =goSportsTeam = true;dragonball = 'z';// goodconst items = ;const goSportsTeam = true;const dragonball = 'z';
-
13.3 把
const
声明的放在一起,把let
声明的放在一起。.为什么? 这在后边如果需要根据前边的赋值变量指定一个变量时很有用。
// badlet i len dragonballitems =goSportsTeam = true;// badlet i;const items = ;let dragonball;const goSportsTeam = true;let len;// goodconst goSportsTeam = true;const items = ;let dragonball;let i;let length;
-
13.4 在你需要的使用定义变量,但是要把它们放在一个合理的地方。
为什么?
let
和const
是块级作用域而不是函数作用域。// bad - 不必要的函数调用{const name = ;if hasName === 'test'return false;if name === 'test'this;return false;return name;}// good{if hasName === 'test'return false;const name = ;if name === 'test'this;return false;return name;}
-
13.5 不要链式变量赋值。 eslint:
no-multi-assign
为什么? 链式变量赋值会创建隐式全局变量。
// bad{// JavaScript 把它解释为// let a = ( b = ( c = 1 ) );// let 关键词只适用于变量 a ;变量 b 和变量 c 则变成了全局变量。let a = b = c = 1;};console; // throws ReferenceErrorconsole; // 1console; // 1// good{let a = 1;let b = a;let c = a;};console; // throws ReferenceErrorconsole; // throws ReferenceErrorconsole; // throws ReferenceError// 对于 `const` 也一样
-
13.6 避免使用不必要的递增和递减 (
++
,--
)。 eslintno-plusplus
为什么? 在eslint文档中,一元递增和递减语句以自动分号插入为主题,并且在应用程序中可能会导致默认值的递增或递减。它还可以用像
num += 1
这样的语句来改变您的值,而不是使用num++
或num ++
。不允许不必要的增量和减量语句也会使您无法预先递增/预递减值,这也会导致程序中的意外行为。// badconst array = 1 2 3;let num = 1;num++;--num;let sum = 0;let truthyCount = 0;for let i = 0; i < arraylength; i++let value = arrayi;sum += value;if valuetruthyCount++;// goodconst array = 1 2 3;let num = 1;num += 1;num -= 1;const sum = array;const truthyCount = arraylength;
-
13.7 避免在赋值语句
=
前后换行。如果你的代码违反了max-len
, 使用括号包裹。 eslintoperator-linebreak
.为什么? 在
=
前后换行,可能混淆赋的值。// badconst foo =;// badconst foo= 'superLongLongLongLongLongLongLongLongString';// goodconst foo =;// goodconst foo = 'superLongLongLongLongLongLongLongLongString';
提升
-
14.1
var
定义的变量会被提升到函数范围的最顶部,但是它的赋值不会。const
和let
声明的变量受到一个称之为 Temporal Dead Zones (TDZ) 的新概念保护。 知道为什么 typeof 不再安全 是很重要的。// 我们知道这个行不通 (假设没有未定义的全局变量){console; // => throws a ReferenceError}// 在引用变量后创建变量声明将会因变量提升而起作用。// 注意: 真正的值 `true` 不会被提升。{console; // => undefinedvar declaredButNotAssigned = true;}// 解释器将变量提升到函数的顶部// 这意味着我们可以将上边的例子重写为:{let declaredButNotAssigned;console; // => undefineddeclaredButNotAssigned = true;}// 使用 const 和 let{console; // => throws a ReferenceErrorconsole; // => throws a ReferenceErrorconst declaredButNotAssigned = true;}
-
14.2 匿名函数表达式提升变量名,而不是函数赋值。
{console; // => undefined; // => TypeError anonymous is not a functionvar {console;};}
-
14.3 命名函数表达式提升的是变量名,而不是函数名或者函数体。
{console; // => undefined; // => TypeError named is not a function; // => ReferenceError superPower is not definedvar {console;};}// 当函数名和变量名相同时也是如此。{console; // => undefined; // => TypeError named is not a functionvar {console;};}
-
14.4 函数声明提升其名称和函数体。
{; // => Flying{console;}} -
更多信息请参考 Ben Cherry 的 JavaScript Scoping & Hoisting。
比较运算符和等号
-
15.2 条件语句,例如
if
语句使用ToBoolean
的抽象方法来计算表达式的结果,并始终遵循以下简单的规则:- Objects 的取值为: true
- Undefined 的取值为: false
- Null 的取值为: false
- Booleans 的取值为: 布尔值的取值
- Numbers 的取值为:如果为 +0, -0, or NaN 值为 false 否则为 true
- Strings 的取值为: 如果是一个空字符串
''
值为 false 否则为 true
if 0 &&// true// 一个数组(即使是空的)是一个对象,对象的取值为 true
-
15.3 对于布尔值使用简写,但是对于字符串和数字进行显式比较。
// badif isValid === true// ...// goodif isValid// ...// badif name// ...// goodif name !== ''// ...// badif collectionlength// ...// goodif collectionlength > 0// ...
- 15.4 获取更多信息请查看 Angus Croll 的 Truth Equality and JavaScript 。
-
15.5 在
case
和default
的子句中,如果存在声明 (例如.let
,const
,function
, 和class
),使用大括号来创建块 。 eslint:no-case-declarations
为什么? 语法声明在整个
switch
块中都是可见的,但是只有在赋值的时候才会被初始化,这种情况只有在case
条件达到才会发生。 当多个case
语句定义相同的东西是,这会导致问题问题。// bad// good
-
15.6 三目表达式不应该嵌套,通常是单行表达式。 eslint:
no-nested-ternary
// badconst foo = maybe1 > maybe2? "bar": value1 > value2 ? "baz" : null;// 分离为两个三目表达式const maybeNull = value1 > value2 ? 'baz' : null;// betterconst foo = maybe1 > maybe2? 'bar': maybeNull;// bestconst foo = maybe1 > maybe2 ? 'bar' : maybeNull;
-
15.7 避免不必要的三目表达式。 eslint:
no-unneeded-ternary
// badconst foo = a ? a : b;const bar = c ? true : false;const baz = c ? false : true;// goodconst foo = a || b;const bar = !!c;const baz = !c;
-
15.8 使用该混合运算符时,使用括号括起来。 唯一例外的是标准算数运算符 (
+
,-
,*
, &/
) 因为他们的优先级被广泛理解。 eslint:no-mixed-operators
为什么? 这能提高可读性并且表明开发人员的意图。
// badconst foo = a && b < 0 || c > 0 || d + 1 === 0;// badconst bar = a ** b - 5 % d;// bad// 可能陷入一种 (a || b) && c 的思考if a || b && creturn d;// goodconst foo = a && b < 0 || c > 0 || d + 1 === 0;// goodconst bar = a ** b - 5 % d;// goodif a || b && creturn d;// goodconst bar = a + b / c * d;
块
-
16.1 当有多行代码块的时候,使用大括号包裹。 eslint:
nonblock-statement-body-position
// badif testreturn false;// goodif test return false;// goodif testreturn false;// bad{ return false; }// good{return false;}
-
16.2 如果你使用的是
if
和else
的多行代码块,则将else
语句放在if
块闭括号同一行的位置。 eslint:brace-style
// badif test;;else;// goodif test;;else;
-
16.3 如果一个
if
块总是执行一个return
语句,那么接下来的else
块就没有必要了。 如果一个包含return
语句的else if
块,在一个包含了return
语句的if
块之后,那么可以拆成多个if
块。 eslint:no-else-return
// bad{if xreturn x;elsereturn y;}// bad{if xreturn x;else if yreturn y;}// bad{if xreturn x;elseif yreturn y;}// good{if xreturn x;return y;}// good{if xreturn x;if yreturn y;}// good{if xif zreturn y;elsereturn z;}
控制语句
-
17.1 如果你的控制语句 (
if
,while
等) 太长或者超过了一行最大长度的限制,则可以将每个条件(或组)放入一个新的行。 逻辑运算符应该在行的开始。为什么? 要求操作符在行的开始保持对齐并遵循类似方法衔接的模式。 这提高了可读性,并且使更复杂的逻辑更容易直观的被理解。
// badif foo === 123 || bar === 'abc' && &&;// badif foo === 123 &&bar === 'abc';// badif foo === 123&& bar === 'abc';// badiffoo === 123 &&bar === 'abc';// goodiffoo === 123&& bar === 'abc';// goodiffoo === 123 || bar === 'abc'&&&&;// goodif foo === 123 && bar === 'abc';
-
17.2 不要使用选择操作符代替控制语句。
// bad!isRunning && ;// goodif !isRunning;
注释
-
18.1 使用
/** ... */
来进行多行注释。// bad// make() returns a new element// based on the passed in tag name//// @param {String} tag// @return {Element} element{// ...return element;}// good/*** make() returns a new element* based on the passed-in tag name*/{// ...return element;}
-
18.2 使用
//
进行单行注释。 将单行注释放在需要注释的行的上方新行。 在注释之前放一个空行,除非它在块的第一行。// badconst active = true; // is current tab// good// is current tabconst active = true;// bad{console;// set the default type to 'no type'const type = thistype || 'no type';return type;}// good{console;// set the default type to 'no type'const type = thistype || 'no type';return type;}// also good{// set the default type to 'no type'const type = thistype || 'no type';return type;}
-
18.3 用一个空格开始所有的注释,使它更容易阅读。 eslint:
spaced-comment
// bad//is current tabconst active = true;// good// is current tabconst active = true;// bad/***make() returns a new element*based on the passed-in tag name*/{// ...return element;}// good/*** make() returns a new element* based on the passed-in tag name*/{// ...return element;}
- 18.4 使用
FIXME
或者TODO
开始你的注释可以帮助其他开发人员快速了解,如果你提出了一个需要重新审视的问题,或者你对需要实现的问题提出的解决方案。 这些不同于其他评论,因为他们是可操作的。 这些行为是FIXME: -- 需要解决这个问题
或者TODO: -- 需要被实现
。
-
18.5 使用
// FIXME:
注释一个问题。{super;// FIXME: 这里不应该使用全局变量total = 0;}
-
18.6 使用
// TODO:
注释解决问题的方法。{super;// TODO: total 应该由一个 param 的选项配置thistotal = 0;}
空白
-
19.1 使用 tabs (空格字符) 设置为 2 个空格。 eslint:
indent
// bad{∙∙∙∙let name;}// bad{∙let name;}// good{∙∙let name;}
-
19.2 在主体前放置一个空格。 eslint:
space-before-blocks
// bad{console;}// good{console;}// baddog;// gooddog;
-
19.3 在控制语句(
if
,while
等)开始括号之前放置一个空格。 在函数调用和是声明中,在参数列表和函数名之间没有空格。 eslint:keyword-spacing
// badifisJedi;// goodif isJedi;// bad{console;}// good{console;}
-
19.4 用空格分离操作符。 eslint:
space-infix-ops
// badconst x=y+5;// goodconst x = y + 5;
-
19.6 在使用链式方法调用的时候使用缩进(超过两个方法链)。 使用一个引导点,强调该行是方法调用,而不是新的语句。 eslint:
newline-per-chained-call
no-whitespace-before-property
// bad;// bad;// good;// badconst leds = stagedatadata;// goodconst leds = stagedatadata;// goodconst leds = stagedatadata;
-
19.7 在块和下一个语句之前留下一空白行。
// badif fooreturn bar;return baz;// goodif fooreturn bar;return baz;// badconst obj ={}{};return obj;// goodconst obj ={}{};return obj;// badconst arr ={}{};return arr;// goodconst arr ={}{};return arr;
-
19.8 不要在块的开头使用空白行。 eslint:
padded-blocks
// bad{console;}// badif bazconsole;elseconsole;// bad{thisbar = bar;}// good{console;}// goodif bazconsole;elseconsole;
-
19.9 不要在括号内添加空格。 eslint:
space-in-parens
// bad{return foo;}// good{return foo;}// badif fooconsole;// goodif fooconsole;
-
19.10 不要在中括号中添加空格。 eslint:
array-bracket-spacing
// badconst foo = 1 2 3 ;console;// goodconst foo = 1 2 3;console;
-
19.11 在花括号内添加空格。 eslint:
object-curly-spacing
// badconst foo = clark: 'kent';// goodconst foo = clark: 'kent' ;
-
19.12 避免让你的代码行超过100个字符(包括空格)。 注意:根据上边的 约束,长字符串可免除此规定,不应分解。 eslint:
max-len
为什么? 这样能够确保可读性和可维护性。
// badconst foo = jsonData && jsonDatafoo && jsonDatafoobar && jsonDatafoobarbaz && jsonDatafoobarbazquux && jsonDatafoobarbazquuxxyzzy;// bad$;// goodconst foo = jsonData&& jsonDatafoo&& jsonDatafoobar&& jsonDatafoobarbaz&& jsonDatafoobarbazquux&& jsonDatafoobarbazquuxxyzzy;// good$;
-
19.13 要求打开的块标志和同一行上的标志拥有一致的间距。此规则还会在同一行关闭的块标记和前边的标记强制实施一致的间距。 eslint:
block-spacing
// bad{return true;}if foo bar = 0;// good{ return true; }if foo bar = 0;
-
19.14 逗号之前避免使用空格,逗号之后需要使用空格。eslint:
comma-spacing
// badvar foo = 1bar = 2;var arr = 1 2;// goodvar foo = 1 bar = 2;var arr = 1 2;
-
19.15 在计算属性之间强化间距。eslint:
computed-property-spacing
// badobjfooobj 'foo'var x = b : aobjfoo bar// goodobjfooobj'foo'var x = b: aobjfoobar
-
19.16 在函数和它的调用之间强化间距。 eslint:
func-call-spacing
// bad;func;// good;
-
19.17 在对象的属性和值之间强化间距。 eslint:
key-spacing
// badvar obj = "foo" : 42 ;var obj2 = "foo":42 ;// goodvar obj = "foo": 42 ;
- 19.18 在行的末尾避免使用空格。 eslint:
no-trailing-spaces
-
19.19 避免多个空行,并且只允许在文件末尾添加一个换行符。 eslint:
no-multiple-empty-lines
// badvar x = 1;var y = 2;// goodvar x = 1;var y = 2;
逗号
-
20.1 逗号前置: 不行 eslint:
comma-style
// badconst story =onceuponaTime;// goodconst story =onceuponaTime;// badconst hero =firstName: 'Ada'lastName: 'Lovelace'birthYear: 1815superPower: 'computers';// goodconst hero =firstName: 'Ada'lastName: 'Lovelace'birthYear: 1815superPower: 'computers';
-
20.2 添加尾随逗号: 可以 eslint:
comma-dangle
为什么? 这个将造成更清洁的 git 扩展差异。 另外,像 Babel 这样的编译器,会在转换后的代码中删除额外的尾随逗号,这意味着你不必担心在浏览器中后面的 尾随逗号问题 。
// bad - 没有尾随逗号的 git 差异const hero = {firstName: 'Florence',- lastName: 'Nightingale'+ lastName: 'Nightingale',+ inventorOf: ['coxcomb chart', 'modern nursing']};// good - 有尾随逗号的 git 差异const hero = {firstName: 'Florence',lastName: 'Nightingale',+ inventorOf: ['coxcomb chart', 'modern nursing'],};// badconst hero =firstName: 'Dana'lastName: 'Scully';const heroes ='Batman''Superman';// goodconst hero =firstName: 'Dana'lastName: 'Scully';const heroes ='Batman''Superman';// bad{// does nothing}// good{// does nothing}// good (注意逗号不能出现在 "rest" 元素后边){// does nothing}// bad;// good;// good (注意逗号不能出现在 "rest" 元素后边);
分号
-
为什么? 当 JavaScript 遇见一个没有分号的换行符时,它会使用一个叫做 Automatic Semicolon Insertion 的规则来确定是否应该以换行符视为语句的结束,并且如果认为如此,会在代码中断前插入一个分号到代码中。 但是,ASI 包含了一些奇怪的行为,如果 JavaScript 错误的解释了你的换行符,你的代码将会中断。 随着新特性成为 JavaScript 的一部分,这些规则将变得更加复杂。 明确地终止你的语句,并配置你的 linter 以捕获缺少的分号将有助于防止你遇到的问题。
// bad - 可能异常const luke = {}const leia = {}luke leia// bad - 可能异常const reaction = "No! That's impossible!"{// handle `leia`, `lando`, `chewie`, `r2`, `c3p0`// ...}// bad - 返回 `undefined` 而不是下一行的值 - 当 `return` 单独一行的时候 ASI 总是会发生{return'search your feelings, you know it to be foo'}// goodconst luke = {};const leia = {};luke leia;// goodconst reaction = "No! That's impossible!";{// handle `leia`, `lando`, `chewie`, `r2`, `c3p0`// ...};// good{return 'search your feelings, you know it to be foo';}更多信息.
类型转换和强制类型转换
- 22.1 在语句开始前进行类型转换。
-
22.2 字符类型: eslint:
no-new-wrappers
// => this.reviewScore = 9;// badconst totalScore = thisreviewScore; // typeof totalScore is "object" not "string"// badconst totalScore = thisreviewScore + ''; // invokes this.reviewScore.valueOf()// badconst totalScore = thisreviewScore; // isn’t guaranteed to return a string// goodconst totalScore = StringthisreviewScore;
-
22.3 数字类型:使用
Number
进行类型铸造和parseInt
总是通过一个基数来解析一个字符串。 eslint:radix
no-new-wrappers
const inputValue = '4';// badconst val = inputValue;// badconst val = +inputValue;// badconst val = inputValue >> 0;// badconst val = ;// goodconst val = NumberinputValue;// goodconst val = ;
-
22.4 如果出于某种原因,你正在做一些疯狂的事情,而
parseInt
是你的瓶颈,并且出于 性能问题 需要使用位运算, 请写下注释,说明为什么这样做和你做了什么。// good/*** parseInt 使我的代码变慢。* 位运算将一个字符串转换成数字更快。*/const val = inputValue >> 0;
-
22.5 注意: 当你使用位运算的时候要小心。 数字总是被以 64-bit 值 的形式表示,但是位运算总是返回一个 32-bit 的整数 (来源)。 对于大于 32 位的整数值,位运算可能会导致意外行为。讨论。 最大的 32 位整数是: 2,147,483,647。
2147483647 >> 0; // => 21474836472147483648 >> 0; // => -21474836482147483649 >> 0; // => -2147483647
-
22.6 布尔类型: eslint:
no-new-wrappers
const age = 0;// badconst hasAge = age;// goodconst hasAge = Booleanage;// bestconst hasAge = !!age;
命名规范
-
23.2 在命名对象、函数和实例时使用驼峰命名法(camelCase)。 eslint:
camelcase
// badconst OBJEcttsssss = {};const this_is_my_object = {};{}// goodconst thisIsMyObject = {};{}
-
23.3 只有在命名构造器或者类的时候才用帕斯卡拼命名法(PascalCase)。 eslint:
new-cap
// bad{thisname = optionsname;}const bad =name: 'nope';// good{thisname = optionsname;}const good =name: 'yup';
-
23.4 不要使用前置或者后置下划线。 eslint:
no-underscore-dangle
为什么? JavaScript 在属性和方法方面没有隐私设置。 虽然前置的下划线是一种常见的惯例,意思是 “private” ,事实上,这些属性时公开的,因此,它们也是你公共 API 的一部分。 这种约定可能导致开发人员错误的认为更改不会被视为中断,或者不需要测试。建议:如果你想要什么东西是 “private” , 那就一定不能有明显的表现。
// badthis__firstName__ = 'Panda';thisfirstName_ = 'Panda';this_firstName = 'Panda';// goodthisfirstName = 'Panda';// 好,在 WeakMapx 可用的环境中// see https://kangax.github.io/compat-table/es6/#test-WeakMapconst firstNames = ;firstNames;
-
23.5 不要保存
this
的引用。 使用箭头函数或者 函数#bind。// bad{const self = this;return {console;};}// bad{const that = this;return {console;};}// good{return {console;};}
-
23.6 文件名应该和默认导出的名称完全匹配。
// file 1 contents// ...;// file 2 contents{ return 42; }// file 3 contents{}// in some other file// bad; // PascalCase import/export, camelCase filename; // PascalCase import/filename, camelCase export; // PascalCase import/filename, camelCase export// bad; // PascalCase import/export, snake_case filename; // snake_case import/filename, camelCase export; // snake_case import, camelCase export; // requiring the index file explicitly; // requiring the index file explicitly// good; // PascalCase export/import/filename; // camelCase export/import/filename; // camelCase export/import/directory name/implicit "index"// ^ supports both insideDirectory.js and insideDirectory/index.js
-
23.7 当你导出默认函数时使用驼峰命名法。 你的文件名应该和方法名相同。
{// ...};
-
23.8 当你导出一个构造器 / 类 / 单例 / 函数库 / 暴露的对象时应该使用帕斯卡命名法。
const AirbnbStyleGuide =es6:;;
-
23.9 缩略词和缩写都必须是全部大写或者全部小写。
为什么? 名字是为了可读性,不是为了满足计算机算法。
// bad;// badconst HttpRequests =// ...;// good;// goodconst HTTPRequests =// ...;// also goodconst httpRequests =// ...;// best;// bestconst requests =// ...;
-
23.10 你可以大写一个常量,如果它:(1)被导出,(2)使用
const
定义(不能被重新赋值),(3)程序员可以信任它(以及其嵌套的属性)是不变的。为什么? 这是一个可以帮助程序员确定变量是否会发生变化的辅助工具。UPPERCASE_VARIABLES 可以让程序员知道他们可以相信变量(及其属性)不会改变。
- 是否是对所有的
const
定义的变量? - 这个是没有必要的,不应该在文件中使用大写。但是,它应该用于导出常量。 - 导出对象呢? - 在顶级导出属性 (e.g.
EXPORTED_OBJECT.key
) 并且保持所有嵌套属性不变。
// badconst PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file';// badconst THING_TO_BE_CHANGED = 'should obviously not be uppercased';// badlet REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables';// ---// 允许,但是不提供语义值const apiKey = 'SOMEKEY';// 多数情况下,很好const API_KEY = 'SOMEKEY';// ---// bad - 不必要大写 key 没有增加语义值const MAPPING =KEY: 'value';// goodconst MAPPING =key: 'value'; - 是否是对所有的
存取器
- 24.1 对于属性的的存取函数不是必须的。
-
24.2 不要使用 JavaScript 的 getters/setters 方法,因为它们会导致意外的副作用,并且更加难以测试、维护和推敲。 相应的,如果你需要存取函数的时候使用
getVal()
和setVal('hello')
。// bad{// ...}{// ...}// good{// ...}{// ...}
-
24.3 如果属性/方法是一个
boolean
值,使用isVal()
或者hasVal()
。// badif !dragonreturn false;// goodif !dragonreturn false;
-
24.4 可以创建
get()
和set()
方法,但是要保证一致性。{const lightsaber = optionslightsaber || 'blue';this;}{thiskey = val;}{return thiskey;}
事件
-
25.1 当给事件(无论是 DOM 事件还是更加私有的事件)附加数据时,传入一个对象(通畅也叫做 “hash” ) 而不是原始值。 这样可以让后边的贡献者向事件数据添加更多的数据,而不用找出更新事件的每个处理器。 例如,不好的写法:
// bad;// ...;更好的写法:
// good;// ...;
jQuery
-
26.1 对于 jQuery 对象的变量使用
$
作为前缀。// badconst sidebar = ;// goodconst $sidebar = ;// goodconst $sidebarBtn = ;
-
26.2 缓存 jQuery 查询。
// bad{;// ...;}// good{const $sidebar = ;$sidebar;// ...$sidebar;}
-
26.4 对于有作用域的 jQuery 对象查询使用
find
。// bad;// bad;// good;// good;// good$sidebar;
ECMAScript 5 兼容性
ECMAScript 6+ (ES 2015+) Styles
- 28.1 这是一个链接到各种 ES6+ 特性的集合。
标准库
标准库 包含功能已损坏的实用工具,但因为遗留原因而保留。
-
29.1 使用
Number.isNaN
代替全局的isNaN
. eslint:no-restricted-globals
为什么? 全局的
isNaN
强制非数字转化为数字,对任何强制转化为 NaN 的东西都返回 true。如果需要这种行为,请明确说明。
// bad; // false; // true// goodNumber; // falseNumber; // true
-
29.2 使用
Number.isFinite
代替全局的isFinite
. eslint:no-restricted-globals
为什么? 全局的
isFinite
强制非数字转化为数字,对任何强制转化为有限数字的东西都返回 true。如果需要这种行为,请明确说明。
// bad; // true// goodNumber; // falseNumber; // true
Testing
-
30.1 是的.
{return true;}
- 30.2 没有,但是认真:
性能
- 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? - Loading...
资源
学习 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
播客