类型Types

基本数据类型

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol
const foo = 1;
let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
  • Symbols不能真正的被polyfilled,因此当目标浏览器或者环境本地不支持时,不应当使用Symbols.

复杂数据类型

  • object
  • array
  • function
const foo = [1, 2];
const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9

引用References

// bad
var a = 1;
var b = 2; // good
const a = 1;
const b = 2;
  • 如果你必须对变量重新赋值,使用let. eslint: no-var

why? let属于块作用域,var是函数作用域

// bad
var count = 1;
if (true) {
count += 1;
} // good, use the let.
let count = 1;
if (true) {
count += 1;
}
  • 记住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

对象Objects

// bad
const item = new Object(); // good
const item = {};
  • 在创建对象时,定义对象的所有属性

Why?这样对象所有的属性都在同一处定义

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,
};
// bad
const atom = {
value: 1, addValue: function (value) {
return atom.value + value;
},
}; // good
const atom = {
value: 1, addValue(value) {
return atom.value + value;
},
};

why?书写更简洁

const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
lukeSkywalker: lukeSkywalker,
}; // good
const obj = {
lukeSkywalker,
};
  • 把简写的属性放在对象定义的开头

why?这样更容易判断哪些属性使用了简写

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,
};
  • 只对不合法的标志符使用引号

why?通常这样代码更可读,同时语法高亮,且js引擎更容易优化。

// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
}; // good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
  • 不要直接调用Object.prototype,同理: hasOwnProperty, propertyIsEnumerable, isPrototypeOf

why?这些方法可能被吞没,比如用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));
  • 使用对象的spread操作符而不是Object.assign方法来浅拷贝对象。使用rest操作符获得一个去除某些属性新的对象。
// 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 }

数组Arrays

// bad
const items = new Array(); // good
const items = [];
  • 使用Array#push方法添加元素
const someStack = [];

// bad
someStack[someStack.length] = 'abracadabra'; // good
someStack.push('abracadabra');
  • 使用...操作拷贝数组
// bad
const len = items.length;
const itemsCopy = [];
let i; for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
} // good
const itemsCopy = [...items];
  • 转换array-like对象为array时,使用...而不是Array.from
const foo = document.querySelectorAll('.foo');

// good
const nodes = Array.from(foo); // best
const nodes = [...foo];
  • 对数组进行map时,使用Array.from替代...。因为前者的方式能避免创建中间数组
// bad
const baz = [...foo].map(bar); // good
const baz = Array.from(foo, bar);
  • 在数组遍历处理的回调函数中,使用返回语句。当回调函数中函数体只有单条语句且不会产生副作用时,可以省略return。eslint: array-callback-return
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
}); // good
[1, 2, 3].map(x => x + 1); // bad - no returned value means `memo` becomes undefined after the first iteration
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item);
memo[index] = flatten;
}); // good
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item);
memo[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;
});
  • 当数组有多行时,在开始和结束符号均换行
// bad
const arr = [
[0, 1], [2, 3], [4, 5],
]; const objectInArray = [{
id: 1,
}, {
id: 2,
}]; const numberInArray = [
1, 2,
]; // good
const arr = [[0, 1], [2, 3], [4, 5]]; const objectInArray = [
{
id: 1,
},
{
id: 2,
},
]; const numberInArray = [
1,
2,
];

解构Destructuring

why?解构可以减少临时变量的定义

// 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}`;
}
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1]; // good
const [first, second] = arr;
  • 当返回多个值时,使用对象解构方式,而不是数组解构

why?当添加字段或者顺序发生变化时,不依赖于位置

// 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

  • 字符串使用单引号' ' eslint: quotes
// bad
const name = "Capt. Janeway"; // bad - template literals should contain interpolation or newlines
const name = `Capt. Janeway`; // good
const name = 'Capt. Janeway';
  • 字符串超过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.';

why?模板字符串可读性更强,语法更简洁

// 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}?`;
}

why?影响可读性

// bad
const foo = '\'this\' \i\s \"quoted\"'; // good
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;

函数Functions

  • ?使用命名函数表达式,而不是函数声明 eslint: func-style

函数声明的方式存在提升,即:无论在哪里声明,效果等同于在函数顶部声明,只要在同一个作用域范围,就视为已经声明,哪怕在声明前就使用,也不会报错。

如果使用函数声明方式定义函数,会影响可读性和可维护性。当函数足够大或者复杂时,对阅读其余代码造成困扰。

// bad
function foo() {
// ...
} // bad
const foo = function () {
// ...
}; // good
// lexical name distinguished from the variable-referenced invocation(s)
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
  • 要求 IIFE 使用括号括起来 eslint: wrap-iife
// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
  • ?Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. eslint: no-loop-func
  • Note: ECMA-262 defines a block

    as a list of statements. A function declaration is not a statement. 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.');
};
}
  • 不要命名参数为arguments。
// bad
function foo(name, options, arguments) {
// ...
} // good
function foo(name, options, args) {
// ...
}
  • 选择缺省参数的方式,避免修改参数
// 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 = {}) {
// ...
}
  • 避免缺省参数带来的副作用
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
  • 将缺省参数放置最后
// bad
function handleThings(opts = {}, name) {
// ...
} // good
function handleThings(name, opts = {}) {
// ...
}
  • 不要使用Function构造函数创建函数 eslint: no-new-func
// bad
var add = new Function('a', 'b', 'return a + b'); // still bad
var subtract = Function('a', 'b', 'return a - b');
// bad
const f = function(){};
const g = function (){};
const h = function() {}; // good
const x = function () {};
const y = function a() {};

why?修改作为参数传入的对象,容易引发不预料的副作用

// bad
function f1(obj) {
obj.key = 1;
} // good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}

why?对参数重新赋值会引发不可预料的行为,特别是访问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) {
// ...
}

why?更简洁

// bad
const x = [1, 2, 3, 4, 5];
console.log.apply(console, x); // good
const x = [1, 2, 3, 4, 5];
console.log(...x); // bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); // good
new Date(...[2016, 8, 5]);
  • 函数签名或者函数调用占多行时,每一个参数占一行,并且最后一个参数带逗号结束
// bad
function foo(bar,
baz,
quux) {
// ...
} // good
function foo(
bar,
baz,
quux,
) {
// ...
} // bad
console.log(foo,
bar,
baz); // good
console.log(
foo,
bar,
baz,
);

箭头函数Arrow Functions

why?语法更简洁,并且this更符合预期

如果函数逻辑相当复杂,应当使用命名函数

// 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;
});
  • 如果函数体只有一条语句,且该语句不会产生副作用。使用简写方式,隐式返回;或者使用完整写法,显式return。

    eslint: arrow-parens, arrow-body-style
// 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,
})); // No implicit return with side effects
function foo(callback) {
const val = callback();
if (val === true) {
// Do something if callback returns true
}
} let bool = false; // bad
foo(() => bool = true); // good
foo(() => {
bool = true;
});
  • 当表达式占多行时,使用括号括起来增强可读性

why?函数开头和结束更明确

// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
); // good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
  • 如果函数只有一个参数,省略括号,省略花括号。否则,一直使用完整写法,保持一致性。eslint: arrow-parens
// 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;
});
// 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;

类和构造函数 Classes & Constructors

  • 使用class,避免直接操作prototype

why?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;
}
}
  • 使用extends 继承

why?这是内置支持的继承原型方法,同时不影响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];
}
}
  • 通过return 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);
  • 自定义toString方法
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
} getName() {
return this.name;
} toString() {
return `Jedi - ${this.getName()}`;
}
}
// 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';
}
}

why?后者会静默覆盖前者

// bad
class Foo {
bar() { return 1; }
bar() { return 2; }
} // good
class Foo {
bar() { return 1; }
} // good
class Foo {
bar() { return 2; }
}

模块Modules

  • 使用modules(import/export)

why?modules是未来的趋势

// 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;
  • 不要使用*import
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good
import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 不要从import直接export

why?尽管单行的方式更简洁,但是一行import一行export代码一致性更好

// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide'; // good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;

why?维护性更好

// 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';
// bad
let foo = 3;
export { foo }; // good
const foo = 3;
export { foo };

why?可读性和可维护性更强

// bad
export function foo() {} // good
export default function foo() {}

why?因为import行为会提升,因此保持它们放在最上面,防止产生令人奇怪的行为

// bad
import foo from 'foo';
foo.init(); import bar from 'bar'; // good
import foo from 'foo';
import bar from 'bar'; foo.init();
  • 多行import,像多行数组和多行对象字面量一样写

why?缩进一致,尾行逗号结束

// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
// 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';

迭代器与生成器 Iterators and Generators

why?有利于函数式编程

使用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; // 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);
  • 暂时不要使用生成器

why?es5编译得不太好

why? 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* () {
// ...
};

属性Properties

const luke = {
jedi: true,
age: 28,
}; // bad
const isJedi = luke['jedi']; // good
const isJedi = luke.jedi;
  • 通过变量访问对象属性时,使用[]
const luke = {
jedi: true,
age: 28,
}; function getProp(prop) {
return luke[prop];
} const isJedi = getProp('jedi');
// bad
const binary = Math.pow(2, 10); // good
const binary = 2 ** 10;

变量

  • 总是使用const或者let定义变量。避免污染全局命名空间。eslint: no-undef prefer-const
// bad
superPower = new SuperPower(); // good
const superPower = new SuperPower();
  • 一个const或者let定义一个变量 eslint: one-var

why? 更方便添加变量定义,方便debug

// 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';
  • const放在一起,let放在一起

why?更容易区分

// 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;
  • 需要时再定义变量,并且放在合理的地方

why?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;
}
  • 不要嵌套变量赋值

why?嵌套变量赋值隐式创建全局变量

// 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 // the same applies for `const`

why?因为一元操作符 ++ 和 -- 会自动添加分号,不同的空白可能会改变源代码的语义。

// 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;

提升Hoisting

  • var声明变量会提升到作用域顶部,赋值操作不会提升。const和let没有类似的提升行为,因此要明白typeof操作不再安全。
// we know this wouldn’t work (assuming there
// is no notDefined global variable)
function example() {
console.log(notDefined); // => throws a ReferenceError
} // creating a variable declaration after you
// reference the variable will work due to
// variable hoisting. Note: the assignment
// value of `true` is not hoisted.
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
} // the interpreter is hoisting the variable
// declaration to the top of the scope,
// which means our example could be rewritten as:
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
} // using const and let
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
}
  • 匿名函数表达式会提升变量,但是不会提升函数赋值
function example() {
console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function () {
console.log('anonymous function expression');
};
}
  • 命名函数表达式会提升变量,但是不是函数名或者函数体
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');
};
}
  • 函数定义提升函数名称和函数体
function example() {
superPower(); // => Flying function superPower() {
console.log('Flying');
}
}

比较操作符和等于操作Comparison Operators & Equality

  • 使用=、! eslint: eqeqeq
  • 条件语句比如if,判断表达式值时,会强制执行ToBoolean,遵循以下规则:
  1. Object:true
  2. Undefined:false
  3. Null:false
  4. Booleans 取决于本身的值
  5. Number:+0,-0,NaN为false,其余为true
  6. String:‘’空字符串为false,其余为true
if ([0] && []) {
// true
// an array (even an empty one) is an object, objects will evaluate to true
}
  • 使用简写方式判断,但是对strings和numbers比较使用显式判断
// bad
if (isValid === true) {
// ...
} // good
if (isValid) {
// ...
} // bad
if (name) {
// ...
} // good
if (name !== '') {
// ...
} // bad
if (collection.length) {
// ...
} // good
if (collection.length > 0) {
// ...
}
// 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 {}
}
}
// 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;
// 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;
// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0; // bad
const bar = a ** b - 5 % d; // bad
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;

块Blocks

  • ?使用{}
// 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;
}
// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
} // good
if (test) {
thing1();
thing2();
} else {
thing3();
}
  • 禁止 if 语句中 return 语句之后有 else 块 eslint: no-else-return
// 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;
}
}
return z;
}

控制语句 Control Statements

  • 对于控制语句中的条件表达式太长的情况,应当换行处理,把操作符放在每行的开头。
// 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();
}

注释

  • 使用/** ... */多行注释
// 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;
}
  • 单行注释使用//。//放在代码的上面一行,同时,如果不是区块的开头,注释上方空行处理。
// 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;
}
// 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;
}
  • 使用FIXME或者TODO帮助开发者明白问题。
  • 使用 // FIXME 注释问题
class Calculator extends Abacus {
constructor() {
super(); // FIXME: shouldn’t use a global here
total = 0;
}
}
  • 使用 // TODO 注释问题的解决方案
class Calculator extends Abacus {
constructor() {
super(); // TODO: total should be configurable by an options param
this.total = 0;
}
}

空白Whitespace eslint: indent

  • 使用2个空格
// bad
function foo() {
∙∙∙∙let name;
} // bad
function bar() {
∙let name;
} // good
function baz() {
∙∙let name;
}
// 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',
});
// bad
if(isJedi) {
fight ();
} // good
if (isJedi) {
fight();
} // bad
function fight () {
console.log ('Swooosh!');
} // good
function fight() {
console.log('Swooosh!');
}
// bad
const x=y+5; // good
const x = y + 5;
  • 文件结束时换一行 . eslint: eol-last
// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;
// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵

// good
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵
// 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);
  • 在每个代码块的结束位置和下一语句的开头空行
// 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;
// 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);
}
// bad
function bar( foo ) {
return foo;
} // good
function bar(foo) {
return foo;
} // bad
if ( foo ) {
console.log(foo);
} // good
if (foo) {
console.log(foo);
}
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]); // good
const foo = [1, 2, 3];
console.log(foo[0]);
// bad
const foo = {clark: 'kent'}; // good
const foo = { clark: 'kent' };
  • 强制一行的最大长度100 eslint: max-len
// bad
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); // good
const foo = jsonData
&& jsonData.foo
&& jsonData.foo.bar
&& jsonData.foo.bar.baz
&& jsonData.foo.bar.baz.quux
&& jsonData.foo.bar.baz.quux.xyzzy; // good
$.ajax({
method: 'POST',
url: 'https://airbnb.com/',
data: { name: 'John' },
})
.done(() => console.log('Congratulations!'))
.fail(() => console.log('You have failed this city.'));
// 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',
};

why?减少无意义的git diffs

// 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
);

分号 Semicolons

// bad
(function () {
const name = 'Skywalker'
return name
})() // good
(function () {
const name = 'Skywalker';
return name;
}()); // good, but legacy (guards against the function becoming an argument when two files with IIFEs are concatenated)
;((() => {
const name = 'Skywalker';
return name;
})());

类型转换和强制类型转换

  • ?Perform type coercion at the beginning of the statement.
  • Strings:
// => this.reviewScore = 9;

// 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);
  • Numbers:使用Number和parseInt eslint: radix
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);
  • 不管因为什么原因你必须使用位移操作,增加注释说明
// 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;
  • 小心位移操作。数值能表示64位数,但是位移操作只能返回32位整数。
2147483647 >> 0; // => 2147483647
2147483648 >> 0; // => -2147483648
2147483649 >> 0; // => -2147483647
  • Boolean
const age = 0;

// bad
const hasAge = new Boolean(age); // good
const hasAge = Boolean(age); // best
const hasAge = !!age;

命名约定Naming Conventions

  • 不要使用一个字母命名 eslint: id-length
// bad
function q() {
// ...
} // good
function query() {
// ...
}
  • 使用驼峰命名对象、函数和实例 eslint: camelcase
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {} // good
const thisIsMyObject = {};
function thisIsMyFunction() {}
  • 使用PascalCase命名构造函数或者Class eslint: new-cap
// 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',
});

why?JavaScript没有私有属性或者私有方法的概念。使用下划线容易造成误解。

// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
this._firstName = 'Panda'; // good
this.firstName = 'Panda';
  • 不要保存this的引用。使用箭头函数或者 Function#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);
};
}
  • 文件名称与export default 相符
// 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
  • 当export default一个函数时,使用驼峰命名。文件名也必须一致
function makeStyleGuide() {
// ...
} export default makeStyleGuide;
  • 当export constructor / class / singleton / function library / bare object,使用PascalCase命名方式
const AirbnbStyleGuide = {
es6: {
},
}; export default AirbnbStyleGuide;
  • 缩略词必须首字母大写,或者全部小写
// 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 = [
// ...
];

访问器Accessors

  • 不需要属性的访问器函数
  • 不要使用getters/setters,会产生不可预期的副作用,而且难以维护和测试。
// bad
class Dragon {
get age() {
// ...
} set age(value) {
// ...
}
} // good
class Dragon {
getAge() {
// ...
} setAge(value) {
// ...
}
}
  • 如果属性或者方法是boolean,使用 isVal() 或者 hasVal()
// bad
if (!dragon.age()) {
return false;
} // good
if (!dragon.hasAge()) {
return false;
}
  • 创建 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];
}
}

事件Events

  • 对于event的handler传递数据时,使用 { key:value... } 方式。这样,当需要传递更多信息时,不需要更改每个handler签名
// 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

  • jQuery对象变量使用 $ 区分
// bad
const sidebar = $('.sidebar'); // good
const $sidebar = $('.sidebar'); // good
const $sidebarBtn = $('.sidebar-btn');
  • 缓存jQuery lookups
// bad
function setSidebar() {
$('.sidebar').hide(); // ... $('.sidebar').css({
'background-color': 'pink',
});
} // good
function setSidebar() {
const $sidebar = $('.sidebar');
$sidebar.hide(); // ... $sidebar.css({
'background-color': 'pink',
});
}
  • Dom查找,使用级联 $('.sidebar ul') 或者父子 $('.sidebar > ul')
  • 指定范围进行find
// bad
$('ul', '.sidebar').hide(); // bad
$('.sidebar').find('ul').hide(); // good
$('.sidebar ul').hide(); // good
$('.sidebar > ul').hide(); // good
$sidebar.find('ul').hide();

Airbnb JavaScript代码规范(完整)的更多相关文章

  1. Airbnb Javascript 代码规范重要点总结es6

    中文原地址 1.对所有的引用使用 const 而非 var.这能确保你无法对引用重复赋值. 当需要变动引用时,使用let. const和let都是块级作用域. 2.创建对象的方式: const ite ...

  2. JavaScript 代码规范

    所有的 JavaScript 项目适用同一种规范. JavaScript 代码规范 代码规范通常包括以下几个方面: 变量和函数的命名规则 空格,缩进,注释的使用规则. 其他常用规范-- 规范的代码可以 ...

  3. 5 JSON&与JavaScript转换&JavaScript:void(0)&JavaScript代码规范

    JSON:JavaScript Object Notation   JS对象简谱 一种轻量级的数据交换格式,用于存储和传输数据的格式,通常用于服务端向网页传递数据 是独立的语言,易于理解 JSON语法 ...

  4. js基石之---易读、易复用、易重构的 JavaScript 代码规范

    易读.易复用.易重构的 JavaScript 代码规范 1.变量命名规范有意义 Bad: const yyyymmdstr = moment().format("YYYY/MM/DD&quo ...

  5. JavaScript代码规范和性能整理

    性能 Js在性能方面有多要注意的地方: 避免全局查找 Js性能优化最重要的就是注意全局查找,因为作用域的查找是先找局部作用域在没有找到之后在去上一级作用域查找直到全局作用域,所以全局作用域查找的性能消 ...

  6. 最流行的JavaScript代码规范

    什么是最佳的JavaScript代码编程规范?这可能是一个众口难调的问题.那么,不妨换个问题,什么代码规范最流行? sideeffect.kr通过分析GitHub上托管的开源代码,得出了一些有趣的结果 ...

  7. javascript代码规范 [转]

    原文:http://www.css88.com/archives/5366 全局命名空间污染与 IIFE 总是将代码包裹成一个 IIFE(Immediately-Invoked Function Ex ...

  8. JavaScript代码规范

    变量名:驼峰命名法(首单词小写,后面每个单词首字母大写) firstName = "John"; lastName = "Doe"; price = 19.90 ...

  9. 谷歌与Airbnb的JS代码规范

    谷歌JS代码规范 规范代码原因:代码规范是为了保持源代码编写模式一致,便于维护代码,可读性高. 1.使用空格代替tab 规范随后指出应该使用2个,而不是4个空格带实现缩进.(除了每一行的终止符序列,A ...

随机推荐

  1. Entity Framework (EF) Core学习笔记 1

    1. Entity Framework (EF) Core 是轻量化.可扩展.开源和跨平台的数据访问技术,它还是一 种对象关系映射器 (ORM),它使 .NET 开发人员能够使用面向对象的思想处理数据 ...

  2. log4net GetLogger(source).IsInfoEnabled = false

    GetLogger(source).IsInfoEnabled = false解决办法 在.net core中需要把log4net.config放到 ITCP.Web\ITCP.Web\obj\Rel ...

  3. CF1463-A. Dungeon

    题意: 你面前有三个怪物,他们分别有a, b, c点血量.现在你可以指定一个怪物,用大炮向他们射击,之后该怪物就会掉一滴血.每七次射击就会使得炮弹威力加强一次,即第7, 14, 21次射击的时候炮弹威 ...

  4. PHP的常用函数 持续更新

    PHP的常用函数 前言: 由于害怕遗忘,故在此记录下常用的php函数,以便复习 1 define函数 作用:定义常量 用法 <?php define('a',100); ?> 2 intv ...

  5. CGI & FastCGI 协议

    目录 CGI 是什么 CGI 特点 CGI 的流程 FastCGI 是什么 CGI & FastCGI(转载) 推荐Blog: CGI是什么,FastCGI是什么 CGI 是什么 公共网关接口 ...

  6. 接口测试框架Requests

    目录 Requests Requests安装 Requests常见接口请求方法构造 请求目标构造 header构造 cookie 构造请求体 Get Query请求 Form请求参数 JSON请求体构 ...

  7. 网络安全知识--PHP代码审计/Web For Pantesters 的 SQL injection

    SQL 注入一般流程 判断有无注入 单引号判断: ?name=root' 对应语句 select * from table where name='root'' 不符合语法规范,报错,说明有注入 an ...

  8. l2-002 链表去重 (未解决)

    L2-002. 链表去重 时间限制 300 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 给定一个带整数键值的单链表L,本题要求你编写程序,删除 ...

  9. Linux 驱动框架---模块参数

    Linux 模块的参数 通过在内核模块中定义模块参数从而可以在安装模块时通过insmod module_name paramname=param形式给模块传递参数.如果安装模块是传参数则将使用模块内定 ...

  10. 动态规划算法 All In One

    动态规划算法 All In One dynamic programming leetcode https://leetcode.com/tag/dynamic-programming/ https:/ ...