https://github.com/airbnb/javascript
Airbnb关于JS的代码规范,还是推荐阅读英文原文,尝试缓慢翻译填坑中.
注意:有很多内容是ES6的规范,使用可以按照自己的需要,并注意浏览器兼容性。
correct me if something wrong :D
(stop updating currently since there are so many es6 stuff and I can't guarantee the quality without good understanding of es6.)
目录
- Types 类型
- References 引用
- Objects 对象
- Arrays 数组
- Destructuring 去构造化
- Strings 字符串
- Functions 函数
- Arrow Functions 箭头函数
- Classes & Constructors 类/构造器
- Modules 模组
- Iterators and Generators
- Properties
- Variables
- Hoisting
- Comparison Operators & Equality
- Blocks
- Comments
- Whitespace
- Commas
- Semicolons
- Type Casting & Coercion
- Naming Conventions
- Accessors
- Events
- jQuery
- ECMAScript 5 Compatibility
- ECMAScript 6+ (ES 2015+) Styles
- Testing
- Performance
- Resources
- In the Wild
- Translation
- The JavaScript Style Guide Guide
- Chat With Us About JavaScript
- Contributors
- License
Type 类型
1.1 当你处理一个如下简单的数据类型时,你是直接对它对应的值进行操作。
string
number
boolean
null
undefined
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
1.2 当你处理一个如下复杂的数据类型时,你处理的是它的引用。
object
array
function
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
References 引用
2.1 使用
const
来声明所有的引用,避免使用var
。详见 eslint:prefer-const
,no-const-assign
为什么?因为这样可以保证你不会重新给引用赋一个另外的值,避免以此引起的bug和对代码的理解困难
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
2.2 如果你必须要重新给一个引用赋值,,使用
let
而不是var
。详见 eslint:no-var
jscs:disallowVar
为什么?因为
let
是块级作用,而不像var
有着函数作用域。// bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; }
2.3 注意,使用
let
和const
声明的变量,都只作用于块级作用域。// const and let only exist in the blocks they are defined in. { let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError
Object 对象
3.1 尽量使用字面语法来直接创建对象。详见eslint:
no-new-object
// bad const item = new Object(); // good const item = {};
3.2 创建一个有着动态的属性名的对象时,尽量使用计算后的属性名。
为什么?这样可以让你同时定义一个对象所有的属性
function getKey(k) { return `a key named ${k}`; } // bad const obj = { id: 5, name: 'San Francisco', }; obj[getKey('enabled')] = true; // good const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true, };
3.3 使用简写的属性方法。详见eslint:
object-shorthand
jscs:requireEnhancedObjectLiterals
// bad const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { value: 1, addValue(value) { return atom.value + value; }, };
3.4使用简写的属性值。详见
object-shorthand
jscs:requireEnhancedObjectLiterals
为什么?书写更简短,描述性更强。
const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, };
3.5 把简写的属性名统一分组写在声明对象最开始的地方。
为什么?这样一下就能看出来哪些属性使用了简写。
const anakinSkywalker = 'Anakin Skywalker'; const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { episodeOne: 1, twoJediWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, anakinSkywalker, }; // good const obj = { lukeSkywalker, anakinSkywalker, episodeOne: 1, twoJediWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4, };
3.6 只有在属性名是无效的标识符的时候才使用引号包起来使之成为字符串(比如数字开头或带有空格的多个词)。详见eslint:
quote-props
jscs:disallowQuotedKeysInObjects
为什么?一般主观上来说这样更容易阅读,也有利于语法高亮和一些JS引擎优化。
// bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // good const good = { foo: 3, bar: 4, 'data-blah': 5, };
3.7 尽量不要直接调用某些
Object.prototype
的方法,比如hasOwnProperty
,propertyIsEnumerable
, 和isPrototypeOf
.为什么?因为某些对象里这些方法可能被覆写了,比如这种
{ hasOwnProperty: false }
,或者是一个null对象 (Object.create(null)
).// bad console.log(object.hasOwnProperty(key)); // good console.log(Object.prototype.hasOwnProperty.call(object, key)); // best const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. /* or */ import has from 'has'; … console.log(has.call(object, key));
3.8 浅复制对象时使用
...
操作符而不是使用Object.assign
// very bad const original = { a: 1, b: 2 }; const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ delete copy.a; // so does this // bad const original = { a: 1, b: 2 }; const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } // good 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 }
Array 数组
4.1 使用字面语法来创建数组对象。详见eslint:
no-array-constructor
// bad const items = new Array(); // good const items = [];
4.2 使用
push()
方法来添加元素而不是直接赋值。const someStack = []; // bad someStack[someStack.length] = 'abracadabra'; // good someStack.push('abracadabra');
4.3 使用
...
操作符来复制数组。// bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items];
4.4 使用
Array.from()
来把一个类数组转化成数组。const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo);
4.5 数组方法的回调函数里使用return声明。除非像8.2那样函数内只有一句表达的时候可以删去return,详见
array-callback-return
// bad [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); // good [1, 2, 3].map(x => x + 1); // bad const flat = {}; [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); flat[index] = flatten; }); // good const flat = {}; [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); flat[index] = flatten; 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; });
Destructuring 去构造化
5.1 在获取和使用多种对象的属性的时候,尽量对对象去构造化。详见jscs:
requireObjectDestructuring
为什么?这样可以免于为这些属性设置很多临时的引用变量。
// bad function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return `${firstName} ${lastName}`; } // good function getFullName(user) { const { firstName, lastName } = user; return `${firstName} ${lastName}`; } // best function getFullName({ firstName, lastName }) { return `${firstName} ${lastName}`; }
5.2 数组的去构造化。详见 jscs:
requireArrayDestructuring
const arr = [1, 2, 3, 4]; // bad const first = arr[0]; const second = arr[1]; // good const [first, second] = arr;
5.3 对多个返回值使用对象去构造化而不是数组去构造化。详见jscs:
disallowArrayDestructuringReturn
为什么?因为这样你可以随时添加新属性或者改变顺序而不会影响调用。
// bad function processInput(input) { // then a miracle occurs return [left, right, top, bottom]; } // the caller needs to think about the order of return data const [left, __, top] = processInput(input); // good function processInput(input) { // then a miracle occurs return { left, right, top, bottom }; } // the caller selects only the data they need const { left, top } = processInput(input);
Strings 字符串
6.1 对字符串使用单引号。详见eslint:
quotes
jscs:validateQuoteMarks
// bad const name = "Capt. Janeway"; // bad - template literals should contain interpolation or newlines const name = `Capt. Janeway`; // good const name = 'Capt. Janeway';
6.2 超过100个字符的长字符串不要使用换行符或者字符串拼接。
为什么?难以处理,并且不方便搜索。
// bad 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.'; // bad 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.'; // good 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.';
6.3 包含动态内容的字符串使用模版字符串而不是字符串拼接。详见eslint:
prefer-template
template-curly-spacing
jscs:requireTemplateStrings
为什么?因为代码更简洁,可读性更好。方便合适的换行和字符串插入。
// bad function sayHi(name) { return 'How are you, ' + name + '?'; } // bad function sayHi(name) { return ['How are you, ', name, '?'].join(); } // bad function sayHi(name) { return `How are you, ${ name }?`; } // good function sayHi(name) { return `How are you, ${name}?`; }
6.4 在字符串中永远不要使用
eval()
,很容易出现bug。6.5 没有必要的时候不要使用转义符。详见eslint:
no-useless-escape
为什么?因为反斜线大大影响代码阅读。除非必要不然不要使用。
// bad const foo = '\'this\' \i\s \"quoted\"'; // good const foo = '\'this\' is "quoted"'; const foo = `'this' is "quoted"`;
Functions 函数
7.1 使用命名的函数表达式,而不是直接声明函数。详见eslint:
func-style
jscs:requireFunctionDeclarations
为什么?因为函数声明会被提升到顶部,也就意味着很轻易就会出现函数在文件中被定义之前就被引用了。这影响了代码的可读性和维护性。如果你发现一个函数的定义已经大到或者复杂到影响对其他代码的理解,那么你最好把它抽出来到一个单独的模组。不要忘记命名函数,匿名函数通常在出现问题的时候很难去定位。
// bad const foo = function () { }; // bad function foo() { } // good const foo = function bar() { };
7.2 立即调用的函数(IIFE)记得用括号包起来。详见eslint:
wrap-iife
jscs:requireParenthesesAroundIIFE
为什么?一个立即调用的函数是一个单独的整体。记得要用括号包起来整个内容。包括调用函数的一对括号。注意在现在这样一个到处都是模组的世界,基本上你不会需要用到这样的函数。
// immediately-invoked function expression (IIFE) (function () { console.log('Welcome to the Internet. Please follow me.'); }());
7.3 不要在非函数的代码块(比如if,while等)里直接声明函数,把函数赋给一个变量。浏览器虽然允许你这样做,但在解析的时候经常会有差别。详见eslint:
no-loop-func
7.4 注意:ECMA-262把块(
block
)定义为一系列语句(statement
)。而函数声明(`function declaration并不算语句。详见 Read ECMA-262's note on this issue.// bad if (currentUser) { function test() { console.log('Nope.'); } } // good let test; if (currentUser) { test = () => { console.log('Yup.'); }; }
7.5 不要把一个参数命名为
arguments
。这样在整个函数作用域里会判定为参数名而影响arguments
对象的使用。// bad function nope(name, options, arguments) { // ...stuff... } // good function yup(name, options, args) { // ...stuff... }
7.6 不要直接使用
arguments
,使用剩余参数的语法…
。详见eslint:prefer-rest-params
为什么?因为对于你想要处理的参数使用
...
是非常明确的,而且剩余参数是一个真实的数组,而不像arguments对象是一个类数组。// bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); }
7.7 使用默认的参数语法,而不是转化成函数的
arguments
// really bad function handleThings(opts) { // 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 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 永远不要使用函数构造器来创建一个新函数。详见eslint:
no-new-func
。为什么?因为这样会类似
eval()
来处理字符串。容易引起错误。// bad var add = new Function('a', 'b', 'return a + b'); // still bad var subtract = Function('a', 'b', 'return a - b');
7.11 函数签名的空格问题。详见eslint:
space-before-function-paren
space-before-blocks
保持一致性总是好的。而且在删除或添加函数名的时候你也不需要添加或删除空格。
// bad const f = function(){}; const g = function (){}; const h = function() {}; // good const x = function () {}; const y = function a() {};