JavaScript最合理的方法 A mostly reasonable approach to JavaScriptjavascript
注意: 這個指南假定你正在使用Babel, 而且須要你使用或等效的使用babel-preset-airbnb。 同時假定你在你的應用裏安裝了帶有或等效的airbnb-browser-shims的css
shims/polyfills
html
string
number
boolean
null
undefined
symbol
const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
複製代碼
object
array
function
const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
複製代碼
2.1 全部的賦值都用const
,避免使用var
. eslint: prefer-const
, no-const-assign
java
Why? 由於這個確保你不會改變你的初始值,重複引用會致使bug和代碼難以理解node
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
複製代碼
2.2 若是你必定要對參數從新賦值,那就用let
,而不是var
. eslint: no-var
jquery
Why? 由於
let
是塊級做用域,而var
是函數級做用域webpack
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
複製代碼
2.3 注意: let
、const
都是塊級做用域git
// const 和 let 都只存在於它定義的那個塊級做用域
{
let a = 1;
const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
複製代碼
3.1 使用字面值建立對象. eslint: no-new-object
程序員
// bad
const item = new Object();
// good
const item = {};
複製代碼
3.2 當建立一個帶有動態屬性名的對象時,用計算後屬性名es6
Why? 這可使你將定義的全部屬性放在對象的一個地方.
function getKey(k) {
return `a key named ${k}`;
}
// bad
const obj = {
id: 5,
name: 'San Francisco',
};
obj[getKey('enabled')] = true;
// good getKey('enabled')是動態屬性名
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
};
複製代碼
3.3 用對象方法簡寫. eslint: object-shorthand
// 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 用屬性值縮寫. eslint: object-shorthand
Why? 這樣寫的更少且更可讀
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
複製代碼
3.5 將你的全部縮寫放在對象聲明的開始.
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,
};
複製代碼
3.6 只對那些無效的標示使用引號 ''
. eslint: quote-props
Why? 一般咱們認爲這種方式主觀上易讀。他優化了代碼高亮,而且頁更容易被許多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
。
Why? 在一些有問題的對象上, 這些方法可能會被屏蔽掉 - 如:
{ hasOwnProperty: false }
- 或這是一個空對象Object.create(null)
// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // 在模塊做用內作一次緩存
/* or */
import has from 'has'; // https://www.npmjs.com/package/has
// ...
console.log(has.call(object, key));
複製代碼
...
運算符],而不是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 es6擴展運算符 ...
const original = { a: 1, b: 2 };
// 淺拷貝
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
// rest 賦值運算符
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
複製代碼
4.1 用字面量賦值。 eslint: no-array-constructor
// bad
const items = new Array();
// good
const items = [];
複製代碼
4.2 用Array#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 += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
複製代碼
4.4 用 ...
運算符而不是Array.from
來將一個可迭代的對象轉換成數組。
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
複製代碼
4.5 用 Array.from
去將一個類數組對象轉成一個數組。
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// bad
const arr = Array.prototype.slice.call(arrLike);
// good
const arr = Array.from(arrLike);
複製代碼
4.6 用 Array.from
而不是 ...
運算符去作map遍歷。 由於這樣能夠避免建立一個臨時數組。
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
複製代碼
4.7 在數組方法的回調函數中使用 return 語句。 若是函數體由一條返回一個表達式的語句組成, 而且這個表達式沒有反作用, 這個時候能夠忽略return,詳見 8.2. 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 - 沒有返回值, 由於在第一次迭代後acc 就變成undefined了
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[index] = flatten;
});
// good
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[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;
});
複製代碼
4.8 若是一個數組有不少行,在數組的 [
後和 ]
前斷行。 請看下面示例
// 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,
];
複製代碼
5.1 用對象的解構賦值來獲取和使用對象某個或多個屬性值。 eslint: prefer-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}`;
}
複製代碼
5.2 用數組解構.
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
複製代碼
5.3 多個返回值用對象的解構,而不是數據解構。
Why? 你能夠在後期添加新的屬性或者變換變量的順序而不會打破原有的調用
// bad
function processInput(input) {
// 而後就是見證奇蹟的時刻
return [left, right, top, bottom];
}
// 調用者須要想想返回值的順序
const [left, __, top] = processInput(input);
// good
function processInput(input) {
// oops, 奇蹟又發生了
return { left, right, top, bottom };
}
// 調用者只須要選擇他想用的值就行了
const { left, top } = processInput(input);
複製代碼
6.1 對string用單引號 ''
。 eslint: quotes
// bad
const name = "Capt. Janeway";
// bad - 樣例應該包含插入文字或換行
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';
複製代碼
6.2 超過100個字符的字符串不該該用string串聯成多行。
Why? 被折斷的字符串工做起來是糟糕的並且使得代碼更不易被搜索。
// 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
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}?`;
}
複製代碼
6.5 不要使用沒必要要的轉義字符。eslint: no-useless-escape
Why? 反斜線可讀性差,因此他們只在必須使用時纔出現哦
// bad
const foo = '\'this\' \i\s \"quoted\"';
// good
const foo = '\'this\' is "quoted"';
//best
const foo = `my name is '${name}'`;
複製代碼
7.1 用命名函數表達式而不是函數聲明。eslint: func-style
函數表達式: const func = function () {}
函數聲明: function func() {}
Why? 函數聲明時做用域被提早了,這意味着在一個文件裏函數很容易(太容易了)在其定義以前被引用。這樣傷害了代碼可讀性和可維護性。若是你發現一個函數又大又複雜,這個函數妨礙這個文件其餘部分的理解性,這可能就是時候把這個函數單獨抽成一個模塊了。別忘了給表達式顯示的命名,不用管這個名字是否是由一個肯定的變量推斷出來的,這消除了由匿名函數在錯誤調用棧產生的全部假設,這在現代瀏覽器和相似babel編譯器中很常見 (Discussion)
Why? 這一段還不理解這種錯誤發生的場景,因此只能直譯過來了, 另附原文 Why? Function declarations are hoisted, which means that it’s easy - too easy - to reference the function before it is defined in the file. This harms readability and maintainability. If you find that a function’s definition is large or complex enough that it is interfering with understanding the rest of the file, then perhaps it’s time to extract it to its own module! Don’t forget to explicitly name the expression, regardless of whether or not the name is inferred from the containing variable (which is often the case in modern browsers or when using compilers such as Babel). This eliminates any assumptions made about the Error’s call stack. (Discussion)
// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
// lexical name distinguished from the variable-referenced invocation(s)
// 函數表達式名和聲明的函數名是不同的
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
複製代碼
7.2 把當即執行函數包裹在圓括號裏。 eslint: wrap-iife
Why? immediately invoked function expression = IIFE Why? 一個當即調用的函數表達式是一個單元 - 把它和他的調用者(圓括號)包裹起來,在括號中能夠清晰的地表達這些。 Why? 注意:在模塊化世界裏,你幾乎用不着 IIFE
// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
複製代碼
no-loop-func
】 eslint: no-loop-func
7.4 Note: 在ECMA-262中 [塊 block
] 的定義是: 一系列的語句; 可是函數聲明不是一個語句。 函數表達式是一個語句。
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
複製代碼
7.5 不要用arguments
命名參數。他的優先級高於每一個函數做用域自帶的 arguments
對象, 這會致使函數自帶的 arguments
值被覆蓋
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
複製代碼
7.6 不要使用arguments
,用rest語法...
代替。 eslint: prefer-rest-params
Why?
...
明確你想用那個參數。並且rest參數是真數組,而不是相似數組的arguments
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
複製代碼
7.7 用默認參數語法而不是在函數裏對參數從新賦值。
// really bad
function handleThings(opts) {
// 不, 咱們不應改arguments
// 第二: 若是 opts 的值爲 false, 它會被賦值爲 {}
// 雖然你想這麼寫, 可是這個會帶來一些細微的bug
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
複製代碼
7.8 默認參數避免反作用
Why? 他會使人疑惑不解, 好比下面這個, a到底等於幾, 這個須要想一下。
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
Why? 以這種方式建立函數將相似於字符串 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
Why? 統一性好,並且在你添加/刪除一個名字的時候不須要添加/刪除空格
// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};
複製代碼
7.12 不要改參數. eslint: no-param-reassign
Why? 操做參數對象對原始調用者會致使意想不到的反作用。 就是不要改參數的數據結構,保留參數原始值和數據結構。
// bad
function f1(obj) {
obj.key = 1;
};
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};
複製代碼
7.13 不要對參數從新賦值。 eslint: no-param-reassign
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) {
// ...
}
複製代碼
7.14 用spread
操做符...
去調用多變的函數更好。 eslint: prefer-spread
Why? 這樣更清晰,你沒必要提供上下文,並且你不能輕易地用
apply
來組成new
// 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]);
複製代碼
7.15 調用或者書寫一個包含多個參數的函數應該像這個指南里的其餘多行代碼寫法同樣: 每行值包含一個參數,每行逗號結尾。
// bad
function foo(bar, baz, quux) {
// ...
}
// good 縮進不要太過度
function foo( bar, baz, quux, ) {
// ...
}
// bad
console.log(foo,
bar,
baz);
// good
console.log(
foo,
bar,
baz,
);
複製代碼
8.1 當你必定要用函數表達式(在回調函數裏)的時候就用箭頭表達式吧。 eslint: prefer-arrow-callback
, arrow-spacing
Why? 他建立了一個
this
的當前執行上下文的函數的版本,這一般就是你想要的;並且箭頭函數是更簡潔的語法
Why? 何時不用箭頭函數: 若是你有一個至關複雜的函數,你可能會把這個邏輯移出到他本身的函數聲明裏。
// bad
[1, 2, 3].map(function (x) {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
複製代碼
8.2 若是函數體由一個沒有反作用的表達式語句組成,刪除大括號和return。不然,繼續用大括號和 return
語句。 eslint: arrow-parens
, arrow-body-style
Why? 語法糖,當多個函數鏈在一塊兒的時候好讀
// 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
}));
// 表達式有反作用就不要用隱式return
function foo(callback) {
const val = callback();
if (val === true) {
// Do something if callback returns true
}
}
let bool = false;
// bad
// 這種狀況會return bool = true, 很差
foo(() => bool = true);
// good
foo(() => {
bool = true;
});
複製代碼
8.3 萬一表達式涉及多行,把他包裹在圓括號裏更可讀。
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
)
));
複製代碼
8.4 若是你的函數只有一個參數而且函數體沒有大括號,就刪除圓括號。不然,參數老是放在圓括號裏。 注意: 一直用圓括號也是沒問題,只須要配置 「always」 option for eslint. eslint: arrow-parens
Why? 這樣少一些混亂, 其實沒啥語法上的講究,就保持一個風格。
// bad
[1, 2, 3].map((x) => x * x);
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].map(number => (
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
));
// bad
[1, 2, 3].map(x => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
複製代碼
8.5 避免箭頭函數(=>
)和比較操做符(<=, >=
)混淆. eslint: no-confusing-arrow
// bad
const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize;
// bad
const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;
// good
const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);
// good
const itemHeight = (item) => {
const { height, largeSize, smallSize } = item;
return height <= 256 ? largeSize : smallSize;
};
複製代碼
8.6 在隱式return中強制約束函數體的位置, 就寫在箭頭後面。 eslint: implicit-arrow-linebreak
// bad
(foo) =>
bar;
(foo) =>
(bar);
// good
(foo) => bar;
(foo) => (bar);
(foo) => (
bar
)
複製代碼
9.1 經常使用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;
}
}
複製代碼
9.2 用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];
}
}
複製代碼
9.3 方法能夠返回this
來實現方法鏈
// bad
Jedi.prototype.jump = function () {
this.jumping = true;
return true;
};
Jedi.prototype.setHeight = function (height) {
this.height = height;
};
const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined
// good
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this;
}
}
const luke = new Jedi();
luke.jump()
.setHeight(20);
複製代碼
9.4 寫一個定製的toString()方法是能夠的,只要保證它是能夠正常工做且沒有反作用的
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
複製代碼
9.5 若是沒有具體說明,類有默認的構造方法。一個空的構造函數或只是表明父類的構造函數是不須要寫的。 eslint: no-useless-constructor
// bad
class Jedi {
constructor() {}
getName() {
return this.name;
}
}
// bad
class Rey extends Jedi {
// 這種構造函數是不須要寫的
constructor(...args) {
super(...args);
}
}
// good
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
複製代碼
9.6 避免重複類成員。 eslint: no-dupe-class-members
Why? 重複類成員會默默的執行最後一個 —— 重複自己也是一個bug
// bad
class Foo {
bar() { return 1; }
bar() { return 2; }
}
// good
class Foo {
bar() { return 1; }
}
// good
class Foo {
bar() { return 2; }
}
複製代碼
this
或被作成靜態方法。做爲一個實例方法應該代表它根據接收者的屬性有不一樣的行爲。eslint: class-methods-use-this
```javascript
// bad
class Foo {
bar() {
console.log('bar');
}
}
// good - this 被使用了
class Foo {
bar() {
console.log(this.bar);
}
}
// good - constructor 不必定要使用this
class Foo {
constructor() {
// ...
}
}
// good - 靜態方法不須要使用 this
class Foo {
static bar() {
console.log('bar');
}
}
```
複製代碼
10.1 用(import
/export
) 模塊而不是無標準的模塊系統。你能夠隨時轉到你喜歡的模塊系統。
Why? 模塊化是將來,讓咱們如今就開啓將來吧。
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
複製代碼
10.2 不要用import通配符, 就是 *
這種方式
Why? 這確保你有單個默認的導出
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
複製代碼
10.3 不要直接從import中直接export
Why? 雖然一行是簡潔的,有一個明確的方式進口和一個明確的出口方式來保證一致性。
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
複製代碼
eslint: no-duplicate-imports
> Why? 從同一個路徑下import多行會使代碼難以維護
```javascript
// 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';
```
複製代碼
eslint: import/no-mutable-exports
> Why? 變化一般都是須要避免,特別是當你要輸出可變的綁定。雖然在某些場景下可能須要這種技術,但總的來講應該導出常量。
```javascript
// bad
let foo = 3;
export { foo }
// good
const foo = 3;
export { foo }
```
複製代碼
export default
更好。eslint: import/prefer-default-export
> Why? 鼓勵使用更多文件,每一個文件只作一件事情並導出,這樣可讀性和可維護性更好。
```javascript
// bad
export function foo() {}
// good
export default function foo() {}
```
複製代碼
import
放在其餘全部語句以前。eslint: import/first
> Why? 讓import
放在最前面防止意外行爲。
```javascript
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
```
複製代碼
10.8 多行import應該縮進,就像多行數組和對象字面量
Why? 花括號與樣式指南中每一個其餘花括號塊遵循相同的縮進規則,逗號也是。
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
複製代碼
eslint: import/no-webpack-loader-syntax
> Why? 一旦用Webpack語法在import裏會把代碼耦合到模塊綁定器。最好是在webpack.config.js
裏寫webpack loader語法
```javascript
// 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';
```
複製代碼
11.1 不要用遍歷器。用JavaScript高級函數代替for-in
、 for-of
。 eslint: no-iterator
no-restricted-syntax
Why? 這強調了咱們不可變的規則。 處理返回值的純函數比反作用更容易。
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);
複製代碼
11.2 如今不要用generator
Why? 它在es5上支持的很差
11.3 若是你必定要用,或者你忽略咱們的建議, 請確保它們的函數簽名空格是得當的。 eslint: generator-star-spacing
Why?
function
和*
是同一律念關鍵字 -*
不是function
的修飾符,function*
是一個和function
不同的獨特結構
// bad
function * foo() {
// ...
}
// bad
const bar = function * () {
// ...
}
// bad
const baz = function *() {
// ...
}
// bad
const quux = function*() {
// ...
}
// bad
function*foo() {
// ...
}
// bad
function *foo() {
// ...
}
// very bad
function * foo() {
// ...
}
// very bad
const wat = function * () {
// ...
}
// good
function* foo() {
// ...
}
// good
const foo = function* () {
// ...
}
複製代碼
12.1 訪問屬性時使用點符號. eslint: dot-notation
const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;
複製代碼
12.2 當獲取的屬性是變量時用方括號[]
取
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
複製代碼
12.3 作冪運算時用冪操做符 **
。 eslint: no-restricted-properties
.
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
複製代碼
13.1 用const
或let
聲明變量。不這樣作會致使全局變量。 咱們想要避免污染全局命名空間。首長這樣警告咱們。 eslint: no-undef
prefer-const
// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();
複製代碼
13.2 每一個變量都用一個 const
或 let
。 eslint: one-var
Why? 這種方式很容易去聲明新的變量,你不用去考慮把
;
調換成,
,或者引入一個只有標點的不一樣的變化。這種作法也能夠是你在調試的時候單步每一個聲明語句,而不是一下跳過全部聲明。
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
goSportsTeam = true;
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
複製代碼
13.3 const
放一塊兒,let
放一塊兒
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;
複製代碼
13.4 在你須要的地方聲明變量,可是要放在合理的位置
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;
}
複製代碼
13.5 不要使用連接變量分配。 eslint: no-multi-assign
Why? 連接變量分配建立隱式全局變量。
// bad
(function example() {
// JavaScript 將這一段解釋爲
// let a = ( b = ( c = 1 ) );
// let 只對變量 a 起做用; 變量 b 和 c 都變成了全局變量
let a = b = c = 1;
}());
console.log(a); // undefined
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // undefined
console.log(b); // undefined
console.log(c); // undefined
// `const` 也是如此
複製代碼
13.6 不要使用一元自增自減運算符(++
, --
). eslint no-plusplus
Why? 根據eslint文檔,一元增量和減量語句受到自動分號插入的影響,而且可能會致使應用程序中的值遞增或遞減的無聲錯誤。 使用
num + = 1
而不是num ++
或num ++
語句來表達你的值也是更有表現力的。 禁止一元增量和減量語句還會阻止您無心地預增/預減值,這也會致使程序出現意外行爲。
// bad
const array = [1, 2, 3];
let num = 1;
num++;
--num;
let sum = 0;
let truthyCount = 0;
for (let i = 0; i < array.length; i++) {
let value = array[i];
sum += value;
if (value) {
truthyCount++;
}
}
// good
const array = [1, 2, 3];
let num = 1;
num += 1;
num -= 1;
const sum = array.reduce((a, b) => a + b, 0);
const truthyCount = array.filter(Boolean).length;
複製代碼
13.7 在賦值的時候避免在 =
前/後換行。 若是你的賦值語句超出 max-len
, 那就用小括號把這個值包起來再換行。 eslint operator-linebreak
.
Why? 在
=
附近換行容易混淆這個賦值語句。
// bad
const foo =
superLongLongLongLongLongLongLongLongFunctionName();
// bad
const foo
= 'superLongLongLongLongLongLongLongLongString';
// good
const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
// good
const foo = 'superLongLongLongLongLongLongLongLongString';
複製代碼
13.8 不容許有未使用的變量。 eslint: no-unused-vars
Why? 一個聲明瞭但未使用的變量更像是因爲重構未完成產生的錯誤。這種在代碼中出現的變量會使閱讀者迷惑。
// bad
var some_unused_var = 42;
// 寫了沒用
var y = 10;
y = 5;
// 變量改了本身的值,也沒有用這個變量
var z = 0;
z = z + 1;
// 參數定義了但未使用
function getX(x, y) {
return x;
}
// good
function getXPlusY(x, y) {
return x + y;
}
var x = 1;
var y = a + 2;
alert(getXPlusY(x, y));
// 'type' 即便沒有使用也能夠能夠被忽略, 由於這個有一個 rest 取值的屬性。
// 這是從對象中抽取一個忽略特殊字段的對象的一種形式
var { type, ...coords } = data;
// 'coords' 如今就是一個沒有 'type' 屬性的 'data' 對象
複製代碼
14.1 var
聲明會被提早到他的做用域的最前面,它分配的值尚未提早。const
和 let
被賦予了新的調用概念時效區 —— Temporal Dead Zones (TDZ)。 重要的是要知道爲何 typeof再也不安全.
// 咱們知道這個不會工做,假設沒有定義全局的notDefined
function example() {
console.log(notDefined); // => throws a ReferenceError
}
// 在你引用的地方以後聲明一個變量,他會正常輸出是由於變量做用域上升。
// 注意: declaredButNotAssigned的值沒有上升
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
// 解釋器把變量聲明提高到做用域最前面,
// 能夠重寫成以下例子, 兩者意義相同
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
// 用 const, let就不同了
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
}
複製代碼
14.2 匿名函數表達式和 var
狀況相同
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () {
console.log('anonymous function expression');
};
}
複製代碼
14.3 已命名函數表達式提高他的變量名,不是函數名或函數體
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
superPower(); // => ReferenceError superPower is not defined
var named = function superPower() {
console.log('Flying');
};
}
// 函數名和變量名同樣是也如此
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() {
console.log('named');
};
}
複製代碼
14.4 函數聲明則提高了函數名和函數體
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
複製代碼
15.2 條件語句如'if'語句使用強制`ToBoolean'抽象方法來評估它們的表達式,而且始終遵循如下簡單規則:
''
計算成 falseif ([0] && []) {
// true
// 數組(即便是空數組)是對象,對象會計算成true
}
複製代碼
15.3 布爾值用縮寫,而字符串和數字要明確比較對象
// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}
複製代碼
15.5 在case
和default
分句裏用大括號建立一塊包含語法聲明的區域(e.g. let
, const
, function
, and class
). eslint rules: no-case-declarations
.
Why? 語法聲明在整個
switch
的代碼塊裏均可見,可是隻有當其被分配後纔會初始化,他的初始化時當這個case
被執行時才產生。 當多個case
分句試圖定義同一個事情時就出問題了
// bad
switch (foo) {
case 1:
let x = 1;
break;
case 2:
const y = 2;
break;
case 3:
function f() {
// ...
}
break;
default:
class C {}
}
// good
switch (foo) {
case 1: {
let x = 1;
break;
}
case 2: {
const y = 2;
break;
}
case 3: {
function f() {
// ...
}
break;
}
case 4:
bar();
break;
default: {
class C {}
}
}
複製代碼
15.6 三元表達式不該該嵌套,一般是單行表達式。
eslint rules: no-nested-ternary
.
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// better
const maybeNull = value1 > value2 ? 'baz' : null;
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const maybeNull = value1 > value2 ? 'baz' : null;
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
複製代碼
15.7 避免不須要的三元表達式
eslint rules: no-unneeded-ternary
.
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;
複製代碼
15.8 用圓括號來混合這些操做符。 只有當標準的算術運算符(+
, -
, *
, & /
), 而且它們的優先級顯而易見時,能夠不用圓括號括起來。 eslint: no-mixed-operators
Why? 這提升了可讀性,而且明確了開發者的意圖
// 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;
複製代碼
16.1 用大括號包裹多行代碼塊。 eslint: nonblock-statement-body-position
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function foo() { return false; }
// good
function bar() {
return false;
}
複製代碼
16.2 if
表達式的else
和if
的關閉大括號在一行。 eslint: brace-style
// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}
複製代碼
16.3 若是 if
語句中老是須要用 return
返回, 那後續的 else
就不須要寫了。 if
塊中包含 return
, 它後面的 else if
塊中也包含了 return
, 這個時候就能夠把 return
分到多個 if
語句塊中。 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;
}
} else {
return z;
}
}
複製代碼
17.1 當你的控制語句(if
, while
等)太長或者超過最大長度限制的時候, 把每個(組)判斷條件放在單獨一行裏。 邏輯操做符放在行首。
Why? 把邏輯操做符放在行首是讓操做符的對齊方式和鏈式函數保持一致。這提升了可讀性,也讓複雜邏輯更容易看清楚。
// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
thing1();
}
// bad
if (foo === 123 &&
bar === 'abc') {
thing1();
}
// bad
if (foo === 123
&& bar === 'abc') {
thing1();
}
// bad
if (
foo === 123 &&
bar === 'abc'
) {
thing1();
}
// good
if (
foo === 123
&& bar === 'abc'
) {
thing1();
}
// good
if (
(foo === 123 || bar === 'abc')
&& doesItLookGoodWhenItBecomesThatLong()
&& isThisReallyHappening()
) {
thing1();
}
// good
if (foo === 123 && bar === 'abc') {
thing1();
}
複製代碼
17.2 不要用選擇操做符代替控制語句。
// bad
!isRunning && startRunning();
// good
if (!isRunning) {
startRunning();
}
複製代碼
18.1 多行註釋用 /** ... */
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {
// ...
return element;
}
// good
/** * make() returns a new element * based on the passed-in tag name */
function make(tag) {
// ...
return element;
}
複製代碼
18.2 單行註釋用//
,將單行註釋放在被註釋區域上面。若是註釋不是在第一行,那麼註釋前面就空一行
// bad
const active = true; // is current tab
// good
// is current tab
const active = true;
// bad
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this._type || 'no type';
return type;
}
// good
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this._type || 'no type';
return type;
}
// also good
function getType() {
// set the default type to 'no type'
const type = this._type || 'no type';
return type;
}
複製代碼
18.3 全部註釋開頭空一個,方便閱讀。 eslint: spaced-comment
// 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: - 須要計算出來
或TODO: - 須要實現
。18.5 用// FIXME:
給問題作註釋
class Calculator extends Abacus {
constructor() {
super();
// FIXME: shouldn't use a global here
total = 0;
}
}
複製代碼
18.6 用// TODO:
去註釋問題的解決方案
class Calculator extends Abacus {
constructor() {
super();
// TODO: total should be configurable by an options param
this.total = 0;
}
}
複製代碼
// bad
function foo() {
∙∙∙∙const name;
}
// bad
function bar() {
∙const name;
}
// good
function baz() {
∙∙const name;
}
複製代碼
19.2 在大括號前空一格。 eslint: space-before-blocks
// bad
function test(){
console.log('test');
}
// good
function test() {
console.log('test');
}
// bad
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog',
});
// good
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog',
});
複製代碼
19.3 在控制語句(if
, while
等)的圓括號前空一格。在函數調用和定義時,參數列表和函數名之間不空格。 eslint: keyword-spacing
// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
複製代碼
19.4 用空格來隔開運算符。 eslint: space-infix-ops
// bad
const x=y+5;
// good
const x = y + 5;
複製代碼
19.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;↵
複製代碼
19.6 當出現長的方法鏈(>2個)時用縮進。用點開頭強調該行是一個方法調用,而不是一個新的語句。eslint: newline-per-chained-call
no-whitespace-before-property
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// bad
$('#items').
find('.selected').
highlight().
end().
find('.open').
updateCount();
// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
.attr('width', (radius + margin) * 2).append('svg:g')
.attr('transform', `translate(${radius + margin},${radius + margin})`)
.call(tron.led);
// good
const leds = stage.selectAll('.led')
.data(data)
.enter().append('svg:svg')
.classed('led', true)
.attr('width', (radius + margin) * 2)
.append('svg:g')
.attr('transform', `translate(${radius + margin},${radius + margin})`)
.call(tron.led);
// good
const leds = stage.selectAll('.led').data(data);
複製代碼
19.7 在一個代碼塊後下一條語句前空一行。
// bad
if (foo) {
return bar;
}
return baz;
// good
if (foo) {
return bar;
}
return baz;
// bad
const obj = {
foo() {
},
bar() {
},
};
return obj;
// good
const obj = {
foo() {
},
bar() {
},
};
return obj;
// bad
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
// good
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
複製代碼
19.8 不要用空白行填充塊。 eslint: padded-blocks
// bad
function bar() {
console.log(foo);
}
// also bad
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
// good
function bar() {
console.log(foo);
}
// good
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
複製代碼
19.9不要在代碼之間使用多個空白行填充。 eslint: no-multiple-empty-lines
// bad
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = this.getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
// good
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
複製代碼
19.10 圓括號裏不要加空格。 eslint: space-in-parens
// bad
function bar( foo ) {
return foo;
}
// good
function bar(foo) {
return foo;
}
// bad
if ( foo ) {
console.log(foo);
}
// good
if (foo) {
console.log(foo);
}
複製代碼
19.11 方括號裏不要加空格。看示例。 eslint: array-bracket-spacing
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good, 逗號分隔符仍是要空格的
const foo = [1, 2, 3];
console.log(foo[0]);
複製代碼
19.12 花括號里加空格。 eslint: object-curly-spacing
// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };
複製代碼
19.13 避免一行代碼超過100個字符(包含空格)。
注意: 對於上面——strings--line-length,長字符串不受此規則限制,不該分解。 eslint: max-len
Why? 這樣確保可讀性和可維護性
// bad
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
// good
const foo = jsonData
&& jsonData.foo
&& jsonData.foo.bar
&& jsonData.foo.bar.baz
&& jsonData.foo.bar.baz.quux
&& jsonData.foo.bar.baz.quux.xyzzy;
// good
$.ajax({
method: 'POST',
url: 'https://airbnb.com/',
data: { name: 'John' },
})
.done(() => console.log('Congratulations!'))
.fail(() => console.log('You have failed this city.'));
複製代碼
19.14 做爲語句的花括號內也要加空格 —— {
後和 }
前都須要空格。 eslint: block-spacing
// bad
function foo() {return true;}
if (foo) { bar = 0;}
// good
function foo() { return true; }
if (foo) { bar = 0; }
複製代碼
19.15 ,
前不要空格, ,
後須要空格。 eslint: comma-spacing
// bad
var foo = 1,bar = 2;
var arr = [1 , 2];
// good
var foo = 1, bar = 2;
var arr = [1, 2];
複製代碼
19.16 計算屬性內要空格。參考上述花括號和中括號的規則。 eslint: computed-property-spacing
// bad
obj[foo ]
obj[ 'foo']
var x = {[ b ]: a}
obj[foo[ bar ]]
// good
obj[foo]
obj['foo']
var x = { [b]: a }
obj[foo[bar]]
複製代碼
19.17 調用函數時,函數名和小括號之間不要空格。 eslint: func-call-spacing
// bad
func ();
func
();
// good
func();
複製代碼
19.18 在對象的字面量屬性中, key
value
之間要有空格。 eslint: key-spacing
// bad
var obj = { "foo" : 42 };
var obj2 = { "foo":42 };
// good
var obj = { "foo": 42 };
複製代碼
no-trailing-spaces
19.20 避免出現多個空行。 在文件末尾只容許空一行。 eslint: no-multiple-empty-lines
// bad
var x = 1;
var y = 2;
// good
var x = 1;
var y = 2;
複製代碼
20.1 不要前置逗號。 eslint: comma-style
// bad
const story = [
once
, upon
, aTime
];
// good
const story = [
once,
upon,
aTime,
];
// bad
const hero = {
firstName: 'Ada'
, lastName: 'Lovelace'
, birthYear: 1815
, superPower: 'computers'
};
// good
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers',
};
複製代碼
20.2 額外結尾逗號: 要 eslint: comma-dangle
Why? 這致使git diffs更清潔。 此外,像Babel這樣的轉換器會刪除轉換代碼中的額外的逗號,這意味着你沒必要擔憂舊版瀏覽器中的結尾逗號問題。
// bad - 沒有結尾逗號的 git diff
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing']
};
// good - 有結尾逗號的 git diff
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
)
複製代碼
Why? 當 JavaScript 遇到沒有分號結尾的一行,它會執行自動插入分號
Automatic Semicolon Insertion
這一規則來決定行末是否加分號。若是JavaScript在你的斷行裏錯誤的插入了分號,就會出現一些古怪的行爲。當新的功能加到JavaScript裏後, 這些規則會變得更復雜難懂。顯示的結束語句,並經過配置代碼檢查去捕獲沒有帶分號的地方能夠幫助你防止這種錯誤。
// bad
(function () {
const name = 'Skywalker'
return name
})()
// good
(function () {
const name = 'Skywalker';
return name;
}());
// good, 行首加分號,避免文件被鏈接到一塊兒時當即執行函數被當作變量來執行。
;(() => {
const name = 'Skywalker';
return name;
}());
複製代碼
22.2 Strings: eslint: no-new-wrappers
// => this.reviewScore = 9;
// bad
const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
// bad
const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
// bad
const totalScore = this.reviewScore.toString(); // 不保證返回string
// good
const totalScore = String(this.reviewScore);
複製代碼
22.3 Numbers: 用 Number
作類型轉換,parseInt
轉換string常須要帶上基數。 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);
複製代碼
22.4 請在註釋中解釋爲何要用移位運算和你在作什麼。不管你作什麼狂野的事,好比因爲 parseInt
是你的性能瓶頸致使你必定要用移位運算。 請說明這個是由於性能緣由,
// good
/** * parseInt是代碼運行慢的緣由 * 用Bitshifting將字符串轉成數字使代碼運行效率大幅增加 */
const val = inputValue >> 0;
複製代碼
22.5 注意: 用移位運算要當心. 數字使用64-位表示的,但移位運算經常返回的是32爲整形source)。移位運算對大於32位的整數會致使意外行爲。Discussion. 最大的32位整數是 2,147,483,647:
2147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> -2147483648
2147483649 >> 0 //=> -2147483647
複製代碼
22.6 布爾:
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;
複製代碼
23.1 避免用一個字母命名,讓你的命名可描述。 eslint: id-length
// bad
function q() {
// ...
}
// good
function query() {
// ...
}
複製代碼
23.2 用小駝峯式命名你的對象、函數、實例。 eslint: camelcase
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
複製代碼
23.3 用大駝峯式命名類。 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',
});
複製代碼
23.4 不要用前置或後置下劃線。 eslint: no-underscore-dangle
Why? JavaScript 沒有私有屬性或私有方法的概念。儘管前置下劃線一般的概念上意味着「private」,事實上,這些屬性是徹底公有的,所以這部分也是你的API的內容。這一律念可能會致使開發者誤覺得更改這個不會致使崩潰或者不須要測試。 若是你想要什麼東西變成「private」,那就不要讓它在這裏出現。
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
this._firstName = 'Panda';
// good
this.firstName = 'Panda';
複製代碼
23.5 不要保存引用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);
};
}
複製代碼
23.6 export default導出模塊A,則這個文件名也叫A.*, import 時候的參數也叫A。 大小寫徹底一致。
// file 1 contents
class CheckBox {
// ...
}
export default CheckBox;
// file 2 contents
export default function fortyTwo() { return 42; }
// file 3 contents
export default function insideDirectory() {}
// in some other file
// bad
import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename
import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export
import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export
// bad
import CheckBox from './check_box'; // PascalCase import/export, snake_case filename
import forty_two from './forty_two'; // snake_case import/filename, camelCase export
import inside_directory from './inside_directory'; // snake_case import, camelCase export
import index from './inside_directory/index'; // requiring the index file explicitly
import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly
// good
import CheckBox from './CheckBox'; // PascalCase export/import/filename
import fortyTwo from './fortyTwo'; // camelCase export/import/filename
import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"
// ^ supports both insideDirectory.js and insideDirectory/index.js
複製代碼
23.7 當你export-default一個函數時,函數名用小駝峯,文件名須要和函數名一致。
function makeStyleGuide() {
// ...
}
export default makeStyleGuide;
複製代碼
23.8 當你export一個結構體/類/單例/函數庫/對象 時用大駝峯。
const AirbnbStyleGuide = {
es6: {
}
};
export default AirbnbStyleGuide;
複製代碼
23.9 簡稱和縮寫應該所有大寫或所有小寫。
Why? 名字都是給人讀的,不是爲了適應電腦的算法的。
// bad
import SmsContainer from './containers/SmsContainer';
// bad
const HttpRequests = [
// ...
];
// good
import SMSContainer from './containers/SMSContainer';
// good
const HTTPRequests = [
// ...
];
// also good
const httpRequests = [
// ...
];
// best
import TextMessageContainer from './containers/TextMessageContainer';
// best
const requests = [
// ...
];
複製代碼
23.10 你能夠用全大寫字母設置靜態變量,他須要知足三個條件。
const
定義的, 保證不能被改變Why? 這是一個附加工具,幫助開發者去辨識一個變量是否是不可變的。
const
變量呢? —— 這個是沒必要要的。大寫變量不該該在同一個文件裏定義並使用, 它只能用來做爲導出變量。 贊同!EXPORTED_OBJECT.key
) 而且他包含的全部子屬性都是不可變的。// bad
const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file';
// bad
export const THING_TO_BE_CHANGED = 'should obviously not be uppercased';
// bad
export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables';
// ---
// 容許但不夠語義化
export const apiKey = 'SOMEKEY';
// 在大多數狀況下更好
export const API_KEY = 'SOMEKEY';
// ---
// bad - 沒必要要的大寫鍵,沒有增長任何語言
export const MAPPING = {
KEY: 'value'
};
// good
export const MAPPING = {
key: 'value'
};
複製代碼
24.2 不要使用JavaScript的getters/setters,由於他們會產生反作用,而且難以測試、維護和理解。相反的,你能夠用 getVal()和setVal('hello')去創造你本身的accessor函數
// bad
class Dragon {
get age() {
// ...
}
set age(value) {
// ...
}
}
// good
class Dragon {
getAge() {
// ...
}
setAge(value) {
// ...
}
}
複製代碼
24.3 若是屬性/方法是boolean
, 用 isVal()
或 hasVal()
// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}
複製代碼
24.4 用get()和set()函數是能夠的,可是要一塊兒用
class Jedi {
constructor(options = {}) {
const lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
set(key, val) {
this[key] = val;
}
get(key) {
return this[key];
}
}
複製代碼
25.1 經過哈希而不是原始值向事件裝載數據時(不管是DOM事件仍是像Backbone事件的不少屬性)。 這使得後續的貢獻者(程序員)向這個事件裝載更多的數據時不用去找或者更新每一個處理器。例如:
// bad
$(this).trigger('listingUpdated', listing.id);
// ...
$(this).on('listingUpdated', (e, listingID) => {
// do something with listingID
});
複製代碼
prefer:
// good
$(this).trigger('listingUpdated', { listingID: listing.id });
// ...
$(this).on('listingUpdated', (e, data) => {
// do something with data.listingID
});
複製代碼
26.1 jQuery對象用$
變量表示。
// bad
const sidebar = $('.sidebar');
// good
const $sidebar = $('.sidebar');
// good
const $sidebarBtn = $('.sidebar-btn');
複製代碼
26.2 暫存jQuery查找
// bad
function setSidebar() {
$('.sidebar').hide();
// ...
$('.sidebar').css({
'background-color': 'pink'
});
}
// good
function setSidebar() {
const $sidebar = $('.sidebar');
$sidebar.hide();
// ...
$sidebar.css({
'background-color': 'pink'
});
}
複製代碼
26.4 用jQuery對象查詢做用域的find
方法查詢
// bad
$('ul', '.sidebar').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good
$sidebar.find('ul').hide();
複製代碼
28.2 不要用TC39 proposals, TC39尚未到 stage 3。
Why? 它還不是最終版, 他可能還有不少變化,或者被撤銷。 咱們想要用的是 JavaScript, 提議還不是JavaScript。
標準庫中包含一些功能受損可是因爲歷史緣由遺留的工具類
29.1 用 Number.isNaN
代替全局的 isNaN
. eslint: no-restricted-globals
Why? 全局
isNaN
強制把非數字轉成數字, 而後對於任何強轉後爲NaN
的變量都返回true
若是你想用這個功能,就顯式的用它。
// bad
isNaN('1.2'); // false
isNaN('1.2.3'); // true
// good
Number.isNaN('1.2.3'); // false
Number.isNaN(Number('1.2.3')); // true
複製代碼
29.2 用 Number.isFinite
代替 isFinite
. eslint: no-restricted-globals
Why? 理由同上,會把一個非數字變量強轉成數字,而後作判斷。
// bad
isFinite('2e3'); // true
// good
Number.isFinite('2e3'); // false
Number.isFinite(parseInt('2e3', 10)); // true
複製代碼
30.1 Yup.
function foo() {
return true;
}
複製代碼