├── .gitignore
├── README.md
├── es5
└── README.md
├── linters
├── .eslintrc
├── .jshintrc
├── README.md
└── SublimeLinter
│ └── SublimeLinter.sublime-settings
├── package.json
├── packages
└── eslint-config-airbnb
│ ├── .eslintrc
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── base.js
│ ├── index.js
│ ├── legacy.js
│ ├── node_modules
│ └── eslint-config-airbnb
│ ├── package.json
│ ├── rules
│ ├── .eslintrc.json
│ ├── best-practices.js
│ ├── errors.js
│ ├── es6.js
│ ├── legacy.js
│ ├── node.js
│ ├── react.js
│ ├── strict.js
│ ├── style.js
│ └── variables.js
│ └── test
│ ├── .eslintrc
│ ├── test-base.js
│ └── test-react-order.js
└── react
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Airbnb JavaScript Style Guide() {
2 |
3 | *一份彙整了在 JavasScript 中被普遍使用的風格指南。*
4 |
5 | [](https://www.npmjs.com/package/eslint-config-airbnb)
6 | [](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
7 |
8 | 其他風格指南
9 | - [ES5](es5/)
10 | - [React](react/)
11 | - [CSS & Sass](https://github.com/airbnb/css)
12 | - [Ruby](https://github.com/airbnb/ruby)
13 |
14 | 翻譯自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。
15 |
16 |
17 | ## 目錄
18 |
19 | 1. [資料型態](#types)
20 | 1. [參考](#references)
21 | 1. [物件](#objects)
22 | 1. [陣列](#arrays)
23 | 1. [解構子](#destructuring)
24 | 1. [字串](#strings)
25 | 1. [函式](#functions)
26 | 1. [箭頭函式](#arrow-functions)
27 | 1. [建構子](#constructors)
28 | 1. [模組](#modules)
29 | 1. [迭代器及產生器](#iterators-and-generators)
30 | 1. [屬性](#properties)
31 | 1. [變數](#variables)
32 | 1. [提升](#hoisting)
33 | 1. [條件式與等號](#comparison-operators--equality)
34 | 1. [區塊](#blocks)
35 | 1. [控制陳述式](#control-statements)
36 | 1. [註解](#comments)
37 | 1. [空格](#whitespace)
38 | 1. [逗號](#commas)
39 | 1. [分號](#semicolons)
40 | 1. [型別轉換](#type-casting--coercion)
41 | 1. [命名規則](#naming-conventions)
42 | 1. [存取器](#accessors)
43 | 1. [事件](#events)
44 | 1. [jQuery](#jquery)
45 | 1. [ECMAScript 5 相容性](#ecmascript-5-compatibility)
46 | 1. [ECMAScript 6 風格](#ecmascript-6-styles)
47 | 1. [標準程式庫](#standard-library)
48 | 1. [測試](#testing)
49 | 1. [效能](#performance)
50 | 1. [資源](#resources)
51 | 1. [誰在使用](#in-the-wild)
52 | 1. [翻譯](#translation)
53 | 1. [JavaScript 風格指南](#the-javascript-style-guide-guide)
54 | 1. [和我們討論 Javascript](#chat-with-us-about-javascript)
55 | 1. [貢獻者](#contributors)
56 | 1. [授權許可](#license)
57 | 1. [Amendments](#amendments)
58 |
59 |
60 | ## 資料型態
61 |
62 | - [1.1](#1.1) **基本**:你可以直接存取基本資料型態。
63 |
64 | + `字串`
65 | + `數字`
66 | + `布林`
67 | + `null`
68 | + `undefined`
69 |
70 | ```javascript
71 | const foo = 1;
72 | let bar = foo;
73 |
74 | bar = 9;
75 |
76 | console.log(foo, bar); // => 1, 9
77 | ```
78 | - [1.2](#1.2) **複合**:你需要透過引用的方式存取複合資料型態。
79 |
80 | + `物件`
81 | + `陣列`
82 | + `函式`
83 |
84 | ```javascript
85 | const foo = [1, 2];
86 | const bar = foo;
87 |
88 | bar[0] = 9;
89 |
90 | console.log(foo[0], bar[0]); // => 9, 9
91 | ```
92 |
93 | **[⬆ 回到頂端](#table-of-contents)**
94 |
95 |
96 | ## 參考
97 |
98 | - [2.1](#2.1) 對於所有的參考使用 `const`;避免使用 `var`。eslint: [`prefer-const`](http://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](http://eslint.org/docs/rules/no-const-assign.html)
99 |
100 | > 為什麼?因為這能確保你無法對參考重新賦值,也不會讓你的程式碼有錯誤或難以理解。
101 |
102 | ```javascript
103 | // bad
104 | var a = 1;
105 | var b = 2;
106 |
107 | // good
108 | const a = 1;
109 | const b = 2;
110 | ```
111 |
112 | - [2.2](#2.2) 如果你需要可變動的參考,使用 `let` 代替 `var`。eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) jscs: [`disallowVar`](http://jscs.info/rule/disallowVar)
113 |
114 | > 為什麼?因為 `let` 的作用域是在區塊內,而不像 `var` 是在函式內。
115 |
116 | ```javascript
117 | // bad
118 | var count = 1;
119 | if (true) {
120 | count += 1;
121 | }
122 |
123 | // good, use the let.
124 | let count = 1;
125 | if (true) {
126 | count += 1;
127 | }
128 | ```
129 |
130 | - [2.3](#2.3) 請注意,`let` 與 `const` 的作用域都只在區塊內。
131 |
132 | ```javascript
133 | // const 及 let 只存在於他們被定義的區塊內。
134 | {
135 | let a = 1;
136 | const b = 1;
137 | }
138 | console.log(a); // ReferenceError
139 | console.log(b); // ReferenceError
140 | ```
141 |
142 | **[⬆ 回到頂端](#table-of-contents)**
143 |
144 |
145 | ## 物件
146 |
147 | - [3.1](#3.1) 使用簡潔的語法建立物件。eslint rules: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html).
148 |
149 | ```javascript
150 | // bad
151 | const item = new Object();
152 |
153 | // good
154 | const item = {};
155 | ```
156 |
157 | - [3.2](#3.2) 別使用[保留字](http://es5.github.io/#x7.6.1)當作鍵值,他在 IE8 上不會被執行。[了解更多](https://github.com/airbnb/javascript/issues/61)。不過在 ES6 模組及伺服器端程式碼中使用是可行的。jscs: [`disallowIdentifierNames`](http://jscs.info/rule/disallowIdentifierNames)
158 |
159 | ```javascript
160 | // bad
161 | const superman = {
162 | default: { clark: 'kent' },
163 | private: true,
164 | };
165 |
166 | // good
167 | const superman = {
168 | defaults: { clark: 'kent' },
169 | hidden: true,
170 | };
171 | ```
172 |
173 | - [3.3](#3.3) 使用同義詞取代保留字。jscs: [`disallowIdentifierNames`](http://jscs.info/rule/disallowIdentifierNames)
174 |
175 | ```javascript
176 | // bad
177 | const superman = {
178 | class: 'alien',
179 | };
180 |
181 | // bad
182 | const superman = {
183 | klass: 'alien',
184 | };
185 |
186 | // good
187 | const superman = {
188 | type: 'alien',
189 | };
190 | ```
191 |
192 |
193 | - [3.4](#3.4) 建立具有動態屬性名稱的物件時請使用可被計算的屬性名稱。
194 |
195 | > 為什麼?因為這樣能夠讓你在同一個地方定義所有的物件屬性。
196 |
197 | ```javascript
198 |
199 | function getKey(k) {
200 | return `a key named ${k}`;
201 | }
202 |
203 | // bad
204 | const obj = {
205 | id: 5,
206 | name: 'San Francisco',
207 | };
208 | obj[getKey('enabled')] = true;
209 |
210 | // good
211 | const obj = {
212 | id: 5,
213 | name: 'San Francisco',
214 | [getKey('enabled')]: true,
215 | };
216 | ```
217 |
218 |
219 | - [3.5](#3.5) 使用物件方法的簡寫。eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals)
220 |
221 | ```javascript
222 | // bad
223 | const atom = {
224 | value: 1,
225 |
226 | addValue: function (value) {
227 | return atom.value + value;
228 | },
229 | };
230 |
231 | // good
232 | const atom = {
233 | value: 1,
234 |
235 | addValue(value) {
236 | return atom.value + value;
237 | },
238 | };
239 | ```
240 |
241 |
242 | - [3.6](#3.6) 使用屬性值的簡寫。eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals)
243 |
244 | > 為什麼?因為寫起來更短且更有描述性。
245 |
246 | ```javascript
247 | const lukeSkywalker = 'Luke Skywalker';
248 |
249 | // bad
250 | const obj = {
251 | lukeSkywalker: lukeSkywalker,
252 | };
253 |
254 | // good
255 | const obj = {
256 | lukeSkywalker,
257 | };
258 | ```
259 |
260 | - [3.7](#3.7) 請在物件宣告的開頭將簡寫的屬性分組。
261 |
262 | > 為什麼?因為這樣能夠很簡單的看出哪些屬性是使用簡寫。
263 |
264 | ```javascript
265 | const anakinSkywalker = 'Anakin Skywalker';
266 | const lukeSkywalker = 'Luke Skywalker';
267 |
268 | // bad
269 | const obj = {
270 | episodeOne: 1,
271 | twoJediWalkIntoACantina: 2,
272 | lukeSkywalker,
273 | episodeThree: 3,
274 | mayTheFourth: 4,
275 | anakinSkywalker,
276 | };
277 |
278 | // good
279 | const obj = {
280 | lukeSkywalker,
281 | anakinSkywalker,
282 | episodeOne: 1,
283 | twoJediWalkIntoACantina: 2,
284 | episodeThree: 3,
285 | mayTheFourth: 4,
286 | };
287 | ```
288 |
289 | - [3.8](#3.8) 只在無效的鍵加上引號。eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects)
290 |
291 | > 為什麼?整體來說,我們認為這在主觀上更容易閱讀。它會改善語法高亮,也能讓多數的 JS 引擎更容易最佳化。
292 |
293 | ```javascript
294 | // bad
295 | const bad = {
296 | 'foo': 3,
297 | 'bar': 4,
298 | 'data-blah': 5,
299 | };
300 |
301 | // good
302 | const good = {
303 | foo: 3,
304 | bar: 4,
305 | 'data-blah': 5,
306 | };
307 | ```
308 |
309 | **[⬆ 回到頂端](#table-of-contents)**
310 |
311 |
312 | ## 陣列
313 |
314 | - [4.1](#4.1) 使用簡潔的語法建立陣列。eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html)
315 |
316 | ```javascript
317 | // bad
318 | const items = new Array();
319 |
320 | // good
321 | const items = [];
322 | ```
323 |
324 | - [4.2](#4.2) 如果你不知道陣列的長度請使用 Array#push。
325 |
326 | ```javascript
327 | const someStack = [];
328 |
329 | // bad
330 | someStack[someStack.length] = 'abracadabra';
331 |
332 | // good
333 | someStack.push('abracadabra');
334 | ```
335 |
336 |
337 | - [4.3](#4.3) 使用陣列的擴展運算子 `...` 來複製陣列。
338 |
339 | ```javascript
340 | // bad
341 | const len = items.length;
342 | const itemsCopy = [];
343 | let i;
344 |
345 | for (i = 0; i < len; i++) {
346 | itemsCopy[i] = items[i];
347 | }
348 |
349 | // good
350 | const itemsCopy = [...items];
351 | ```
352 | - [4.4](#4.4) 如果要轉換一個像陣列的物件至陣列,可以使用 Array#from。
353 |
354 | ```javascript
355 | const foo = document.querySelectorAll('.foo');
356 | const nodes = Array.from(foo);
357 | ```
358 |
359 | - [4.5](#4.5) 在陣列方法的回呼使用 return 宣告。若函式本體是如 [8.2](#8.2) 的單一語法,那麼省略 return 是可以的。eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return)
360 |
361 | ```javascript
362 | // good
363 | [1, 2, 3].map((x) => {
364 | const y = x + 1;
365 | return x * y;
366 | });
367 |
368 | // good
369 | [1, 2, 3].map(x => x + 1);
370 |
371 | // bad
372 | const flat = {};
373 | [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
374 | const flatten = memo.concat(item);
375 | flat[index] = memo.concat(item);
376 | });
377 |
378 | // good
379 | const flat = {};
380 | [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
381 | const flatten = memo.concat(item);
382 | flat[index] = flatten;
383 | return flatten;
384 | });
385 |
386 | // bad
387 | inbox.filter((msg) => {
388 | const { subject, author } = msg;
389 | if (subject === 'Mockingbird') {
390 | return author === 'Harper Lee';
391 | } else {
392 | return false;
393 | }
394 | });
395 |
396 | // good
397 | inbox.filter((msg) => {
398 | const { subject, author } = msg;
399 | if (subject === 'Mockingbird') {
400 | return author === 'Harper Lee';
401 | }
402 |
403 | return false;
404 | });
405 | ```
406 |
407 | **[⬆ 回到頂端](#table-of-contents)**
408 |
409 |
410 | ## 解構子
411 |
412 | - [5.1](#5.1) 存取或使用多屬性的物件時,請使用物件解構子。jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring)
413 |
414 | > 為什麼?因為解構子能夠節省你對這些屬性建立暫時的參考。
415 |
416 | ```javascript
417 | // bad
418 | function getFullName(user) {
419 | const firstName = user.firstName;
420 | const lastName = user.lastName;
421 |
422 | return `${firstName} ${lastName}`;
423 | }
424 |
425 | // good
426 | function getFullName(user) {
427 | const { firstName, lastName } = user;
428 | return `${firstName} ${lastName}`;
429 | }
430 |
431 | // best
432 | function getFullName({ firstName, lastName }) {
433 | return `${firstName} ${lastName}`;
434 | }
435 | ```
436 |
437 | - [5.2](#5.2) 使用陣列解構子。jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring)
438 |
439 | ```javascript
440 | const arr = [1, 2, 3, 4];
441 |
442 | // bad
443 | const first = arr[0];
444 | const second = arr[1];
445 |
446 | // good
447 | const [first, second] = arr;
448 | ```
449 |
450 | - [5.3](#5.3) 需要回傳多個值時請使用物件解構子,而不是陣列解構子。
451 |
452 | > 為什麼?因為你可以增加新的屬性或改變排序且不須更動呼叫的位置。
453 |
454 | ```javascript
455 | // bad
456 | function processInput(input) {
457 | // 這時神奇的事情出現了
458 | return [left, right, top, bottom];
459 | }
460 |
461 | // 呼叫時必須考慮回傳資料的順序。
462 | const [left, __, top] = processInput(input);
463 |
464 | // good
465 | function processInput(input) {
466 | // 這時神奇的事情出現了
467 | return { left, right, top, bottom };
468 | }
469 |
470 | // 呼叫時只需選擇需要的資料
471 | const { left, right } = processInput(input);
472 | ```
473 |
474 |
475 | **[⬆ 回到頂端](#table-of-contents)**
476 |
477 |
478 | ## 字串
479 |
480 | - [6.1](#6.1) 字串請使用單引號 `''`。eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks)
481 |
482 | ```javascript
483 | // bad
484 | const name = "Capt. Janeway";
485 |
486 | // good
487 | const name = 'Capt. Janeway';
488 | ```
489 |
490 | - [6.2](#6.2) 如果字串超過 100 個字元,請使用字串連接符號換行。
491 | - [6.3](#6.3) 注意:過度的長字串連接可能會影響效能。[jsPerf](http://jsperf.com/ya-string-concat) 及[討論串](https://github.com/airbnb/javascript/issues/40)。
492 |
493 | ```javascript
494 | // bad
495 | 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.';
496 |
497 | // bad
498 | const errorMessage = 'This is a super long error that was thrown because \
499 | of Batman. When you stop to think about how Batman had anything to do \
500 | with this, you would get nowhere \
501 | fast.';
502 |
503 | // good
504 | const errorMessage = 'This is a super long error that was thrown because ' +
505 | 'of Batman. When you stop to think about how Batman had anything to do ' +
506 | 'with this, you would get nowhere fast.';
507 | ```
508 |
509 |
510 | - [6.4](#6.4) 當以程式方式建構字串時,請使用模板字串而不是字串連接。eslint: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](http://eslint.org/docs/rules/template-curly-spacing) jscs: [`requireTemplateStrings`](http://jscs.info/rule/requireTemplateStrings)
511 |
512 | > 為什麼?因為模板字串更有可讀性,正確的換行符號及字串插值功能讓語法更簡潔。
513 |
514 | ```javascript
515 | // bad
516 | function sayHi(name) {
517 | return 'How are you, ' + name + '?';
518 | }
519 |
520 | // bad
521 | function sayHi(name) {
522 | return ['How are you, ', name, '?'].join();
523 | }
524 |
525 | // bad
526 | function sayHi(name) {
527 | return `How are you, ${ name }?`;
528 | }
529 |
530 | // good
531 | function sayHi(name) {
532 | return `How are you, ${name}?`;
533 | }
534 | ```
535 | - [6.5](#6.5) 千萬不要在字串中使用 `eval()`,會造成許多的漏洞。
536 |
537 | **[⬆ 回到頂端](#table-of-contents)**
538 |
539 |
540 | ## 函式
541 |
542 | - [7.1](#7.1) 使用函式宣告而不是函式表達式。jscs: [`requireFunctionDeclarations`](http://jscs.info/rule/requireFunctionDeclarations)
543 |
544 | > 為什麼?因為函式宣告是可命名的,所以他們在呼叫堆疊中更容易被識別。此外,函式宣告自身都會被提升,而函式表達式則只有參考會被提升。這個規則使得[箭頭函式](#arrow-functions)可以完全取代函式表達式。
545 |
546 | ```javascript
547 | // bad
548 | const foo = function () {
549 | };
550 |
551 | // good
552 | function foo() {
553 | }
554 | ```
555 |
556 | - [7.2](#7.2) 立即函式:eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE)
557 |
558 | > 為什麼?一個立即函式是個獨立的單元-將函式及呼叫函式的括號包起來明確表示這一點。注意在模組世界的任何地方,你都不需要使用立即函式。
559 |
560 | ```javascript
561 | // 立即函式(IIFE)
562 | (function () {
563 | console.log('Welcome to the Internet. Please follow me.');
564 | }());
565 | ```
566 |
567 | - [7.3](#7.3) 絕對不要在非函式的區塊(if、while 等等)宣告函式。你可以將函式賦予至變數解決這個問題。瀏覽器會允許你這麼做,但不同瀏覽器產生的結果可能會不同。eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html)
568 |
569 | - [7.4](#7.4) **注意:**ECMA-262 將`區塊`定義為陳述式。函式宣告則不是陳述式。[閱讀 ECMA-262 關於這個問題的說明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。
570 |
571 | ```javascript
572 | // bad
573 | if (currentUser) {
574 | function test() {
575 | console.log('Nope.');
576 | }
577 | }
578 |
579 | // good
580 | let test;
581 | if (currentUser) {
582 | test = () => {
583 | console.log('Yup.');
584 | };
585 | }
586 | ```
587 |
588 | - [7.5](#7.5) 請勿將參數命名為 `arguments`,這樣會將覆蓋掉函式作用域傳來的 `arguments`。
589 |
590 | ```javascript
591 | // bad
592 | function nope(name, options, arguments) {
593 | // ...stuff...
594 | }
595 |
596 | // good
597 | function yup(name, options, args) {
598 | // ...stuff...
599 | }
600 | ```
601 |
602 |
603 | - [7.6](#7.6) 絕對不要使用 `arguments`,可以選擇使用 rest 語法 `...` 替代。[`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params)
604 |
605 | > 為什麼?使用 `...` 能夠明確指出你要將參數傳入哪個變數。再加上 rest 參數是一個真正的陣列,而不像 `arguments` 似陣列而非陣列。
606 |
607 | ```javascript
608 | // bad
609 | function concatenateAll() {
610 | const args = Array.prototype.slice.call(arguments);
611 | return args.join('');
612 | }
613 |
614 | // good
615 | function concatenateAll(...args) {
616 | return args.join('');
617 | }
618 | ```
619 |
620 |
621 | - [7.7](#7.7) 使用預設參數的語法,而不是變動函式的參數。
622 |
623 | ```javascript
624 | // really bad
625 | function handleThings(opts) {
626 | // 不!我們不該變動函式的參數。
627 | // Double bad: 如果 opt 是 false ,那們它就會被設定為一個物件,
628 | // 或許你想要這麼做,但是這樣可能會造成一些 Bug。
629 | opts = opts || {};
630 | // ...
631 | }
632 |
633 | // still bad
634 | function handleThings(opts) {
635 | if (opts === void 0) {
636 | opts = {};
637 | }
638 | // ...
639 | }
640 |
641 | // good
642 | function handleThings(opts = {}) {
643 | // ...
644 | }
645 | ```
646 |
647 | - [7.8](#7.8) 使用預設參數時請避免副作用。
648 |
649 | > 為什麼?因為這樣會讓思緒混淆。
650 |
651 | ```javascript
652 | var b = 1;
653 | // bad
654 | function count(a = b++) {
655 | console.log(a);
656 | }
657 | count(); // 1
658 | count(); // 2
659 | count(3); // 3
660 | count(); // 3
661 | ```
662 |
663 | - [7.9](#7.9) 永遠將預設參數放置於最後。
664 |
665 | ```javascript
666 | // bad
667 | function handleThings(opts = {}, name) {
668 | // ...
669 | }
670 |
671 | // good
672 | function handleThings(name, opts = {}) {
673 | // ...
674 | }
675 | ```
676 |
677 | - [7.10](#7.10) 千萬別使用建構函式去建立一個新的函式。
678 |
679 | > 為什麼?透過這種方式建立一個函數來計算字串類似於 eval(),會造成許多的漏洞。
680 |
681 | ```javascript
682 | // bad
683 | var add = new Function('a', 'b', 'return a + b');
684 |
685 | // still bad
686 | var subtract = Function('a', 'b', 'return a - b');
687 | ```
688 |
689 | - [7.11](#7.11) 在函式的標示後放置空格。
690 |
691 | > 為什麼?一致性較好,而且你不應該在新增或刪除名稱時增加或減少空格。
692 |
693 | ```javascript
694 | // bad
695 | const f = function(){};
696 | const g = function (){};
697 | const h = function() {};
698 |
699 | // good
700 | const x = function () {};
701 | const y = function a() {};
702 | ```
703 |
704 | - [7.12](#7.12) 切勿變更參數。eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html)
705 |
706 | > 為什麼?操作作為參數傳入的物件可能導致變數產生原呼叫者不期望的副作用。
707 |
708 | ```javascript
709 | // bad
710 | function f1(obj) {
711 | obj.key = 1;
712 | };
713 |
714 | // good
715 | function f2(obj) {
716 | const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
717 | };
718 | ```
719 |
720 | - [7.13](#7.13) 切勿重新賦值給參數。eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html)
721 |
722 | > 為什麼?將參數重新賦值可能導致意外的行為,尤其在存取 `arguments` 物件時。它可能會引起最佳化的問題,尤其在 V8。
723 |
724 | ```javascript
725 | // bad
726 | function f1(a) {
727 | a = 1;
728 | }
729 |
730 | function f2(a) {
731 | if (!a) { a = 1; }
732 | }
733 |
734 | // good
735 | function f3(a) {
736 | const b = a || 1;
737 | }
738 |
739 | function f4(a = 1) {
740 | }
741 | ```
742 |
743 | **[⬆ 回到頂端](#table-of-contents)**
744 |
745 |
746 | ## 箭頭函式
747 |
748 | - [8.1](#8.1) 當你必須使用函式表達式(或傳遞一個匿名函式)時,請使用箭頭函式的符號。eslint: [`prefer-arrow-callback`](http://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](http://eslint.org/docs/rules/arrow-spacing.html) jscs: [`requireArrowFunctions`](http://jscs.info/rule/requireArrowFunctions)
749 |
750 | > 為什麼?它會在有 `this` 的內部建立了一個新版本的函式,通常功能都是你所想像的,而且語法更為簡潔。
751 |
752 | > 為什麼不?如果你已經有一個相當複雜的函式時,或許你該將邏輯都移到一個函式宣告上。
753 |
754 | ```javascript
755 | // bad
756 | [1, 2, 3].map(function (x) {
757 | const y = x + 1;
758 | return x * y;
759 | });
760 |
761 | // good
762 | [1, 2, 3].map((x) => {
763 | const y = x + 1;
764 | return x * y;
765 | });
766 | ```
767 |
768 | - [8.2](#8.2) 如果函式適合只使用一行,你可以很隨性的省略大括號及使用隱藏的回傳。否則請使用 `return` 語法。eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](http://eslint.org/docs/rules/arrow-body-style.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam), [`requireShorthandArrowFunctions`](http://jscs.info/rule/requireShorthandArrowFunctions)
769 |
770 | > 為什麼?因為語法修飾。這樣能夠在多個函式鏈結在一起的時候更易讀。
771 |
772 | > 為什麼不?如果你打算回傳一個物件。
773 |
774 | ```javascript
775 | // bad
776 | [1, 2, 3].map(number => {
777 | const nextNumber = number + 1;
778 | `A string containing the ${nextNumber}.`;
779 | });
780 |
781 | // good
782 | [1, 2, 3].map(number => `A string containing the ${number}.`);
783 |
784 | // good
785 | [1, 2, 3].map((number) => {
786 | const nextNumber = number + 1;
787 | return `A string containing the ${nextNumber}.`;
788 | });
789 | ```
790 |
791 | - [8.3](#8.3) 如果表達式跨了多行,請將它們包在括號中增加可讀性。
792 |
793 | > 為什麼?這麼做更清楚的表達函式的開始與結束的位置。
794 |
795 | ```js
796 | // bad
797 | [1, 2, 3].map(number => 'As time went by, the string containing the ' +
798 | `${number} became much longer. So we needed to break it over multiple ` +
799 | 'lines.'
800 | );
801 |
802 | // good
803 | [1, 2, 3].map(number => (
804 | `As time went by, the string containing the ${number} became much ` +
805 | 'longer. So we needed to break it over multiple lines.'
806 | ));
807 | ```
808 |
809 |
810 | - [8.4](#8.4) 如果你的函式只使用一個參數,那麼可以很隨意的省略括號。否則請在參數兩側加上括號。eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam)
811 |
812 | > 為什麼?減少視覺上的混亂。
813 |
814 | ```js
815 | // bad
816 | [1, 2, 3].map((x) => x * x);
817 |
818 | // good
819 | [1, 2, 3].map(x => x * x);
820 |
821 | // good
822 | [1, 2, 3].map(number => (
823 | `A long string with the ${number}. It’s so long that we’ve broken it ` +
824 | 'over multiple lines!'
825 | ));
826 |
827 | // bad
828 | [1, 2, 3].map(x => {
829 | const y = x + 1;
830 | return x * y;
831 | });
832 |
833 | // good
834 | [1, 2, 3].map((x) => {
835 | const y = x + 1;
836 | return x * y;
837 | });
838 | ```
839 |
840 | - [8.5](#8.5) 避免混淆箭頭函式語法(`=>`)及比較運算子(`<=`、`>=`)。eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow)
841 |
842 | ```js
843 | // bad
844 | const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
845 |
846 | // bad
847 | const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
848 |
849 | // good
850 | const itemHeight = item => { return item.height > 256 ? item.largeSize : item.smallSize; }
851 | ```
852 |
853 | **[⬆ 回到頂端](#table-of-contents)**
854 |
855 |
856 | ## 建構子
857 |
858 | - [9.1](#9.1) 總是使用 `class`。避免直接操作 `prototype`。
859 |
860 | > 為什麼?因為 `class` 語法更簡潔且更易讀。
861 |
862 | ```javascript
863 | // bad
864 | function Queue(contents = []) {
865 | this._queue = [...contents];
866 | }
867 | Queue.prototype.pop = function () {
868 | const value = this._queue[0];
869 | this._queue.splice(0, 1);
870 | return value;
871 | }
872 |
873 |
874 | // good
875 | class Queue {
876 | constructor(contents = []) {
877 | this._queue = [...contents];
878 | }
879 | pop() {
880 | const value = this._queue[0];
881 | this._queue.splice(0, 1);
882 | return value;
883 | }
884 | }
885 | ```
886 |
887 | - [9.2](#9.2) 使用 `extends` 繼承。
888 |
889 | > 為什麼?因為他是一個內建繼承原型方法的方式,且不會破壞 `instanceof`。
890 |
891 | ```javascript
892 | // bad
893 | const inherits = require('inherits');
894 | function PeekableQueue(contents) {
895 | Queue.apply(this, contents);
896 | }
897 | inherits(PeekableQueue, Queue);
898 | PeekableQueue.prototype.peek = function () {
899 | return this._queue[0];
900 | }
901 |
902 | // good
903 | class PeekableQueue extends Queue {
904 | peek() {
905 | return this._queue[0];
906 | }
907 | }
908 | ```
909 |
910 | - [9.3](#9.3) 方法可以回傳 `this` 幫助方法鏈結。
911 |
912 | ```javascript
913 | // bad
914 | Jedi.prototype.jump = function () {
915 | this.jumping = true;
916 | return true;
917 | };
918 |
919 | Jedi.prototype.setHeight = function (height) {
920 | this.height = height;
921 | };
922 |
923 | const luke = new Jedi();
924 | luke.jump(); // => true
925 | luke.setHeight(20); // => undefined
926 |
927 | // good
928 | class Jedi {
929 | jump() {
930 | this.jumping = true;
931 | return this;
932 | }
933 |
934 | setHeight(height) {
935 | this.height = height;
936 | return this;
937 | }
938 | }
939 |
940 | const luke = new Jedi();
941 |
942 | luke.jump()
943 | .setHeight(20);
944 | ```
945 |
946 |
947 | - [9.4](#9.4) 可以寫一個 toString() 的方法,但是請確保它可以正常執行且沒有函式副作用。
948 |
949 | ```javascript
950 | class Jedi {
951 | constructor(options = {}) {
952 | this.name = options.name || 'no name';
953 | }
954 |
955 | getName() {
956 | return this.name;
957 | }
958 |
959 | toString() {
960 | return `Jedi - ${this.getName()}`;
961 | }
962 | }
963 | ```
964 |
965 | - [9.5](#9.5) 若類別沒有指定建構子,那它會擁有預設的建構子。一個空的建構子函式或只委派給父類別是不必要的。[`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor)
966 |
967 | ```javascript
968 | // bad
969 | class Jedi {
970 | constructor() {}
971 |
972 | getName() {
973 | return this.name;
974 | }
975 | }
976 |
977 | // bad
978 | class Rey extends Jedi {
979 | constructor(...args) {
980 | super(...args);
981 | }
982 | }
983 |
984 | // good
985 | class Rey extends Jedi {
986 | constructor(...args) {
987 | super(...args);
988 | this.name = 'Rey';
989 | }
990 | }
991 | ```
992 |
993 | **[⬆ 回到頂端](#table-of-contents)**
994 |
995 |
996 |
997 | ## 模組
998 |
999 | - [10.1](#10.1) 總是使用模組(`import`/`export`)勝過一個非標準模組的系統。你可以編譯為喜歡的模組系統。
1000 |
1001 | > 為什麼?模組就是未來的趨勢,讓我們現在就開始前往未來吧。
1002 |
1003 | ```javascript
1004 | // bad
1005 | const AirbnbStyleGuide = require('./AirbnbStyleGuide');
1006 | module.exports = AirbnbStyleGuide.es6;
1007 |
1008 | // ok
1009 | import AirbnbStyleGuide from './AirbnbStyleGuide';
1010 | export default AirbnbStyleGuide.es6;
1011 |
1012 | // best
1013 | import { es6 } from './AirbnbStyleGuide';
1014 | export default es6;
1015 | ```
1016 |
1017 | - [10.2](#10.2) 請別使用萬用字元引入。
1018 |
1019 | > 為什麼?這樣能夠確保你只有一個預設導出。
1020 |
1021 | ```javascript
1022 | // bad
1023 | import * as AirbnbStyleGuide from './AirbnbStyleGuide';
1024 |
1025 | // good
1026 | import AirbnbStyleGuide from './AirbnbStyleGuide';
1027 | ```
1028 |
1029 | - [10.3](#10.3) 然後也不要在引入的地方導出。
1030 |
1031 | > 為什麼?雖然一行程式相當的簡明,但是讓引入及導出各自有明確的方式能夠讓事情保持一致。
1032 |
1033 | ```javascript
1034 | // bad
1035 | // filename es6.js
1036 | export { es6 as default } from './airbnbStyleGuide';
1037 |
1038 | // good
1039 | // filename es6.js
1040 | import { es6 } from './AirbnbStyleGuide';
1041 | export default es6;
1042 | ```
1043 |
1044 | **[⬆ 回到頂端](#table-of-contents)**
1045 |
1046 |
1047 | ## 迭代器及產生器
1048 |
1049 | - [11.1](#11.1) 不要使用迭代器。更好的做法是使用 JavaScript 的高階函式,像是 `map()` 及 `reduce()`,替代如 `for-of ` 的迴圈語法。eslint: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html)
1050 |
1051 | > 為什麼?這加強了我們不變的規則。處理純函式的回傳值讓程式碼更易讀,勝過它所造成的函式副作用。
1052 |
1053 | eslint rules: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html).
1054 |
1055 | ```javascript
1056 | const numbers = [1, 2, 3, 4, 5];
1057 |
1058 | // bad
1059 | let sum = 0;
1060 | for (let num of numbers) {
1061 | sum += num;
1062 | }
1063 |
1064 | sum === 15;
1065 |
1066 | // good
1067 | let sum = 0;
1068 | numbers.forEach(num => sum += num);
1069 | sum === 15;
1070 |
1071 | // best (使用 javascript 的高階函式)
1072 | const sum = numbers.reduce((total, num) => total + num, 0);
1073 | sum === 15;
1074 | ```
1075 |
1076 | - [11.2](#11.2) 現在還不要使用產生器。
1077 |
1078 | > 為什麼?因為它現在編譯至 ES5 還沒有編譯得非常好。
1079 |
1080 | **[⬆ 回到頂端](#table-of-contents)**
1081 |
1082 |
1083 | ## 屬性
1084 |
1085 | - [12.1](#12.1) 使用點 `.` 來存取屬性。eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation)
1086 |
1087 | ```javascript
1088 | const luke = {
1089 | jedi: true,
1090 | age: 28,
1091 | };
1092 |
1093 | // bad
1094 | const isJedi = luke['jedi'];
1095 |
1096 | // good
1097 | const isJedi = luke.jedi;
1098 | ```
1099 |
1100 | - [12.2](#12.2) 需要帶參數存取屬性時請使用中括號 `[]`。
1101 |
1102 | ```javascript
1103 | const luke = {
1104 | jedi: true,
1105 | age: 28,
1106 | };
1107 |
1108 | function getProp(prop) {
1109 | return luke[prop];
1110 | }
1111 |
1112 | const isJedi = getProp('jedi');
1113 | ```
1114 |
1115 | **[⬆ 回到頂端](#table-of-contents)**
1116 |
1117 |
1118 | ## 變數
1119 |
1120 | - [13.1](#13.1) 為了避免污染全域的命名空間,請使用 `const` 來宣告變數,如果不這麼做將會產生全域變數。Captain Planet warned us of that.
1121 |
1122 | ```javascript
1123 | // bad
1124 | superPower = new SuperPower();
1125 |
1126 | // good
1127 | const superPower = new SuperPower();
1128 | ```
1129 |
1130 | - [13.2](#13.2) 每個變數只使用一個 `const` 來宣告。eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl)
1131 |
1132 | > 為什麼?因為這樣更容易增加新的變數宣告,而且你也不用擔心替換 `;` 為 `,` 及加入的標點符號不同的問題。
1133 |
1134 | ```javascript
1135 | // bad
1136 | const items = getItems(),
1137 | goSportsTeam = true,
1138 | dragonball = 'z';
1139 |
1140 | // bad
1141 | // (比較上述例子找出錯誤)
1142 | const items = getItems(),
1143 | goSportsTeam = true;
1144 | dragonball = 'z';
1145 |
1146 | // good
1147 | const items = getItems();
1148 | const goSportsTeam = true;
1149 | const dragonball = 'z';
1150 | ```
1151 |
1152 | - [13.3](#13.3) 將所有的 `const` 及 `let` 分組。
1153 |
1154 | > 為什麼?當你需要根據之前已賦值的變數來賦值給未賦值變數時相當有幫助。
1155 |
1156 | ```javascript
1157 | // bad
1158 | let i, len, dragonball,
1159 | items = getItems(),
1160 | goSportsTeam = true;
1161 |
1162 | // bad
1163 | let i;
1164 | const items = getItems();
1165 | let dragonball;
1166 | const goSportsTeam = true;
1167 | let len;
1168 |
1169 | // good
1170 | const goSportsTeam = true;
1171 | const items = getItems();
1172 | let dragonball;
1173 | let i;
1174 | let length;
1175 | ```
1176 |
1177 | - [13.4](#13.4) 在你需要的地方賦值給變數,但是請把它們放在合理的位置。
1178 |
1179 | > 為什麼?因為 `let` 及 `const` 是在區塊作用域內,而不是函式作用域。
1180 |
1181 | ```javascript
1182 | // bad - unnecessary function call
1183 | function checkName(hasName) {
1184 | const name = getName();
1185 |
1186 | if (hasName === 'test') {
1187 | return false;
1188 | }
1189 |
1190 | if (name === 'test') {
1191 | this.setName('');
1192 | return false;
1193 | }
1194 |
1195 | return name;
1196 | }
1197 |
1198 | // good
1199 | function checkName(hasName) {
1200 | if (hasName === 'test') {
1201 | return false;
1202 | }
1203 |
1204 | const name = getName();
1205 |
1206 | if (name === 'test') {
1207 | this.setName('');
1208 | return false;
1209 | }
1210 |
1211 | return name;
1212 | }
1213 | ```
1214 |
1215 | **[⬆ 回到頂端](#table-of-contents)**
1216 |
1217 |
1218 | ## 提升
1219 |
1220 | - [14.1](#14.1) `var` 宣告可以被提升至該作用域的最頂層,但賦予的值並不會。`const` 及 `let` 的宣告被賦予了新的概念,稱為[暫時性死區(Temporal Dead Zones, TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let)。這對於瞭解為什麼 [typeof 不再那麼安全](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15)是相當重要的。
1221 |
1222 | ```javascript
1223 | // 我們知道這樣是行不通的
1224 | // (假設沒有名為 notDefined 的全域變數)
1225 | function example() {
1226 | console.log(notDefined); // => throws a ReferenceError
1227 | }
1228 |
1229 | // 由於變數提升的關係,
1230 | // 你在引用變數後再宣告變數是行得通的。
1231 | // 注:賦予給變數的 `true` 並不會被提升。
1232 | function example() {
1233 | console.log(declaredButNotAssigned); // => undefined
1234 | var declaredButNotAssigned = true;
1235 | }
1236 |
1237 | // 直譯器會將宣告的變數提升至作用域的最頂層,
1238 | // 表示我們可以將這個例子改寫成以下:
1239 | function example() {
1240 | let declaredButNotAssigned;
1241 | console.log(declaredButNotAssigned); // => undefined
1242 | declaredButNotAssigned = true;
1243 | }
1244 |
1245 | // 使用 const 及 let
1246 | function example() {
1247 | console.log(declaredButNotAssigned); // => throws a ReferenceError
1248 | console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
1249 | const declaredButNotAssigned = true;
1250 | }
1251 | ```
1252 |
1253 | - [14.2](#14.2) 賦予匿名函式的變數會被提升,但函式內容並不會。
1254 |
1255 | ```javascript
1256 | function example() {
1257 | console.log(anonymous); // => undefined
1258 |
1259 | anonymous(); // => TypeError anonymous is not a function
1260 |
1261 | var anonymous = function () {
1262 | console.log('anonymous function expression');
1263 | };
1264 | }
1265 | ```
1266 |
1267 | - [14.3](#14.3) 賦予命名函式的變數會被提升,但函式內容及函式名稱並不會。
1268 |
1269 | ```javascript
1270 | function example() {
1271 | console.log(named); // => undefined
1272 |
1273 | named(); // => TypeError named is not a function
1274 |
1275 | superPower(); // => ReferenceError superPower is not defined
1276 |
1277 | var named = function superPower() {
1278 | console.log('Flying');
1279 | };
1280 | }
1281 |
1282 | // 當函式名稱和變數名稱相同時也是如此。
1283 | function example() {
1284 | console.log(named); // => undefined
1285 |
1286 | named(); // => TypeError named is not a function
1287 |
1288 | var named = function named() {
1289 | console.log('named');
1290 | }
1291 | }
1292 | ```
1293 |
1294 | - [14.4](#14.4) 宣告函式的名稱及函式內容都會被提升。
1295 |
1296 | ```javascript
1297 | function example() {
1298 | superPower(); // => Flying
1299 |
1300 | function superPower() {
1301 | console.log('Flying');
1302 | }
1303 | }
1304 | ```
1305 |
1306 | - 想瞭解更多訊息,請參考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/)。
1307 |
1308 | **[⬆ 回到頂端](#table-of-contents)**
1309 |
1310 |
1311 | ## 條件式與等號
1312 |
1313 | ## Comparison Operators & Equality
1314 |
1315 | - [15.1](#15.1) 請使用 `===` 和 `!==` ,別使用 `==` 及 `!=` 。eslint: [`eqeqeq`](http://eslint.org/docs/rules/eqeqeq.html)
1316 |
1317 | - [15.2](#15.2) 像是 `if` 的條件語法內會使用 `ToBoolean` 的抽象方法強轉類型,並遵循以下規範:
1318 |
1319 | + **物件** 轉換為 **true**
1320 | + **Undefined** 轉換為 **false**
1321 | + **Null** 轉換為 **false**
1322 | + **布林** 轉換為 **該布林值**
1323 | + **數字** 如果是 **+0, -0, 或 NaN** 則轉換為 **false**,其他的皆為 **true**
1324 | + **字串** 如果是空字串 `''` 則轉換為 **false**,其他的皆為 **true**
1325 |
1326 | ```javascript
1327 | if ([0] && []) {
1328 | // true
1329 | // 陣列(即使為空)為一個物件,所以轉換為 true
1330 | }
1331 | ```
1332 |
1333 | - [15.3](#15.3) 使用簡短的方式。
1334 |
1335 | ```javascript
1336 | // bad
1337 | if (name !== '') {
1338 | // ...stuff...
1339 | }
1340 |
1341 | // good
1342 | if (name) {
1343 | // ...stuff...
1344 | }
1345 |
1346 | // bad
1347 | if (collection.length > 0) {
1348 | // ...stuff...
1349 | }
1350 |
1351 | // good
1352 | if (collection.length) {
1353 | // ...stuff...
1354 | }
1355 | ```
1356 |
1357 | - [15.4](#15.4) 想瞭解更多訊息請參考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。
1358 | - [15.5](#15.5) Use braces to create blocks in `case` and `default` clauses that contain lexical declarations (e.g. `let`, `const`, `function`, and `class`).
1359 | - [15.6](#15.6) 若 `case` 與 `default` 包含了宣告語法(例如:`let`、`const`、`function` 及 `class`)時使用大括號來建立區塊。
1360 |
1361 | > Why? Lexical declarations are visible in the entire `switch` block but only get initialized when assigned, which only happens when its `case` is reached. This causes problems when multiple `case` clauses attempt to define the same thing.
1362 | > 為什麼?宣告語法可以在整個 `switch` 區塊中可見,但是只在進入該 `case` 時初始化。當多個 `case` 語法時會導致嘗試定義相同事情的問題。
1363 |
1364 | eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html).
1365 |
1366 | ```javascript
1367 | // bad
1368 | switch (foo) {
1369 | case 1:
1370 | let x = 1;
1371 | break;
1372 | case 2:
1373 | const y = 2;
1374 | break;
1375 | case 3:
1376 | function f() {}
1377 | break;
1378 | default:
1379 | class C {}
1380 | }
1381 |
1382 | // good
1383 | switch (foo) {
1384 | case 1: {
1385 | let x = 1;
1386 | break;
1387 | }
1388 | case 2: {
1389 | const y = 2;
1390 | break;
1391 | }
1392 | case 3: {
1393 | function f() {}
1394 | break;
1395 | }
1396 | case 4:
1397 | bar();
1398 | break;
1399 | default: {
1400 | class C {}
1401 | }
1402 | }
1403 | ```
1404 |
1405 | - [15.7](#15.7) 不應該使用巢狀的三元運算子,且通常應該使用單行來表示。
1406 |
1407 | eslint rules: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html).
1408 |
1409 | ```javascript
1410 | // bad
1411 | const foo = maybe1 > maybe2
1412 | ? "bar"
1413 | : value1 > value2 ? "baz" : null;
1414 |
1415 | // better
1416 | const maybeNull = value1 > value2 ? 'baz' : null;
1417 |
1418 | const foo = maybe1 > maybe2
1419 | ? 'bar'
1420 | : maybeNull;
1421 |
1422 | // best
1423 | const maybeNull = value1 > value2 ? 'baz' : null;
1424 |
1425 | const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
1426 | ```
1427 |
1428 | - [15.8](#15.8) 避免不必要的三元運算子語法。
1429 |
1430 | eslint rules: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html).
1431 |
1432 | ```javascript
1433 | // bad
1434 | const foo = a ? a : b;
1435 | const bar = c ? true : false;
1436 | const baz = c ? false : true;
1437 |
1438 | // good
1439 | const foo = a || b;
1440 | const bar = !!c;
1441 | const baz = !c;
1442 | ```
1443 |
1444 | **[⬆ 回到頂端](#table-of-contents)**
1445 |
1446 |
1447 | ## 區塊
1448 |
1449 | - [16.1](#16.1) 多行區塊請使用大括號刮起來。
1450 |
1451 | ```javascript
1452 | // bad
1453 | if (test)
1454 | return false;
1455 |
1456 | // good
1457 | if (test) return false;
1458 |
1459 | // good
1460 | if (test) {
1461 | return false;
1462 | }
1463 |
1464 | // bad
1465 | function foo() { return false; }
1466 |
1467 | // good
1468 | function bar() {
1469 | return false;
1470 | }
1471 | ```
1472 |
1473 | - [16.2](#16.2) 如果你使用 `if` 及 `else` 的多行區塊,請將 `else` 放在 `if` 區塊的結尾大括號後。eslint: [`brace-style`](http://eslint.org/docs/rules/brace-style.html) jscs: [`disallowNewlineBeforeBlockStatements`](http://jscs.info/rule/disallowNewlineBeforeBlockStatements)
1474 |
1475 | ```javascript
1476 | // bad
1477 | if (test) {
1478 | thing1();
1479 | thing2();
1480 | }
1481 | else {
1482 | thing3();
1483 | }
1484 |
1485 | // good
1486 | if (test) {
1487 | thing1();
1488 | thing2();
1489 | } else {
1490 | thing3();
1491 | }
1492 | ```
1493 |
1494 | **[⬆ 回到頂端](#table-of-contents)**
1495 |
1496 |
1497 | ## 控制陳述式
1498 |
1499 | - [17.1](#17.1) 為避免控制陳述式(`if`、`while` 等)太長或超過該行字數限制,每組條件式可自成一行。邏輯運算子應置於行首。
1500 |
1501 | > 為什麼?行首的運算子可維持版面整齊,遵守和方法鏈類似的排版模式。還能提供視覺線索,讓複雜的邏輯述句更容易閱讀。
1502 |
1503 | ```javascript
1504 | // bad
1505 | if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
1506 | thing1();
1507 | }
1508 |
1509 | // bad
1510 | if (foo === 123 &&
1511 | bar === 'abc') {
1512 | thing1();
1513 | }
1514 |
1515 | // bad
1516 | if (foo === 123
1517 | && bar === 'abc') {
1518 | thing1();
1519 | }
1520 |
1521 | // bad
1522 | if (
1523 | foo === 123 &&
1524 | bar === 'abc'
1525 | ) {
1526 | thing1();
1527 | }
1528 |
1529 | // good
1530 | if (
1531 | foo === 123
1532 | && bar === 'abc'
1533 | ) {
1534 | thing1();
1535 | }
1536 |
1537 | // good
1538 | if (
1539 | (foo === 123 || bar === 'abc')
1540 | && doesItLookGoodWhenItBecomesThatLong()
1541 | && isThisReallyHappening()
1542 | ) {
1543 | thing1();
1544 | }
1545 |
1546 | // good
1547 | if (foo === 123 && bar === 'abc') {
1548 | thing1();
1549 | }
1550 | ```
1551 |
1552 | - [17.2](#17.2) 不要用選擇運算子(selection operators)來取代控制陳述式。
1553 |
1554 | ```javascript
1555 | // bad
1556 | !isRunning && startRunning();
1557 |
1558 | // good
1559 | if (!isRunning) {
1560 | startRunning();
1561 | }
1562 | ```
1563 |
1564 | **[⬆ 回到頂端](#table-of-contents)**
1565 |
1566 |
1567 | ## 註解
1568 |
1569 | - [18.1](#18.1) 多行註解請使用 `/** ... */` ,包含描述,指定類型以及參數值還有回傳值。
1570 |
1571 | ```javascript
1572 | // bad
1573 | // make() 根據傳入的 tag 名稱回傳一個新的元件
1574 | //
1575 | // @param {String} tag
1576 | // @return {Element} element
1577 | function make(tag) {
1578 |
1579 | // ...stuff...
1580 |
1581 | return element;
1582 | }
1583 |
1584 | // good
1585 | /**
1586 | * make() 根據傳入的 tag 名稱回傳一個新的元件
1587 | *
1588 | * @param {String} tag
1589 | * @return {Element} element
1590 | */
1591 | function make(tag) {
1592 |
1593 | // ...stuff...
1594 |
1595 | return element;
1596 | }
1597 | ```
1598 |
1599 | - [18.2](#18.2) 單行註解請使用 `//`。在欲註解的上方新增一行進行註解。在註解的上方空一行,除非他在區塊的第一行。
1600 |
1601 | ```javascript
1602 | // bad
1603 | const active = true; // 當目前分頁
1604 |
1605 | // good
1606 | // is current tab
1607 | const active = true;
1608 |
1609 | // bad
1610 | function getType() {
1611 | console.log('fetching type...');
1612 | // 設定預設的類型為 'no type'
1613 | const type = this._type || 'no type';
1614 |
1615 | return type;
1616 | }
1617 |
1618 | // good
1619 | function getType() {
1620 | console.log('fetching type...');
1621 |
1622 | // 設定預設的類型為 'no type'
1623 | const type = this._type || 'no type';
1624 |
1625 | return type;
1626 | }
1627 |
1628 | // also good
1629 | function getType() {
1630 | // set the default type to 'no type'
1631 | const type = this._type || 'no type';
1632 |
1633 | return type;
1634 | }
1635 | ```
1636 |
1637 | - [18.3](#18.3) 在註解前方加上 `FIXME` 或 `TODO` 可以幫助其他開發人員快速瞭解這是一個需要重新討論的問題,或是一個等待解決的問題。和一般的註解不同,他們是可被執行的。對應的動作為 `FIXME -- 重新討論並解決` 或 `TODO -- 必須執行`。
1638 |
1639 | - [18.4](#18.4) 使用 `// FIXME:` 標注問題。
1640 |
1641 | ```javascript
1642 | class Calculator extends Abacus {
1643 | constructor() {
1644 | super();
1645 |
1646 | // FIXME: 不該在這使用全域變數
1647 | total = 0;
1648 | }
1649 | }
1650 | ```
1651 |
1652 | - [18.5](#18.5) 使用 `// TODO:` 標注問題的解決方式。
1653 |
1654 | ```javascript
1655 | class Calculator extends Abacus {
1656 | constructor() {
1657 | super();
1658 |
1659 | // TODO: total 應該可被傳入的參數所修改
1660 | this.total = 0;
1661 | }
1662 | }
1663 | ```
1664 |
1665 | **[⬆ 回到頂端](#table-of-contents)**
1666 |
1667 |
1668 | ## 空格
1669 |
1670 | - [19.1](#19.1) 將 Tab 設定為兩個空格。eslint: [`indent`](http://eslint.org/docs/rules/indent.html) jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation)
1671 |
1672 | ```javascript
1673 | // bad
1674 | function foo() {
1675 | ∙∙∙∙const name;
1676 | }
1677 |
1678 | // bad
1679 | function bar() {
1680 | ∙const name;
1681 | }
1682 |
1683 | // good
1684 | function baz() {
1685 | ∙∙const name;
1686 | }
1687 | ```
1688 |
1689 | - [19.2](#19.2) 在大括號前加一個空格。eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements)
1690 |
1691 | ```javascript
1692 | // bad
1693 | function test(){
1694 | console.log('test');
1695 | }
1696 |
1697 | // good
1698 | function test() {
1699 | console.log('test');
1700 | }
1701 |
1702 | // bad
1703 | dog.set('attr',{
1704 | age: '1 year',
1705 | breed: 'Bernese Mountain Dog',
1706 | });
1707 |
1708 | // good
1709 | dog.set('attr', {
1710 | age: '1 year',
1711 | breed: 'Bernese Mountain Dog',
1712 | });
1713 | ```
1714 |
1715 | - [19.3](#19.3) 在控制流程的語句(`if`, `while` 等等。)的左括號前加上一個空格。宣告的函式和傳入的變數間則沒有空格。eslint: [`space-after-keywords`](http://eslint.org/docs/rules/space-after-keywords.html), [`space-before-keywords`](http://eslint.org/docs/rules/space-before-keywords.html) jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords)
1716 |
1717 | ```javascript
1718 | // bad
1719 | if(isJedi) {
1720 | fight ();
1721 | }
1722 |
1723 | // good
1724 | if (isJedi) {
1725 | fight();
1726 | }
1727 |
1728 | // bad
1729 | function fight () {
1730 | console.log ('Swooosh!');
1731 | }
1732 |
1733 | // good
1734 | function fight() {
1735 | console.log('Swooosh!');
1736 | }
1737 | ```
1738 |
1739 | - [19.4](#19.4) 將運算元用空格隔開。eslint: [`space-infix-ops`](http://eslint.org/docs/rules/space-infix-ops.html) jscs: [`requireSpaceBeforeBinaryOperators`](http://jscs.info/rule/requireSpaceBeforeBinaryOperators), [`requireSpaceAfterBinaryOperators`](http://jscs.info/rule/requireSpaceAfterBinaryOperators)
1740 |
1741 | ```javascript
1742 | // bad
1743 | const x=y+5;
1744 |
1745 | // good
1746 | const x = y + 5;
1747 | ```
1748 |
1749 | - [19.5](#19.5) 在檔案的最尾端加上一行空白行。
1750 |
1751 | ```javascript
1752 | // bad
1753 | (function (global) {
1754 | // ...stuff...
1755 | })(this);
1756 | ```
1757 |
1758 | ```javascript
1759 | // bad
1760 | (function (global) {
1761 | // ...stuff...
1762 | })(this);↵
1763 | ↵
1764 | ```
1765 |
1766 | ```javascript
1767 | // good
1768 | (function (global) {
1769 | // ...stuff...
1770 | })(this);↵
1771 | ```
1772 |
1773 | - [19.6](#19.6) 當多個方法鏈結(大於兩個方法鏈結)時請換行縮排。利用前面的 `.` 強調該行是呼叫方法,而不是一個新的宣告。eslint: [`newline-per-chained-call`](http://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](http://eslint.org/docs/rules/no-whitespace-before-property)
1774 |
1775 | ```javascript
1776 | // bad
1777 | $('#items').find('.selected').highlight().end().find('.open').updateCount();
1778 |
1779 | // bad
1780 | $('#items').
1781 | find('.selected').
1782 | highlight().
1783 | end().
1784 | find('.open').
1785 | updateCount();
1786 |
1787 | // good
1788 | $('#items')
1789 | .find('.selected')
1790 | .highlight()
1791 | .end()
1792 | .find('.open')
1793 | .updateCount();
1794 |
1795 | // bad
1796 | const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
1797 | .attr('width', (radius + margin) * 2).append('svg:g')
1798 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
1799 | .call(tron.led);
1800 |
1801 | // good
1802 | const leds = stage.selectAll('.led')
1803 | .data(data)
1804 | .enter().append('svg:svg')
1805 | .classed('led', true)
1806 | .attr('width', (radius + margin) * 2)
1807 | .append('svg:g')
1808 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
1809 | .call(tron.led);
1810 |
1811 | // good
1812 | const leds = stage.selectAll('.led').data(data);
1813 | ```
1814 |
1815 | - [19.7](#19.7) 在區塊的結束及下個語法間加上空行。jscs: [`requirePaddingNewLinesAfterBlocks`](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks)
1816 |
1817 | ```javascript
1818 | // bad
1819 | if (foo) {
1820 | return bar;
1821 | }
1822 | return baz;
1823 |
1824 | // good
1825 | if (foo) {
1826 | return bar;
1827 | }
1828 |
1829 | return baz;
1830 |
1831 | // bad
1832 | const obj = {
1833 | foo() {
1834 | },
1835 | bar() {
1836 | },
1837 | };
1838 | return obj;
1839 |
1840 | // good
1841 | const obj = {
1842 | foo() {
1843 | },
1844 |
1845 | bar() {
1846 | },
1847 | };
1848 |
1849 | return obj;
1850 |
1851 | // bad
1852 | const arr = [
1853 | function foo() {
1854 | },
1855 | function bar() {
1856 | },
1857 | ];
1858 | return arr;
1859 |
1860 | // good
1861 | const arr = [
1862 | function foo() {
1863 | },
1864 |
1865 | function bar() {
1866 | },
1867 | ];
1868 |
1869 | return arr;
1870 | ```
1871 |
1872 | - [19.8](#19.8) 別在區塊中置放空行。eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks)
1873 |
1874 | ```javascript
1875 | // bad
1876 | function bar() {
1877 |
1878 | console.log(foo);
1879 |
1880 | }
1881 |
1882 | // also bad
1883 | if (baz) {
1884 |
1885 | console.log(qux);
1886 | } else {
1887 | console.log(foo);
1888 |
1889 | }
1890 |
1891 | // good
1892 | function bar() {
1893 | console.log(foo);
1894 | }
1895 |
1896 | // good
1897 | if (baz) {
1898 | console.log(qux);
1899 | } else {
1900 | console.log(foo);
1901 | }
1902 | ```
1903 |
1904 | - [19.9](#19.9) 不要在括號內的兩側置放空格。eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses)
1905 |
1906 | ```javascript
1907 | // bad
1908 | function bar( foo ) {
1909 | return foo;
1910 | }
1911 |
1912 | // good
1913 | function bar(foo) {
1914 | return foo;
1915 | }
1916 |
1917 | // bad
1918 | if ( foo ) {
1919 | console.log(foo);
1920 | }
1921 |
1922 | // good
1923 | if (foo) {
1924 | console.log(foo);
1925 | }
1926 | ```
1927 |
1928 | - [19.10](#19.10) 不要在中括號內的兩側置放空格。eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets)
1929 |
1930 | ```javascript
1931 | // bad
1932 | const foo = [ 1, 2, 3 ];
1933 | console.log(foo[ 0 ]);
1934 |
1935 | // good
1936 | const foo = [1, 2, 3];
1937 | console.log(foo[0]);
1938 | ```
1939 |
1940 | - [19.11](#19.11) 在大括號內的兩側置放空格。eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`disallowSpacesInsideObjectBrackets`](http://jscs.info/rule/
1941 |
1942 | ```javascript
1943 | // bad
1944 | const foo = {clark: 'kent'};
1945 |
1946 | // good
1947 | const foo = { clark: 'kent' };
1948 | ```
1949 |
1950 | - [19.12](#19.12) 避免一行的程式碼超過 100 字元(包含空白)。eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength)
1951 |
1952 | > 為什麼?這樣確保可讀性及維護性。
1953 |
1954 | ```javascript
1955 | // bad
1956 | const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';
1957 |
1958 | // bad
1959 | $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
1960 |
1961 | // good
1962 | const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
1963 | 'Whatever wizard constrains a helpful ally. The counterpart ascends!';
1964 |
1965 | // good
1966 | $.ajax({
1967 | method: 'POST',
1968 | url: 'https://airbnb.com/',
1969 | data: { name: 'John' },
1970 | })
1971 | .done(() => console.log('Congratulations!'))
1972 | .fail(() => console.log('You have failed this city.'));
1973 | ```
1974 |
1975 | **[⬆ 回到頂端](#table-of-contents)**
1976 |
1977 |
1978 | ## 逗號
1979 |
1980 | - [20.1](#20.1) 不要將逗號放在前方。eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak)
1981 |
1982 | ```javascript
1983 | // bad
1984 | const story = [
1985 | once
1986 | , upon
1987 | , aTime
1988 | ];
1989 |
1990 | // good
1991 | const story = [
1992 | once,
1993 | upon,
1994 | aTime,
1995 | ];
1996 |
1997 | // bad
1998 | const hero = {
1999 | firstName: 'Ada'
2000 | , lastName: 'Lovelace'
2001 | , birthYear: 1815
2002 | , superPower: 'computers'
2003 | };
2004 |
2005 | // good
2006 | const hero = {
2007 | firstName: 'Ada',
2008 | lastName: 'Lovelace',
2009 | birthYear: 1815,
2010 | superPower: 'computers',
2011 | };
2012 | ```
2013 |
2014 | - [20.2](#20.2) 增加結尾的逗號:**別懷疑**eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma)
2015 |
2016 | > 為什麼?這會讓 Git 的差異列表更乾淨。另外,Babel 轉譯器也會刪除結尾多餘的逗號,也就是說你完全不需要擔心在老舊的瀏覽器發生[多餘逗號的問題](es5/README.md#commas)。
2017 |
2018 | ```javascript
2019 | // bad - 不含多餘逗號的 git 差異列表
2020 | const hero = {
2021 | firstName: 'Florence',
2022 | - lastName: 'Nightingale'
2023 | + lastName: 'Nightingale',
2024 | + inventorOf: ['coxcomb graph', 'modern nursing']
2025 | };
2026 |
2027 | // good - 包含多餘逗號的 git 差異列表
2028 | const hero = {
2029 | firstName: 'Florence',
2030 | lastName: 'Nightingale',
2031 | + inventorOf: ['coxcomb chart', 'modern nursing'],
2032 | };
2033 |
2034 | // bad
2035 | const hero = {
2036 | firstName: 'Dana',
2037 | lastName: 'Scully'
2038 | };
2039 |
2040 | const heroes = [
2041 | 'Batman',
2042 | 'Superman'
2043 | ];
2044 |
2045 | // good
2046 | const hero = {
2047 | firstName: 'Dana',
2048 | lastName: 'Scully',
2049 | };
2050 |
2051 | const heroes = [
2052 | 'Batman',
2053 | 'Superman',
2054 | ];
2055 | ```
2056 |
2057 | **[⬆ 回到頂端](#table-of-contents)**
2058 |
2059 |
2060 | ## 分號
2061 |
2062 | - [21.1](#21.1) **對啦。**eslint: [`semi`](http://eslint.org/docs/rules/semi.html) jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons)
2063 |
2064 | ```javascript
2065 | // bad
2066 | (function () {
2067 | const name = 'Skywalker'
2068 | return name
2069 | })()
2070 |
2071 | // good
2072 | (() => {
2073 | const name = 'Skywalker';
2074 | return name;
2075 | }());
2076 |
2077 | // good(防止當兩個檔案含有立即函式需要合併時,函式被當成參數處理)
2078 | ;(() => {
2079 | const name = 'Skywalker';
2080 | return name;
2081 | }());
2082 | ```
2083 |
2084 | [瞭解更多](http://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214)。
2085 |
2086 | **[⬆ 回到頂端](#table-of-contents)**
2087 |
2088 |
2089 | ## 型別轉換
2090 |
2091 | - [22.1](#22.1) 在開頭的宣告進行強制型別轉換。
2092 | - [22.2](#22.2) 字串:
2093 |
2094 | ```javascript
2095 | // => this.reviewScore = 9;
2096 |
2097 | // bad
2098 | const totalScore = this.reviewScore + '';
2099 |
2100 | // good
2101 | const totalScore = String(this.reviewScore);
2102 | ```
2103 |
2104 | - [22.3](#22.3) 數字:使用 `Number` 做型別轉換,而 `parseInt` 則始終以基數解析字串。eslint: [`radix`](http://eslint.org/docs/rules/radix)
2105 |
2106 | ```javascript
2107 | const inputValue = '4';
2108 |
2109 | // bad
2110 | const val = new Number(inputValue);
2111 |
2112 | // bad
2113 | const val = +inputValue;
2114 |
2115 | // bad
2116 | const val = inputValue >> 0;
2117 |
2118 | // bad
2119 | const val = parseInt(inputValue);
2120 |
2121 | // good
2122 | const val = Number(inputValue);
2123 |
2124 | // good
2125 | const val = parseInt(inputValue, 10);
2126 | ```
2127 |
2128 | - [22.4](#22.4) 如果你因為某個原因在做些瘋狂的事情,但是 `parseInt` 是你的瓶頸,所以你對於[性能方面的原因](http://jsperf.com/coercion-vs-casting/3)而必須使用位元右移,請留下評論並解釋為什麼使用,及你做了哪些事情。
2129 |
2130 | ```javascript
2131 | // good
2132 | /**
2133 | * 使用 parseInt 導致我的程式變慢,改成使用
2134 | * 位元右移強制將字串轉為數字加快了他的速度。
2135 | */
2136 | const val = inputValue >> 0;
2137 | ```
2138 |
2139 | - [22.5](#22.5) **注意:**使用位元轉換時請小心。數字為 [64 位元數值](http://es5.github.io/#x4.3.19),但是使用位元轉換時則會回傳一個 32 位元的整數([來源](http://es5.github.io/#x11.7)),這會導致大於 32 位元的數值產生異常 [討論串](https://github.com/airbnb/javascript/issues/109),32 位元的整數最大值為 2,147,483,647:
2140 |
2141 | ```javascript
2142 | 2147483647 >> 0 //=> 2147483647
2143 | 2147483648 >> 0 //=> -2147483648
2144 | 2147483649 >> 0 //=> -2147483647
2145 | ```
2146 |
2147 | - [22.6](#22.6) 布林:
2148 |
2149 | ```javascript
2150 | const age = 0;
2151 |
2152 | // bad
2153 | const hasAge = new Boolean(age);
2154 |
2155 | // good
2156 | const hasAge = Boolean(age);
2157 |
2158 | // good
2159 | const hasAge = !!age;
2160 | ```
2161 |
2162 | **[⬆ 回到頂端](#table-of-contents)**
2163 |
2164 |
2165 | ## 命名規則
2166 |
2167 | - [23.1](#23.1) 避免使用單一字母的名稱,讓你的名稱有解釋的含義。
2168 |
2169 | ```javascript
2170 | // bad
2171 | function q() {
2172 | // ...stuff...
2173 | }
2174 |
2175 | // good
2176 | function query() {
2177 | // ..stuff..
2178 | }
2179 | ```
2180 |
2181 | - [23.2](#23.2) 使用駝峰式大小寫命名物件,函式及實例。eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers)
2182 |
2183 | ```javascript
2184 | // bad
2185 | const OBJEcttsssss = {};
2186 | const this_is_my_object = {};
2187 | function c() {}
2188 |
2189 | // good
2190 | const thisIsMyObject = {};
2191 | function thisIsMyFunction() {}
2192 | ```
2193 |
2194 | - [23.3](#23.3) 使用帕斯卡命名法來命名建構子或類別。eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors)
2195 |
2196 | ```javascript
2197 | // bad
2198 | function user(options) {
2199 | this.name = options.name;
2200 | }
2201 |
2202 | const bad = new user({
2203 | name: 'nope',
2204 | });
2205 |
2206 | // good
2207 | class User {
2208 | constructor(options) {
2209 | this.name = options.name;
2210 | }
2211 | }
2212 |
2213 | const good = new User({
2214 | name: 'yup',
2215 | });
2216 | ```
2217 |
2218 | - [23.4](#23.4) 命名私有屬性時請在前面加底線 `_`。eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores)
2219 |
2220 | ```javascript
2221 | // bad
2222 | this.__firstName__ = 'Panda';
2223 | this.firstName_ = 'Panda';
2224 |
2225 | // good
2226 | this._firstName = 'Panda';
2227 | ```
2228 |
2229 | - [23.5](#23.5) 請別儲存 `this` 為參考。請使用箭頭函式或是 Function#bind。jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes)
2230 |
2231 | ```javascript
2232 | // bad
2233 | function foo() {
2234 | const self = this;
2235 | return function () {
2236 | console.log(self);
2237 | };
2238 | }
2239 |
2240 | // bad
2241 | function foo() {
2242 | const that = this;
2243 | return function () {
2244 | console.log(that);
2245 | };
2246 | }
2247 |
2248 | // good
2249 | function foo() {
2250 | return () => {
2251 | console.log(this);
2252 | };
2253 | }
2254 | ```
2255 |
2256 | - [23.6](#23.6) 如果你的檔案只有輸出一個類別,你的檔案名稱必須和你的類別名稱相同。
2257 |
2258 | ```javascript
2259 | // 檔案內容
2260 | class CheckBox {
2261 | // ...
2262 | }
2263 | export default CheckBox;
2264 |
2265 | // 在其他的檔案
2266 | // bad
2267 | import CheckBox from './checkBox';
2268 |
2269 | // bad
2270 | import CheckBox from './check_box';
2271 |
2272 | // good
2273 | import CheckBox from './CheckBox';
2274 | ```
2275 |
2276 | - [23.7](#23.7) 當你導出為預設的函式時請使用駝峰式大小寫。檔案名稱必須與你的函式名稱一致。
2277 |
2278 | ```javascript
2279 | function makeStyleGuide() {
2280 | }
2281 |
2282 | export default makeStyleGuide;
2283 | ```
2284 |
2285 | - [23.8](#23.8) 當你導出為單例 / 函式庫 / 空物件時請使用帕斯卡命名法。
2286 |
2287 | ```javascript
2288 | const AirbnbStyleGuide = {
2289 | es6: {
2290 | }
2291 | };
2292 |
2293 | export default AirbnbStyleGuide;
2294 | ```
2295 |
2296 |
2297 | **[⬆ 回到頂端](#table-of-contents)**
2298 |
2299 |
2300 | ## 存取器
2301 |
2302 | - [24.1](#24.1) 屬性的存取器函式不是必須的。
2303 | - [24.2](#24.2) 別使用 JavaScript 的 getters 或 setters,因為它們會導致意想不到的副作用,而且不易於測試、維護以及進行推測。取而代之,如果你要建立一個存取器函式,請使用 getVal() 及 setVal('hello')。
2304 |
2305 | ```javascript
2306 | // bad
2307 | dragon.age();
2308 |
2309 | // good
2310 | dragon.getAge();
2311 |
2312 | // bad
2313 | dragon.age(25);
2314 |
2315 | // good
2316 | dragon.setAge(25);
2317 | ```
2318 |
2319 | - [24.3](#24.3) 如果屬性是布林,請使用 `isVal()` 或 `hasVal()`。
2320 |
2321 | ```javascript
2322 | // bad
2323 | if (!dragon.age()) {
2324 | return false;
2325 | }
2326 |
2327 | // good
2328 | if (!dragon.hasAge()) {
2329 | return false;
2330 | }
2331 | ```
2332 |
2333 | - [24.4](#24.4) 可以建立 get() 及 set() 函式,但請保持一致。
2334 |
2335 | ```javascript
2336 | class Jedi {
2337 | constructor(options = {}) {
2338 | const lightsaber = options.lightsaber || 'blue';
2339 | this.set('lightsaber', lightsaber);
2340 | }
2341 |
2342 | set(key, val) {
2343 | this[key] = val;
2344 | }
2345 |
2346 | get(key) {
2347 | return this[key];
2348 | }
2349 | }
2350 | ```
2351 |
2352 | **[⬆ 回到頂端](#table-of-contents)**
2353 |
2354 |
2355 | ## 事件
2356 |
2357 | - [25.1](#25.1) 當需要對事件傳入資料時(不論是 DOM 事件或是其他私有事件),請傳入物件替代單一的資料。這樣可以使之後的開發人員直接加入其他的資料到事件裡,而不需更新該事件的處理器。例如,比較不好的做法:
2358 |
2359 | ```javascript
2360 | // bad
2361 | $(this).trigger('listingUpdated', listing.id);
2362 |
2363 | ...
2364 |
2365 | $(this).on('listingUpdated', (e, listingId) => {
2366 | // do something with listingId
2367 | });
2368 | ```
2369 |
2370 | 更好的做法:
2371 |
2372 | ```javascript
2373 | // good
2374 | $(this).trigger('listingUpdated', { listingId: listing.id });
2375 |
2376 | ...
2377 |
2378 | $(this).on('listingUpdated', (e, data) => {
2379 | // do something with data.listingId
2380 | });
2381 | ```
2382 |
2383 | **[⬆ 回到頂端](#table-of-contents)**
2384 |
2385 |
2386 | ## jQuery
2387 |
2388 | - [26.1](#26.1) jQuery 的物件請使用 `$` 當前綴。jscs: [`requireDollarBeforejQueryAssignment`](http://jscs.info/rule/requireDollarBeforejQueryAssignment)
2389 |
2390 | ```javascript
2391 | // bad
2392 | const sidebar = $('.sidebar');
2393 |
2394 | // good
2395 | const $sidebar = $('.sidebar');
2396 |
2397 | // good
2398 | const $sidebarBtn = $('.sidebar-btn');
2399 | ```
2400 |
2401 | - [26.2](#26.2) 快取 jQuery 的查詢。
2402 |
2403 | ```javascript
2404 | // bad
2405 | function setSidebar() {
2406 | $('.sidebar').hide();
2407 |
2408 | // ...stuff...
2409 |
2410 | $('.sidebar').css({
2411 | 'background-color': 'pink'
2412 | });
2413 | }
2414 |
2415 | // good
2416 | function setSidebar() {
2417 | const $sidebar = $('.sidebar');
2418 | $sidebar.hide();
2419 |
2420 | // ...stuff...
2421 |
2422 | $sidebar.css({
2423 | 'background-color': 'pink'
2424 | });
2425 | }
2426 | ```
2427 |
2428 | - [26.3](#26.3) DOM 的查詢請使用層遞的 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。[jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16)
2429 | - [26.4](#26.4) 對作用域內的 jQuery 物件使用 `find` 做查詢。
2430 |
2431 | ```javascript
2432 | // bad
2433 | $('ul', '.sidebar').hide();
2434 |
2435 | // bad
2436 | $('.sidebar').find('ul').hide();
2437 |
2438 | // good
2439 | $('.sidebar ul').hide();
2440 |
2441 | // good
2442 | $('.sidebar > ul').hide();
2443 |
2444 | // good
2445 | $sidebar.find('ul').hide();
2446 | ```
2447 |
2448 | **[⬆ 回到頂端](#table-of-contents)**
2449 |
2450 |
2451 | ## ECMAScript 5 相容性
2452 |
2453 | - [27.1](#27.1) 參考 [Kangax](https://twitter.com/kangax/) 的 ES5 [相容性列表](http://kangax.github.io/es5-compat-table/)。
2454 |
2455 | **[⬆ 回到頂端](#table-of-contents)**
2456 |
2457 |
2458 | ## ECMAScript 6 風格
2459 |
2460 | - [28.1](#28.1) 以下是連結到各個 ES6 特性的列表。
2461 |
2462 | 1. [箭頭函式](#arrow-functions)
2463 | 1. [類別](#constructors)
2464 | 1. [物件簡寫](#es6-object-shorthand)
2465 | 1. [簡潔物件](#es6-object-concise)
2466 | 1. [可計算的物件屬性](#es6-computed-properties)
2467 | 1. [模板字串](#es6-template-literals)
2468 | 1. [解構子](#destructuring)
2469 | 1. [預設參數](#es6-default-parameters)
2470 | 1. [剩餘參數(Rest)](#es6-rest)
2471 | 1. [陣列擴展](#es6-array-spreads)
2472 | 1. [Let 及 Const](#references)
2473 | 1. [迭代器及產生器](#iterators-and-generators)
2474 | 1. [模組](#modules)
2475 |
2476 | **[⬆ 回到頂端](#table-of-contents)**
2477 |
2478 |
2479 | ## 標準程式庫
2480 |
2481 | [標準程式庫(Standard Library)](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects)基於歷史因素,仍保有某些功能有缺陷的函式。
2482 |
2483 | - [29.1](#29.1) 使用 `Number.isNaN` 而非 `isNaN`。
2484 |
2485 | > 為什麼?全域函式 `isNaN` 會先將任何非數值轉換為數值,如果轉換後之值為 NaN,則函式回傳 true。
2486 | > 若真要轉換為數值,請表達清楚。
2487 |
2488 | ```javascript
2489 | // bad
2490 | isNaN('1.2'); // false
2491 | isNaN('1.2.3'); // true
2492 |
2493 | // good
2494 | Number.isNaN('1.2.3'); // false
2495 | Number.isNaN(Number('1.2.3')); // true
2496 | ```
2497 |
2498 | - [29.2](#29.2) 使用 `Number.isFinite` 而非 `isFinite`。
2499 |
2500 | > 為什麼?全域函式 `isFinite` 會先將任何非數值轉換為數值,如果轉換後之值有限,則函式回傳 true。
2501 | > 若真要轉換為數值,請表達清楚。
2502 |
2503 | ```javascript
2504 | // bad
2505 | isFinite('2e3'); // true
2506 |
2507 | // good
2508 | Number.isFinite('2e3'); // false
2509 | Number.isFinite(parseInt('2e3', 10)); // true
2510 | ```
2511 |
2512 | **[⬆ 回到頂端](#table-of-contents)**
2513 |
2514 |
2515 | ## 測試
2516 |
2517 | - [30.1](#30.1) **如題。**
2518 |
2519 | ```javascript
2520 | function foo() {
2521 | return true;
2522 | }
2523 | ```
2524 |
2525 | - [30.2](#30.2) **無題,不過很重要**:
2526 | - 不論你用哪個測試框架,你都應該撰寫測試!
2527 | - 力求撰寫許多的純函式,並盡量減少異常發生的機會。
2528 | - 要對 stubs 及 mocks 保持嚴謹——他們可以讓你的測試變得更加脆弱。
2529 | - 我們在 Airbnb 主要使用 [`mocha`](https://www.npmjs.com/package/mocha)。對小型或單獨的模組偶爾使用 [`tape`](https://www.npmjs.com/package/tape)。
2530 | - 努力達到 100% 的測試涵蓋率是個很好的目標,即使實現這件事是不切實際的。
2531 | - 每當你修復完一個 bug,_就撰寫回歸測試_。一個修復完的 bug 若沒有回歸測試,通常在未來肯定會再次發生損壞。
2532 |
2533 | **[⬆ 回到頂端](#table-of-contents)**
2534 |
2535 |
2536 | ## 效能
2537 |
2538 | - [On Layout & Web Performance](http://www.kellegous.com/j/2013/01/26/layout-performance/)
2539 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2)
2540 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost)
2541 | - [Bang Function](http://jsperf.com/bang-function)
2542 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13)
2543 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text)
2544 | - [Long String Concatenation](http://jsperf.com/ya-string-concat)
2545 | - Loading...
2546 |
2547 | **[⬆ 回到頂端](#table-of-contents)**
2548 |
2549 |
2550 | ## 資源
2551 |
2552 | **學習 ES6**
2553 |
2554 | - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html)
2555 | - [ExploringJS](http://exploringjs.com/)
2556 | - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/)
2557 | - [Comprehensive Overview of ES6 Features](http://es6-features.org/)
2558 |
2559 | **請讀這個**
2560 |
2561 | - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html)
2562 |
2563 | **工具**
2564 |
2565 | - Code Style Linters
2566 | + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc)
2567 | + [JSHint](http://jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc)
2568 | + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json)
2569 |
2570 | **其他的風格指南**
2571 |
2572 | - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)
2573 | - [jQuery Core Style Guidelines](http://contribute.jquery.org/style-guide/js/)
2574 | - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js)
2575 |
2576 | **其他風格**
2577 |
2578 | - [Naming this in nested functions](https://gist.github.com/cjohansen/4135065) - Christian Johansen
2579 | - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen
2580 | - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun
2581 | - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman
2582 |
2583 | **瞭解更多**
2584 |
2585 | - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll
2586 | - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer
2587 | - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz
2588 | - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban
2589 | - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock
2590 |
2591 | **書籍**
2592 |
2593 | - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford
2594 | - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov
2595 | - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz
2596 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders
2597 | - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas
2598 | - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw
2599 | - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig
2600 | - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch
2601 | - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault
2602 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg
2603 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
2604 | - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon
2605 | - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov
2606 | - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman
2607 | - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke
2608 | - [You Don't Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson
2609 |
2610 | **部落格**
2611 |
2612 | - [DailyJS](http://dailyjs.com/)
2613 | - [JavaScript Weekly](http://javascriptweekly.com/)
2614 | - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/)
2615 | - [Bocoup Weblog](https://bocoup.com/weblog)
2616 | - [Adequately Good](http://www.adequatelygood.com/)
2617 | - [NCZOnline](https://www.nczonline.net/)
2618 | - [Perfection Kills](http://perfectionkills.com/)
2619 | - [Ben Alman](http://benalman.com/)
2620 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/)
2621 | - [Dustin Diaz](http://dustindiaz.com/)
2622 | - [nettuts](http://code.tutsplus.com/?s=javascript)
2623 |
2624 | **Podcasts**
2625 |
2626 | - [JavaScript Jabber](https://devchat.tv/js-jabber/)
2627 |
2628 |
2629 | **[⬆ 回到頂端](#table-of-contents)**
2630 |
2631 |
2632 | ## 誰在使用
2633 |
2634 | 這是正在使用這份風格指南的組織列表。送一個 pull request 後我們會將你增加到列表上。
2635 |
2636 | - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript)
2637 | - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript)
2638 | - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript)
2639 | - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript)
2640 | - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript)
2641 | - **Avant**: [avantcredit/javascript](https://github.com/avantcredit/javascript)
2642 | - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript)
2643 | - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/)
2644 | - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript)
2645 | - **Brainshark**: [brainshark/javascript](https://github.com/brainshark/javascript)
2646 | - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript-style-guide)
2647 | - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide)
2648 | - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript)
2649 | - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript)
2650 | - **Ecosia**: [ecosia/javascript](https://github.com/ecosia/javascript)
2651 | - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide)
2652 | - **Evolution Gaming**: [evolution-gaming/javascript](https://github.com/evolution-gaming/javascript)
2653 | - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript)
2654 | - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md)
2655 | - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide)
2656 | - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript)
2657 | - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript)
2658 | - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style)
2659 | - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript)
2660 | - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript-style-guide)
2661 | - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript)
2662 | - **HubSpot**: [HubSpot/javascript](https://github.com/HubSpot/javascript)
2663 | - **Hyper**: [hyperoslo/javascript-playbook](https://github.com/hyperoslo/javascript-playbook/blob/master/style.md)
2664 | - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide)
2665 | - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript)
2666 | - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions)
2667 | - **JeopardyBot**: [kesne/jeopardy-bot](https://github.com/kesne/jeopardy-bot/blob/master/STYLEGUIDE.md)
2668 | - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript)
2669 | - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/Javascript-style-guide)
2670 | - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript)
2671 | - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript)
2672 | - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript)
2673 | - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript)
2674 | - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript)
2675 | - **Muber**: [muber/javascript](https://github.com/muber/javascript)
2676 | - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript)
2677 | - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript)
2678 | - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript)
2679 | - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript)
2680 | - **OutBoxSoft**: [OutBoxSoft/javascript](https://github.com/OutBoxSoft/javascript)
2681 | - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript)
2682 | - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide)
2683 | - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript)
2684 | - **React**: [/facebook/react/blob/master/CONTRIBUTING.md#style-guide](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#style-guide)
2685 | - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/blob/master/docs/javascript.md)
2686 | - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide)
2687 | - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide)
2688 | - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript)
2689 | - **Springload**: [springload/javascript](https://github.com/springload/javascript)
2690 | - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript)
2691 | - **Target**: [target/javascript](https://github.com/target/javascript)
2692 | - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript)
2693 | - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript)
2694 | - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide)
2695 | - **WeBox Studio**: [weboxstudio/javascript](https://github.com/weboxstudio/javascript)
2696 | - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript)
2697 | - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript)
2698 | - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript)
2699 |
2700 | **[⬆ 回到頂端](#table-of-contents)**
2701 |
2702 |
2703 | ## 翻譯
2704 |
2705 | This style guide is also available in other languages:
2706 |
2707 | -  **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide)
2708 | -  **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript)
2709 | -  **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide)
2710 | -  **Chinese (Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide)
2711 | -  **Chinese (Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript)
2712 | -  **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide)
2713 | -  **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide)
2714 | -  **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide)
2715 | -  **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide)
2716 | -  **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide)
2717 | -  **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript)
2718 | -  **Russian**: [uprock/javascript](https://github.com/uprock/javascript)
2719 | -  **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide)
2720 | -  **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide)
2721 |
2722 |
2723 | ## JavaScript 風格指南
2724 |
2725 | - [參考](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide)
2726 |
2727 |
2728 | ## 與我們討論 JavaScript
2729 |
2730 | - Find us on [gitter](https://gitter.im/airbnb/javascript).
2731 |
2732 |
2733 | ## 貢獻者
2734 |
2735 | - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors)
2736 |
2737 |
2738 | ## License
2739 |
2740 | (The MIT License)
2741 |
2742 | Copyright (c) 2014-2016 Airbnb
2743 |
2744 | Permission is hereby granted, free of charge, to any person obtaining
2745 | a copy of this software and associated documentation files (the
2746 | 'Software'), to deal in the Software without restriction, including
2747 | without limitation the rights to use, copy, modify, merge, publish,
2748 | distribute, sublicense, and/or sell copies of the Software, and to
2749 | permit persons to whom the Software is furnished to do so, subject to
2750 | the following conditions:
2751 |
2752 | The above copyright notice and this permission notice shall be
2753 | included in all copies or substantial portions of the Software.
2754 |
2755 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2756 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2757 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2758 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2759 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2760 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2761 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2762 |
2763 | **[⬆ 回到頂端](#table-of-contents)**
2764 |
2765 | ## Amendments
2766 |
2767 | We encourage you to fork this guide and change the rules to fit your team's style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts.
2768 |
2769 | # };
2770 |
--------------------------------------------------------------------------------
/es5/README.md:
--------------------------------------------------------------------------------
1 | [](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
2 |
3 | # Airbnb JavaScript Style Guide() {
4 |
5 |
6 | *一份彙整了在 JavasScript 中被普遍使用的風格指南。*
7 |
8 | 翻譯自 [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 。
9 |
10 |
11 |
12 | ## 目錄
13 |
14 | 1. [資料型態](#types)
15 | 1. [物件](#objects)
16 | 1. [陣列](#arrays)
17 | 1. [字串](#strings)
18 | 1. [函式](#functions)
19 | 1. [屬性](#properties)
20 | 1. [變數](#variables)
21 | 1. [提升](#hoisting)
22 | 1. [條件式與等號](#conditional-expressions--equality)
23 | 1. [區塊](#blocks)
24 | 1. [註解](#comments)
25 | 1. [空格](#whitespace)
26 | 1. [逗號](#commas)
27 | 1. [分號](#semicolons)
28 | 1. [型別轉換](#type-casting--coercion)
29 | 1. [命名規則](#naming-conventions)
30 | 1. [存取器](#accessors)
31 | 1. [建構子](#constructors)
32 | 1. [事件](#events)
33 | 1. [模組](#modules)
34 | 1. [jQuery](#jquery)
35 | 1. [ECMAScript 5 相容性](#ecmascript-5-compatibility)
36 | 1. [測試](#testing)
37 | 1. [效能](#performance)
38 | 1. [資源](#resources)
39 | 1. [誰在使用](#in-the-wild)
40 | 1. [翻譯](#translation)
41 | 1. [JavaScript 風格指南](#the-javascript-style-guide-guide)
42 | 1. [和我們討論 Javascript](#chat-with-us-about-javascript)
43 | 1. [貢獻者](#contributors)
44 | 1. [授權許可](#license)
45 |
46 |
47 | ## 資料型態
48 |
49 | - **基本**: 你可以直接存取基本資料型態。
50 |
51 | + `字串`
52 | + `數字`
53 | + `布林`
54 | + `null`
55 | + `undefined`
56 |
57 | ```javascript
58 | var foo = 1;
59 | var bar = foo;
60 |
61 | bar = 9;
62 |
63 | console.log(foo, bar); // => 1, 9
64 | ```
65 | - **複合**: 你需要透過引用的方式存取複合資料型態。
66 |
67 | + `物件`
68 | + `陣列`
69 | + `函式`
70 |
71 | ```javascript
72 | var foo = [1, 2];
73 | var bar = foo;
74 |
75 | bar[0] = 9;
76 |
77 | console.log(foo[0], bar[0]); // => 9, 9
78 | ```
79 |
80 | **[⬆ 回到頂端](#table-of-contents)**
81 |
82 |
83 | ## 物件
84 |
85 | - 使用簡潔的語法建立物件。
86 |
87 | ```javascript
88 | // bad
89 | var item = new Object();
90 |
91 | // good
92 | var item = {};
93 | ```
94 |
95 | - 別使用[保留字](http://es5.github.io/#x7.6.1)當作鍵值,他在 IE8 上不會被執行。[了解更多](https://github.com/airbnb/javascript/issues/61)
96 |
97 | ```javascript
98 | // bad
99 | var superman = {
100 | default: { clark: 'kent' },
101 | private: true
102 | };
103 |
104 | // good
105 | var superman = {
106 | defaults: { clark: 'kent' },
107 | hidden: true
108 | };
109 | ```
110 |
111 | - 使用同義詞取代保留字。
112 |
113 | ```javascript
114 | // bad
115 | var superman = {
116 | class: 'alien'
117 | };
118 |
119 | // bad
120 | var superman = {
121 | klass: 'alien'
122 | };
123 |
124 | // good
125 | var superman = {
126 | type: 'alien'
127 | };
128 | ```
129 |
130 |
131 | **[⬆ 回到頂端](#table-of-contents)**
132 |
133 |
134 | ## 陣列
135 |
136 | - 使用簡潔的語法建立陣列。
137 |
138 | ```javascript
139 | // bad
140 | var items = new Array();
141 |
142 | // good
143 | var items = [];
144 | ```
145 |
146 | - 如果你不知道陣列的長度請使用 Array#push。
147 |
148 | ```javascript
149 | var someStack = [];
150 |
151 |
152 | // bad
153 | someStack[someStack.length] = 'abracadabra';
154 |
155 | // good
156 | someStack.push('abracadabra');
157 | ```
158 |
159 | - 如果你要複製一個陣列請使用 Array#slice。[jsPerf](http://jsperf.com/converting-arguments-to-an-array/7)
160 |
161 | ```javascript
162 | var len = items.length;
163 | var itemsCopy = [];
164 | var i;
165 |
166 | // bad
167 | for (i = 0; i < len; i++) {
168 | itemsCopy[i] = items[i];
169 | }
170 |
171 | // good
172 | itemsCopy = items.slice();
173 | ```
174 |
175 | - 如果要轉換一個像陣列的物件至陣列,可以使用 Array#slice。
176 |
177 | ```javascript
178 | function trigger() {
179 | var args = Array.prototype.slice.call(arguments);
180 | ...
181 | }
182 | ```
183 |
184 | **[⬆ 回到頂端](#table-of-contents)**
185 |
186 |
187 | ## 字串
188 |
189 | - 字串請使用單引號 `''` 。
190 |
191 | ```javascript
192 | // bad
193 | var name = "Bob Parr";
194 |
195 | // good
196 | var name = 'Bob Parr';
197 |
198 | // bad
199 | var fullName = "Bob " + this.lastName;
200 |
201 | // good
202 | var fullName = 'Bob ' + this.lastName;
203 | ```
204 |
205 | - 如果字串超過 100 個字元,請使用字串連接符號換行。
206 | - 注意: 過度的長字串連接可能會影響效能 [jsPerf](http://jsperf.com/ya-string-concat) 與[討論串](https://github.com/airbnb/javascript/issues/40)。
207 |
208 | ```javascript
209 | // bad
210 | var 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.';
211 |
212 | // bad
213 | var errorMessage = 'This is a super long error that was thrown because \
214 | of Batman. When you stop to think about how Batman had anything to do \
215 | with this, you would get nowhere \
216 | fast.';
217 |
218 | // good
219 | var errorMessage = 'This is a super long error that was thrown because ' +
220 | 'of Batman. When you stop to think about how Batman had anything to do ' +
221 | 'with this, you would get nowhere fast.';
222 | ```
223 |
224 | - 如果要透過陣列產生字串,請使用 Array#join 代替字串連接符號,尤其是 IE:[jsPerf](http://jsperf.com/string-vs-array-concat/2)。
225 |
226 | ```javascript
227 | var items;
228 | var messages;
229 | var length;
230 | var i;
231 |
232 | messages = [{
233 | state: 'success',
234 | message: 'This one worked.'
235 | }, {
236 | state: 'success',
237 | message: 'This one worked as well.'
238 | }, {
239 | state: 'error',
240 | message: 'This one did not work.'
241 | }];
242 |
243 | length = messages.length;
244 |
245 | // bad
246 | function inbox(messages) {
247 | items = '
';
248 |
249 | for (i = 0; i < length; i++) {
250 | items += '- ' + messages[i].message + '
';
251 | }
252 |
253 | return items + '
';
254 | }
255 |
256 | // good
257 | function inbox(messages) {
258 | items = [];
259 |
260 | for (i = 0; i < length; i++) {
261 | // 在這個狀況時我們可以直接賦值來稍微最佳化程式
262 | items[i] = '' + messages[i].message + '';
263 | }
264 |
265 | return '';
266 | }
267 | ```
268 |
269 | **[⬆ 回到頂端](#table-of-contents)**
270 |
271 |
272 | ## 函式
273 |
274 | - 函式表達式:
275 |
276 | ```javascript
277 | // 匿名函式
278 | var anonymous = function() {
279 | return true;
280 | };
281 |
282 | // 命名函式
283 | var named = function named() {
284 | return true;
285 | };
286 |
287 | // 立即函式 (immediately-invoked function expression, IIFE)
288 | (function() {
289 | console.log('Welcome to the Internet. Please follow me.');
290 | })();
291 | ```
292 |
293 | - 絕對不要在非函式的區塊(if, while, 等等)宣告函式,瀏覽器或許會允許你這麼做,但不同瀏覽器產生的結果可能會不同。你可以將函式賦予一個區塊外的變數解決這個問題。
294 | - **注意:**ECMA-262 將 `區塊` 定義為陳述式,函式宣告則不是陳述式。 [閱讀 ECMA-262 關於這個問題的說明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。
295 |
296 | ```javascript
297 | // bad
298 | if (currentUser) {
299 | function test() {
300 | console.log('Nope.');
301 | }
302 | }
303 |
304 | // good
305 | var test;
306 | if (currentUser) {
307 | test = function test() {
308 | console.log('Yup.');
309 | };
310 | }
311 | ```
312 |
313 | - 請勿將參數命名為 `arguments` ,這樣會將覆蓋掉函式作用域傳來的 `arguments` 。
314 |
315 | ```javascript
316 | // bad
317 | function nope(name, options, arguments) {
318 | // ...stuff...
319 | }
320 |
321 | // good
322 | function yup(name, options, args) {
323 | // ...stuff...
324 | }
325 | ```
326 |
327 | **[⬆ 回到頂端](#table-of-contents)**
328 |
329 |
330 | ## 屬性
331 |
332 | - 使用點 `.` 來存取屬性。
333 |
334 | ```javascript
335 | var luke = {
336 | jedi: true,
337 | age: 28
338 | };
339 |
340 | // bad
341 | var isJedi = luke['jedi'];
342 |
343 | // good
344 | var isJedi = luke.jedi;
345 | ```
346 |
347 | - 需要帶參數存取屬性時請使用中括號 `[]` 。
348 |
349 | ```javascript
350 | var luke = {
351 | jedi: true,
352 | age: 28
353 | };
354 |
355 | function getProp(prop) {
356 | return luke[prop];
357 | }
358 |
359 | var isJedi = getProp('jedi');
360 | ```
361 |
362 | **[⬆ 回到頂端](#table-of-contents)**
363 |
364 |
365 | ## 變數
366 |
367 | - 為了避免污染全域的命名空間,請使用 `var` 來宣告變數,如果不這麼做將會產生全域變數。
368 |
369 | ```javascript
370 | // bad
371 | superPower = new SuperPower();
372 |
373 | // good
374 | var superPower = new SuperPower();
375 | ```
376 |
377 | - 每個變數只使用一個 `var` 來宣告,這樣更容易增加新的變數宣告,而且你也不用擔心替換 `;` 為 `,` 及加入的標點符號不同的問題。
378 |
379 | ```javascript
380 | // bad
381 | var items = getItems(),
382 | goSportsTeam = true,
383 | dragonball = 'z';
384 |
385 | // bad
386 | // (比較上述例子找出錯誤)
387 | var items = getItems(),
388 | goSportsTeam = true;
389 | dragonball = 'z';
390 |
391 | // good
392 | var items = getItems();
393 | var goSportsTeam = true;
394 | var dragonball = 'z';
395 | ```
396 |
397 | - 將未賦值的變數宣告在最後,當你需要根據之前已賦值變數來賦值給未賦值變數時相當有幫助。
398 |
399 | ```javascript
400 | // bad
401 | var i, len, dragonball,
402 | items = getItems(),
403 | goSportsTeam = true;
404 |
405 | // bad
406 | var i;
407 | var items = getItems();
408 | var dragonball;
409 | var goSportsTeam = true;
410 | var len;
411 |
412 | // good
413 | var items = getItems();
414 | var goSportsTeam = true;
415 | var dragonball;
416 | var length;
417 | var i;
418 | ```
419 |
420 | - 在作用域的最頂層宣告變數,避免變數宣告及賦值提升的相關問題。
421 |
422 | ```javascript
423 | // bad
424 | function() {
425 | test();
426 | console.log('doing stuff..');
427 |
428 | //..other stuff..
429 |
430 | var name = getName();
431 |
432 | if (name === 'test') {
433 | return false;
434 | }
435 |
436 | return name;
437 | }
438 |
439 | // good
440 | function() {
441 | var name = getName();
442 |
443 | test();
444 | console.log('doing stuff..');
445 |
446 | //..other stuff..
447 |
448 | if (name === 'test') {
449 | return false;
450 | }
451 |
452 | return name;
453 | }
454 |
455 | // bad - 呼叫多餘的函式
456 | function() {
457 | var name = getName();
458 |
459 | if (!arguments.length) {
460 | return false;
461 | }
462 |
463 | this.setFirstName(name);
464 |
465 | return true;
466 | }
467 |
468 | // good
469 | function() {
470 | var name;
471 |
472 | if (!arguments.length) {
473 | return false;
474 | }
475 |
476 | name = getName();
477 | this.setFirstName(name);
478 |
479 | return true;
480 | }
481 | ```
482 |
483 | **[⬆ 回到頂端](#table-of-contents)**
484 |
485 |
486 | ## 提升
487 |
488 | - 變數宣告可以被提升至該作用域的最頂層,但賦予的值並不會。
489 |
490 | ```javascript
491 | // 我們知道這樣是行不通的
492 | // (假設沒有名為 notDefined 的全域變數)
493 | function example() {
494 | console.log(notDefined); // => throws a ReferenceError
495 | }
496 |
497 | // 由於變數提升的關係,
498 | // 你在引用變數後再宣告變數是行得通的。
499 | // 注:賦予給變數的 `true` 並不會被提升。
500 | function example() {
501 | console.log(declaredButNotAssigned); // => undefined
502 | var declaredButNotAssigned = true;
503 | }
504 |
505 | // 直譯器會將宣告的變數提升至作用域的最頂層,
506 | // 表示我們可以將這個例子改寫成以下:
507 | function example() {
508 | var declaredButNotAssigned;
509 | console.log(declaredButNotAssigned); // => undefined
510 | declaredButNotAssigned = true;
511 | }
512 | ```
513 |
514 | - 賦予匿名函式的變數會被提升,但函式內容並不會。
515 |
516 | ```javascript
517 | function example() {
518 | console.log(anonymous); // => undefined
519 |
520 | anonymous(); // => TypeError anonymous is not a function
521 |
522 | var anonymous = function() {
523 | console.log('anonymous function expression');
524 | };
525 | }
526 | ```
527 |
528 | - 賦予命名函式的變數會被提升,但函式內容及函式名稱並不會。
529 |
530 | ```javascript
531 | function example() {
532 | console.log(named); // => undefined
533 |
534 | named(); // => TypeError named is not a function
535 |
536 | superPower(); // => ReferenceError superPower is not defined
537 |
538 | var named = function superPower() {
539 | console.log('Flying');
540 | };
541 | }
542 |
543 | // 當函式名稱和變數名稱相同時也是如此。
544 | function example() {
545 | console.log(named); // => undefined
546 |
547 | named(); // => TypeError named is not a function
548 |
549 | var named = function named() {
550 | console.log('named');
551 | }
552 | }
553 | ```
554 |
555 | - 宣告函式的名稱及函式內容都會被提升。
556 |
557 | ```javascript
558 | function example() {
559 | superPower(); // => Flying
560 |
561 | function superPower() {
562 | console.log('Flying');
563 | }
564 | }
565 | ```
566 |
567 | - 想瞭解更多訊息,請參考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting)。
568 |
569 | **[⬆ 回到頂端](#table-of-contents)**
570 |
571 |
572 | ## 條件式與等號
573 |
574 | - 請使用 `===` 和 `!==` ,別使用 `==` 及 `!=` 。
575 | - 像是 `if` 的條件語法內會使用 `ToBoolean` 的抽象方法強轉類型,並遵循以下規範:
576 |
577 | + **物件** 轉換為 **true**
578 | + **Undefined** 轉換為 **false**
579 | + **Null** 轉換為 **false**
580 | + **布林** 轉換為 **該布林值**
581 | + **數字** 如果是 **+0, -0, 或 NaN** 則轉換為 **false** ,其他的皆為 **true**
582 | + **字串** 如果是空字串 `''` 則轉換為 **false** ,其他的皆為 **true**
583 |
584 | ```javascript
585 | if ([0]) {
586 | // true
587 | // 陣列為一個物件,所以轉換為 true
588 | }
589 | ```
590 |
591 | - 使用快速的方式。
592 |
593 | ```javascript
594 | // bad
595 | if (name !== '') {
596 | // ...stuff...
597 | }
598 |
599 | // good
600 | if (name) {
601 | // ...stuff...
602 | }
603 |
604 | // bad
605 | if (collection.length > 0) {
606 | // ...stuff...
607 | }
608 |
609 | // good
610 | if (collection.length) {
611 | // ...stuff...
612 | }
613 | ```
614 |
615 | - 想瞭解更多訊息請參考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。
616 |
617 | **[⬆ 回到頂端](#table-of-contents)**
618 |
619 |
620 | ## 區塊
621 |
622 | - 多行區塊請使用花括號括起來。
623 |
624 | ```javascript
625 | // bad
626 | if (test)
627 | return false;
628 |
629 | // good
630 | if (test) return false;
631 |
632 | // good
633 | if (test) {
634 | return false;
635 | }
636 |
637 | // bad
638 | function() { return false; }
639 |
640 | // good
641 | function() {
642 | return false;
643 | }
644 | ```
645 |
646 | - 如果你使用 `if` 及 `else` 的多行區塊,請將 `else` 放在 `if` 區塊的結尾花括號之後。
647 |
648 | ```javascript
649 | // bad
650 | if (test) {
651 | thing1();
652 | thing2();
653 | }
654 | else {
655 | thing3();
656 | }
657 |
658 | // good
659 | if (test) {
660 | thing1();
661 | thing2();
662 | } else {
663 | thing3();
664 | }
665 | ```
666 |
667 | **[⬆ 回到頂端](#table-of-contents)**
668 |
669 |
670 | ## 註解
671 |
672 | - 多行註解請使用 `/** ... */` ,包含描述,指定類型以及參數值還有回傳值。
673 |
674 | ```javascript
675 | // bad
676 | // make() 根據傳入的 tag 名稱回傳一個新的元件
677 | //
678 | // @param {String} tag
679 | // @return {Element} element
680 | function make(tag) {
681 |
682 | // ...stuff...
683 |
684 | return element;
685 | }
686 |
687 | // good
688 | /**
689 | * make() 根據傳入的 tag 名稱回傳一個新的元件
690 | *
691 | * @param {String} tag
692 | * @return {Element} element
693 | */
694 | function make(tag) {
695 |
696 | // ...stuff...
697 |
698 | return element;
699 | }
700 | ```
701 |
702 | - 單行註解請使用 `//` ,在欲註解的地方上方進行當行註解,並在註解前空一格。
703 |
704 | ```javascript
705 | // bad
706 | var active = true; // 當目前分頁
707 |
708 | // good
709 | // 當目前分頁
710 | var active = true;
711 |
712 | // bad
713 | function getType() {
714 | console.log('fetching type...');
715 | // 設定預設的類型為 'no type'
716 | var type = this._type || 'no type';
717 |
718 | return type;
719 | }
720 |
721 | // good
722 | function getType() {
723 | console.log('fetching type...');
724 |
725 | // 設定預設的類型為 'no type'
726 | var type = this._type || 'no type';
727 |
728 | return type;
729 | }
730 | ```
731 |
732 | - 在註解前方加上 `FIXME` 或 `TODO` 可以幫助其他開發人員快速瞭解這是一個需要重新討論的問題,或是一個等待解決的問題。和一般的註解不同,他們是可被執行的。對應的動作為 `FIXME -- 重新討論並解決` 或 `TODO -- 必須執行`.
733 |
734 | - 使用 `// FIXME:` 標注問題。
735 |
736 | ```javascript
737 | function Calculator() {
738 |
739 | // FIXME: 不該在這使用全域變數
740 | total = 0;
741 |
742 | return this;
743 | }
744 | ```
745 |
746 | - 使用 `// TODO:` 標注問題的解決方式
747 |
748 | ```javascript
749 | function Calculator() {
750 |
751 | // TODO: total 應該可被傳入的參數所修改
752 | this.total = 0;
753 |
754 | return this;
755 | }
756 | ```
757 |
758 | **[⬆ 回到頂端](#table-of-contents)**
759 |
760 |
761 | ## 空格
762 |
763 | - 將 Tab 設定為兩個空格。
764 |
765 | ```javascript
766 | // bad
767 | function() {
768 | ∙∙∙∙var name;
769 | }
770 |
771 | // bad
772 | function() {
773 | ∙var name;
774 | }
775 |
776 | // good
777 | function() {
778 | ∙∙var name;
779 | }
780 | ```
781 |
782 | - 在花括號前加一個空格。
783 |
784 | ```javascript
785 | // bad
786 | function test(){
787 | console.log('test');
788 | }
789 |
790 | // good
791 | function test() {
792 | console.log('test');
793 | }
794 |
795 | // bad
796 | dog.set('attr',{
797 | age: '1 year',
798 | breed: 'Bernese Mountain Dog'
799 | });
800 |
801 | // good
802 | dog.set('attr', {
803 | age: '1 year',
804 | breed: 'Bernese Mountain Dog'
805 | });
806 | ```
807 |
808 | - 在控制流程的語句(`if`, `while` 等等。)的左括號前加上一個空格。宣告的函式和傳入的變數間則沒有空格。
809 |
810 | ```javascript
811 | // bad
812 | if(isJedi) {
813 | fight ();
814 | }
815 |
816 | // good
817 | if (isJedi) {
818 | fight();
819 | }
820 |
821 | // bad
822 | function fight () {
823 | console.log ('Swooosh!');
824 | }
825 |
826 | // good
827 | function fight() {
828 | console.log('Swooosh!');
829 | }
830 | ```
831 |
832 | - 將運算元用空格隔開。
833 |
834 | ```javascript
835 | // bad
836 | var x=y+5;
837 |
838 | // good
839 | var x = y + 5;
840 | ```
841 |
842 | - 在檔案的最尾端加上一行空白行。
843 |
844 | ```javascript
845 | // bad
846 | (function(global) {
847 | // ...stuff...
848 | })(this);
849 | ```
850 |
851 | ```javascript
852 | // bad
853 | (function(global) {
854 | // ...stuff...
855 | })(this);↵
856 | ↵
857 | ```
858 |
859 | ```javascript
860 | // good
861 | (function(global) {
862 | // ...stuff...
863 | })(this);↵
864 | ```
865 |
866 | - 當多個方法連接時請換行縮排,利用前面的 `.` 強調該行是呼叫方法,而不是一個新的宣告。
867 |
868 | ```javascript
869 | // bad
870 | $('#items').find('.selected').highlight().end().find('.open').updateCount();
871 |
872 | // bad
873 | $('#items').
874 | find('.selected').
875 | highlight().
876 | end().
877 | find('.open').
878 | updateCount();
879 |
880 | // good
881 | $('#items')
882 | .find('.selected')
883 | .highlight()
884 | .end()
885 | .find('.open')
886 | .updateCount();
887 |
888 | // bad
889 | var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
890 | .attr('width', (radius + margin) * 2).append('svg:g')
891 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
892 | .call(tron.led);
893 |
894 | // good
895 | var leds = stage.selectAll('.led')
896 | .data(data)
897 | .enter().append('svg:svg')
898 | .classed('led', true)
899 | .attr('width', (radius + margin) * 2)
900 | .append('svg:g')
901 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
902 | .call(tron.led);
903 | ```
904 |
905 | - 區塊的結束和下個語法間加上空行。
906 |
907 | ```javascript
908 | // bad
909 | if (foo) {
910 | return bar;
911 | }
912 | return baz;
913 |
914 | // good
915 | if (foo) {
916 | return bar;
917 | }
918 |
919 | return baz;
920 |
921 | // bad
922 | var obj = {
923 | foo: function() {
924 | },
925 | bar: function() {
926 | }
927 | };
928 | return obj;
929 |
930 | // good
931 | var obj = {
932 | foo: function() {
933 | },
934 |
935 | bar: function() {
936 | }
937 | };
938 |
939 | return obj;
940 | ```
941 |
942 | **[⬆ 回到頂端](#table-of-contents)**
943 |
944 |
945 | ## 逗號
946 |
947 | - 不要將逗號放在前方。
948 |
949 | ```javascript
950 | // bad
951 | var story = [
952 | once
953 | , upon
954 | , aTime
955 | ];
956 |
957 | // good
958 | var story = [
959 | once,
960 | upon,
961 | aTime
962 | ];
963 |
964 | // bad
965 | var hero = {
966 | firstName: 'Bob'
967 | , lastName: 'Parr'
968 | , heroName: 'Mr. Incredible'
969 | , superPower: 'strength'
970 | };
971 |
972 | // good
973 | var hero = {
974 | firstName: 'Bob',
975 | lastName: 'Parr',
976 | heroName: 'Mr. Incredible',
977 | superPower: 'strength'
978 | };
979 | ```
980 |
981 | - 多餘的逗號:**Nope.** 在 IE6/7 及 IE9 的相容性模式中,多餘的逗號可能會產生問題。另外,在 ES3 的一些實現方式上會多計算陣列的長度,不過在 ES5 中已經被修正了([來源](http://es5.github.io/#D)):
982 |
983 | > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.
984 |
985 | ```javascript
986 | // bad
987 | var hero = {
988 | firstName: 'Kevin',
989 | lastName: 'Flynn',
990 | };
991 |
992 | var heroes = [
993 | 'Batman',
994 | 'Superman',
995 | ];
996 |
997 | // good
998 | var hero = {
999 | firstName: 'Kevin',
1000 | lastName: 'Flynn'
1001 | };
1002 |
1003 | var heroes = [
1004 | 'Batman',
1005 | 'Superman'
1006 | ];
1007 | ```
1008 |
1009 | **[⬆ 回到頂端](#table-of-contents)**
1010 |
1011 |
1012 | ## 分號
1013 |
1014 | - 句尾請加分號。
1015 |
1016 | ```javascript
1017 | // bad
1018 | (function() {
1019 | var name = 'Skywalker'
1020 | return name
1021 | })()
1022 |
1023 | // good
1024 | (function() {
1025 | var name = 'Skywalker';
1026 | return name;
1027 | })();
1028 |
1029 | // good (防止當兩個檔案含有立即函式需要合併時,函式被當成參數處理)
1030 | ;(function() {
1031 | var name = 'Skywalker';
1032 | return name;
1033 | })();
1034 | ```
1035 |
1036 | [瞭解更多](http://stackoverflow.com/a/7365214/1712802).
1037 |
1038 | **[⬆ 回到頂端](#table-of-contents)**
1039 |
1040 |
1041 | ## 型別轉換
1042 |
1043 | - 在開頭的宣告進行強制型別轉換。
1044 | - 字串:
1045 |
1046 | ```javascript
1047 | // => this.reviewScore = 9;
1048 |
1049 | // bad
1050 | var totalScore = this.reviewScore + '';
1051 |
1052 | // good
1053 | var totalScore = '' + this.reviewScore;
1054 |
1055 | // bad
1056 | var totalScore = '' + this.reviewScore + ' total score';
1057 |
1058 | // good
1059 | var totalScore = this.reviewScore + ' total score';
1060 | ```
1061 |
1062 | - 對數字使用 `parseInt` 轉換,並帶上型別轉換的基數。
1063 |
1064 | ```javascript
1065 | var inputValue = '4';
1066 |
1067 | // bad
1068 | var val = new Number(inputValue);
1069 |
1070 | // bad
1071 | var val = +inputValue;
1072 |
1073 | // bad
1074 | var val = inputValue >> 0;
1075 |
1076 | // bad
1077 | var val = parseInt(inputValue);
1078 |
1079 | // good
1080 | var val = Number(inputValue);
1081 |
1082 | // good
1083 | var val = parseInt(inputValue, 10);
1084 | ```
1085 |
1086 | - 如果你因為某個原因在做些瘋狂的事情,但是 `parseInt` 是你的瓶頸,所以你對於[性能方面的原因](http://jsperf.com/coercion-vs-casting/3)而必須使用位元右移,請留下評論並解釋為什麼使用,及你做了哪些事情。
1087 |
1088 | ```javascript
1089 | // good
1090 | /**
1091 | * 使用 parseInt 導致我的程式變慢,改成使用
1092 | * 位元右移強制將字串轉為數字加快了他的速度。
1093 | */
1094 | var val = inputValue >> 0;
1095 | ```
1096 |
1097 | - **注意:**使用位元轉換時請小心,數字為 [64 位元數值](http://es5.github.io/#x4.3.19),但是使用位元轉換時則會回傳一個 32 位元的整數 ([來源](http://es5.github.io/#x11.7)),這會導致大於 32 位元的數值產生異常[討論串](https://github.com/airbnb/javascript/issues/109),32 位元的整數最大值為 2,147,483,647:
1098 |
1099 | ```javascript
1100 | 2147483647 >> 0 //=> 2147483647
1101 | 2147483648 >> 0 //=> -2147483648
1102 | 2147483649 >> 0 //=> -2147483647
1103 | ```
1104 |
1105 | - 布林:
1106 |
1107 | ```javascript
1108 | var age = 0;
1109 |
1110 | // bad
1111 | var hasAge = new Boolean(age);
1112 |
1113 | // good
1114 | var hasAge = Boolean(age);
1115 |
1116 | // good
1117 | var hasAge = !!age;
1118 | ```
1119 |
1120 | **[⬆ 回到頂端](#table-of-contents)**
1121 |
1122 |
1123 | ## 命名規則
1124 |
1125 | - 避免使用單一字母的名稱,讓你的名稱有解釋的含義。
1126 |
1127 | ```javascript
1128 | // bad
1129 | function q() {
1130 | // ...stuff...
1131 | }
1132 |
1133 | // good
1134 | function query() {
1135 | // ..stuff..
1136 | }
1137 | ```
1138 |
1139 | - 使用駝峰式大小寫命名物件,函式及實例。
1140 |
1141 | ```javascript
1142 | // bad
1143 | var OBJEcttsssss = {};
1144 | var this_is_my_object = {};
1145 | var o = {};
1146 | function c() {}
1147 |
1148 | // good
1149 | var thisIsMyObject = {};
1150 | function thisIsMyFunction() {}
1151 | ```
1152 |
1153 | - 使用帕斯卡命名法來命名建構函式或類別。
1154 |
1155 | ```javascript
1156 | // bad
1157 | function user(options) {
1158 | this.name = options.name;
1159 | }
1160 |
1161 | var bad = new user({
1162 | name: 'nope'
1163 | });
1164 |
1165 | // good
1166 | function User(options) {
1167 | this.name = options.name;
1168 | }
1169 |
1170 | var good = new User({
1171 | name: 'yup'
1172 | });
1173 | ```
1174 |
1175 | - 命名私有屬性時請在前面加底線 `_`。
1176 |
1177 | ```javascript
1178 | // bad
1179 | this.__firstName__ = 'Panda';
1180 | this.firstName_ = 'Panda';
1181 |
1182 | // good
1183 | this._firstName = 'Panda';
1184 | ```
1185 |
1186 | - 要保留對 `this` 的使用時請用 `_this` 取代。
1187 |
1188 | ```javascript
1189 | // bad
1190 | function() {
1191 | var self = this;
1192 | return function() {
1193 | console.log(self);
1194 | };
1195 | }
1196 |
1197 | // bad
1198 | function() {
1199 | var that = this;
1200 | return function() {
1201 | console.log(that);
1202 | };
1203 | }
1204 |
1205 | // good
1206 | function() {
1207 | var _this = this;
1208 | return function() {
1209 | console.log(_this);
1210 | };
1211 | }
1212 | ```
1213 |
1214 | - 將你的函式命名,這對於在做堆疊追蹤時相當有幫助。
1215 |
1216 | ```javascript
1217 | // bad
1218 | var log = function(msg) {
1219 | console.log(msg);
1220 | };
1221 |
1222 | // good
1223 | var log = function log(msg) {
1224 | console.log(msg);
1225 | };
1226 | ```
1227 |
1228 | - **注意:**IE8 及 IE8 以下對於命名函式有獨到見解。更多的訊息在 [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/)。
1229 |
1230 | - 如果你的檔案只有輸出一個類別,你的檔案名稱必須和你的類別名稱相同。
1231 | ```javascript
1232 | // 檔案內容
1233 | class CheckBox {
1234 | // ...
1235 | }
1236 | module.exports = CheckBox;
1237 |
1238 | // 在其他的檔案
1239 | // bad
1240 | var CheckBox = require('./checkBox');
1241 |
1242 | // bad
1243 | var CheckBox = require('./check_box');
1244 |
1245 | // good
1246 | var CheckBox = require('./CheckBox');
1247 | ```
1248 |
1249 | **[⬆ 回到頂端](#table-of-contents)**
1250 |
1251 |
1252 | ## 存取器
1253 |
1254 | - 存取器不是必須的。
1255 | - 如果你要建立一個存取器,請使用 getVal() 及 setVal('hello')。
1256 |
1257 | ```javascript
1258 | // bad
1259 | dragon.age();
1260 |
1261 | // good
1262 | dragon.getAge();
1263 |
1264 | // bad
1265 | dragon.age(25);
1266 |
1267 | // good
1268 | dragon.setAge(25);
1269 | ```
1270 |
1271 | - 如果屬性是布林,請使用 isVal() 或 hasVal()。
1272 |
1273 | ```javascript
1274 | // bad
1275 | if (!dragon.age()) {
1276 | return false;
1277 | }
1278 |
1279 | // good
1280 | if (!dragon.hasAge()) {
1281 | return false;
1282 | }
1283 | ```
1284 |
1285 | - 可以建立 get() 及 set() 函式,但請保持一致。
1286 |
1287 | ```javascript
1288 | function Jedi(options) {
1289 | options || (options = {});
1290 | var lightsaber = options.lightsaber || 'blue';
1291 | this.set('lightsaber', lightsaber);
1292 | }
1293 |
1294 | Jedi.prototype.set = function(key, val) {
1295 | this[key] = val;
1296 | };
1297 |
1298 | Jedi.prototype.get = function(key) {
1299 | return this[key];
1300 | };
1301 | ```
1302 |
1303 | **[⬆ 回到頂端](#table-of-contents)**
1304 |
1305 |
1306 | ## 建構子
1307 |
1308 | - 將方法分配給物件原型,而不是用新的物件覆蓋掉原型,否則會導致繼承出現問題:重置原型時你會覆蓋原有的原型。
1309 |
1310 | ```javascript
1311 | function Jedi() {
1312 | console.log('new jedi');
1313 | }
1314 |
1315 | // bad
1316 | Jedi.prototype = {
1317 | fight: function fight() {
1318 | console.log('fighting');
1319 | },
1320 |
1321 | block: function block() {
1322 | console.log('blocking');
1323 | }
1324 | };
1325 |
1326 | // good
1327 | Jedi.prototype.fight = function fight() {
1328 | console.log('fighting');
1329 | };
1330 |
1331 | Jedi.prototype.block = function block() {
1332 | console.log('blocking');
1333 | };
1334 | ```
1335 |
1336 | - 方法可以回傳 `this` 幫助方法鏈接。
1337 |
1338 | ```javascript
1339 | // bad
1340 | Jedi.prototype.jump = function() {
1341 | this.jumping = true;
1342 | return true;
1343 | };
1344 |
1345 | Jedi.prototype.setHeight = function(height) {
1346 | this.height = height;
1347 | };
1348 |
1349 | var luke = new Jedi();
1350 | luke.jump(); // => true
1351 | luke.setHeight(20); // => undefined
1352 |
1353 | // good
1354 | Jedi.prototype.jump = function() {
1355 | this.jumping = true;
1356 | return this;
1357 | };
1358 |
1359 | Jedi.prototype.setHeight = function(height) {
1360 | this.height = height;
1361 | return this;
1362 | };
1363 |
1364 | var luke = new Jedi();
1365 |
1366 | luke.jump()
1367 | .setHeight(20);
1368 | ```
1369 |
1370 |
1371 | - 可以寫一個 toString() 的方法,但是請確保他可以正常執行且沒有函式副作用。
1372 |
1373 | ```javascript
1374 | function Jedi(options) {
1375 | options || (options = {});
1376 | this.name = options.name || 'no name';
1377 | }
1378 |
1379 | Jedi.prototype.getName = function getName() {
1380 | return this.name;
1381 | };
1382 |
1383 | Jedi.prototype.toString = function toString() {
1384 | return 'Jedi - ' + this.getName();
1385 | };
1386 | ```
1387 |
1388 | **[⬆ 回到頂端](#table-of-contents)**
1389 |
1390 |
1391 | ## 事件
1392 |
1393 | - 當需要對事件傳入資料時(不論是 DOM 事件或是其他私有事件),請傳入物件替代單一的資料。這樣可以使之後的開發人員直接加入其他的資料到事件裡,而不需更新該事件的處理器。例如,比較不好的做法:
1394 |
1395 | ```js
1396 | // bad
1397 | $(this).trigger('listingUpdated', listing.id);
1398 |
1399 | ...
1400 |
1401 | $(this).on('listingUpdated', function(e, listingId) {
1402 | // do something with listingId
1403 | });
1404 | ```
1405 |
1406 | 更好的做法:
1407 |
1408 | ```js
1409 | // good
1410 | $(this).trigger('listingUpdated', { listingId : listing.id });
1411 |
1412 | ...
1413 |
1414 | $(this).on('listingUpdated', function(e, data) {
1415 | // do something with data.listingId
1416 | });
1417 | ```
1418 |
1419 | **[⬆ 回到頂端](#table-of-contents)**
1420 |
1421 |
1422 | ## 模組
1423 |
1424 | - 模組的開頭必須以 `!` 開頭, 這樣可以確保前一模組結尾忘記加分號時在合併後不會出現錯誤。[說明](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933)
1425 | - 命名方式請使用駝峰式大小寫,並存在同名的資料夾下,導出時的名稱也必須一致。
1426 | - 加入一個名稱為 `noConflict()` 方法來設置導出時的模組為前一個版本,並將他回傳。
1427 | - 記得在模組的最頂端加上 `'use strict';` 。
1428 |
1429 | ```javascript
1430 | // fancyInput/fancyInput.js
1431 |
1432 | !function(global) {
1433 | 'use strict';
1434 |
1435 | var previousFancyInput = global.FancyInput;
1436 |
1437 | function FancyInput(options) {
1438 | this.options = options || {};
1439 | }
1440 |
1441 | FancyInput.noConflict = function noConflict() {
1442 | global.FancyInput = previousFancyInput;
1443 | return FancyInput;
1444 | };
1445 |
1446 | global.FancyInput = FancyInput;
1447 | }(this);
1448 | ```
1449 |
1450 | **[⬆ 回到頂端](#table-of-contents)**
1451 |
1452 |
1453 | ## jQuery
1454 |
1455 | - jQuery 的物件請使用 `$` 當前綴。
1456 |
1457 | ```javascript
1458 | // bad
1459 | var sidebar = $('.sidebar');
1460 |
1461 | // good
1462 | var $sidebar = $('.sidebar');
1463 | ```
1464 |
1465 | - 快取 jQuery 的查詢。
1466 |
1467 | ```javascript
1468 | // bad
1469 | function setSidebar() {
1470 | $('.sidebar').hide();
1471 |
1472 | // ...stuff...
1473 |
1474 | $('.sidebar').css({
1475 | 'background-color': 'pink'
1476 | });
1477 | }
1478 |
1479 | // good
1480 | function setSidebar() {
1481 | var $sidebar = $('.sidebar');
1482 | $sidebar.hide();
1483 |
1484 | // ...stuff...
1485 |
1486 | $sidebar.css({
1487 | 'background-color': 'pink'
1488 | });
1489 | }
1490 | ```
1491 |
1492 | - DOM 的查詢請使用層遞的 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')` 。[jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16)
1493 | - 對作用域內的 jQuery 物件使用 `find` 做查詢。
1494 |
1495 | ```javascript
1496 | // bad
1497 | $('ul', '.sidebar').hide();
1498 |
1499 | // bad
1500 | $('.sidebar').find('ul').hide();
1501 |
1502 | // good
1503 | $('.sidebar ul').hide();
1504 |
1505 | // good
1506 | $('.sidebar > ul').hide();
1507 |
1508 | // good
1509 | $sidebar.find('ul').hide();
1510 | ```
1511 |
1512 | **[⬆ 回到頂端](#table-of-contents)**
1513 |
1514 |
1515 | ## ECMAScript 5 相容性
1516 |
1517 | - 參考 [Kangax](https://twitter.com/kangax/) 的 ES5 [相容性列表](http://kangax.github.com/es5-compat-table/).
1518 |
1519 | **[⬆ 回到頂端](#table-of-contents)**
1520 |
1521 |
1522 | ## 測試
1523 |
1524 | - **如題。**
1525 |
1526 | ```javascript
1527 | function() {
1528 | return true;
1529 | }
1530 | ```
1531 |
1532 | **[⬆ 回到頂端](#table-of-contents)**
1533 |
1534 |
1535 | ## 效能
1536 |
1537 | - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/)
1538 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2)
1539 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost)
1540 | - [Bang Function](http://jsperf.com/bang-function)
1541 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13)
1542 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text)
1543 | - [Long String Concatenation](http://jsperf.com/ya-string-concat)
1544 | - Loading...
1545 |
1546 | **[⬆ 回到頂端](#table-of-contents)**
1547 |
1548 |
1549 | ## 資源
1550 |
1551 |
1552 | **請讀這個**
1553 |
1554 | - [Annotated ECMAScript 5.1](http://es5.github.com/)
1555 |
1556 | **工具**
1557 |
1558 | - Code Style Linters
1559 | + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc)
1560 | + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json)
1561 |
1562 | **其他的風格指南**
1563 |
1564 | - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)
1565 | - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines)
1566 | - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/)
1567 |
1568 | **其他風格**
1569 |
1570 | - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen
1571 | - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen
1572 | - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun
1573 | - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman
1574 |
1575 | **瞭解更多**
1576 |
1577 | - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll
1578 | - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer
1579 | - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz
1580 | - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban
1581 | - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock
1582 |
1583 | **書籍**
1584 |
1585 | - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford
1586 | - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov
1587 | - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz
1588 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders
1589 | - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas
1590 | - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw
1591 | - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig
1592 | - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch
1593 | - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault
1594 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg
1595 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
1596 | - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon
1597 | - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov
1598 | - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman
1599 | - [Eloquent JavaScript](http://eloquentjavascript.net) - Marijn Haverbeke
1600 | - [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS) - Kyle Simpson
1601 |
1602 | **部落格**
1603 |
1604 | - [DailyJS](http://dailyjs.com/)
1605 | - [JavaScript Weekly](http://javascriptweekly.com/)
1606 | - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/)
1607 | - [Bocoup Weblog](http://weblog.bocoup.com/)
1608 | - [Adequately Good](http://www.adequatelygood.com/)
1609 | - [NCZOnline](http://www.nczonline.net/)
1610 | - [Perfection Kills](http://perfectionkills.com/)
1611 | - [Ben Alman](http://benalman.com/)
1612 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/)
1613 | - [Dustin Diaz](http://dustindiaz.com/)
1614 | - [nettuts](http://net.tutsplus.com/?s=javascript)
1615 |
1616 | **Podcasts**
1617 |
1618 | - [JavaScript Jabber](http://devchat.tv/js-jabber/)
1619 |
1620 |
1621 | **[⬆ 回到頂端](#table-of-contents)**
1622 |
1623 |
1624 | ## 誰在使用
1625 |
1626 | 這是正在使用這份風格指南的組織列表。送一個 pull request 或提一個 issue 讓我們將你增加到列表上。
1627 |
1628 | - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript)
1629 | - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript)
1630 | - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript)
1631 | - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript)
1632 | - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript)
1633 | - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript)
1634 | - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide)
1635 | - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript)
1636 | - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript)
1637 | - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide)
1638 | - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript)
1639 | - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide)
1640 | - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript)
1641 | - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript)
1642 | - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style)
1643 | - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript)
1644 | - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript)
1645 | - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide)
1646 | - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript)
1647 | - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions)
1648 | - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript)
1649 | - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript)
1650 | - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript)
1651 | - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript)
1652 | - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript)
1653 | - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript)
1654 | - **Muber**: [muber/javascript](https://github.com/muber/javascript)
1655 | - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript)
1656 | - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript)
1657 | - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript)
1658 | - **Nordic Venture Family**: [CodeDistillery/javascript](https://github.com/CodeDistillery/javascript)
1659 | - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript)
1660 | - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript)
1661 | - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide)
1662 | - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript)
1663 | - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide)
1664 | - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide)
1665 | - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide)
1666 | - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript)
1667 | - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript)
1668 | - **Super**: [SuperJobs/javascript](https://github.com/SuperJobs/javascript)
1669 | - **Target**: [target/javascript](https://github.com/target/javascript)
1670 | - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript)
1671 | - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript)
1672 | - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide)
1673 | - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript)
1674 | - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript)
1675 | - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript)
1676 |
1677 |
1678 | ## 翻譯
1679 |
1680 | 這份風格指南也提供其他語言的版本:
1681 |
1682 | -  **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide)
1683 | -  **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript)
1684 | -  **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide)
1685 | -  **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript)
1686 | -  **Chinese(Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide)
1687 | -  **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide)
1688 | -  **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide)
1689 | -  **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide)
1690 | -  **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide)
1691 | -  **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide)
1692 | -  **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript)
1693 | -  **Russian**: [uprock/javascript](https://github.com/uprock/javascript)
1694 | -  **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide)
1695 | -  **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide)
1696 |
1697 |
1698 | ## JavaScript 風格指南
1699 |
1700 | - [參考](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide)
1701 |
1702 |
1703 | ## 與我們討論 JavaScript
1704 |
1705 | - 請到 [gitter](https://gitter.im/airbnb/javascript) 尋找我們.
1706 |
1707 |
1708 | ## 貢獻者
1709 |
1710 | - [查看貢獻者](https://github.com/airbnb/javascript/graphs/contributors)
1711 |
1712 |
1713 | ## 授權許可
1714 |
1715 | (The MIT License)
1716 |
1717 | Copyright (c) 2014 Airbnb
1718 |
1719 | Permission is hereby granted, free of charge, to any person obtaining
1720 | a copy of this software and associated documentation files (the
1721 | 'Software'), to deal in the Software without restriction, including
1722 | without limitation the rights to use, copy, modify, merge, publish,
1723 | distribute, sublicense, and/or sell copies of the Software, and to
1724 | permit persons to whom the Software is furnished to do so, subject to
1725 | the following conditions:
1726 |
1727 | The above copyright notice and this permission notice shall be
1728 | included in all copies or substantial portions of the Software.
1729 |
1730 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
1731 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1732 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1733 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1734 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
1735 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1736 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1737 |
1738 | **[⬆ 回到頂端](#table-of-contents)**
1739 |
1740 | # };
1741 |
--------------------------------------------------------------------------------
/linters/.eslintrc:
--------------------------------------------------------------------------------
1 | // Use this file as a starting point for your project's .eslintrc.
2 | // Copy this file, and add rule overrides as needed.
3 | {
4 | "extends": "airbnb"
5 | }
6 |
--------------------------------------------------------------------------------
/linters/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | /*
3 | * ENVIRONMENTS
4 | * =================
5 | */
6 |
7 | // Define globals exposed by modern browsers.
8 | "browser": true,
9 |
10 | // Define globals exposed by jQuery.
11 | "jquery": true,
12 |
13 | // Define globals exposed by Node.js.
14 | "node": true,
15 |
16 | // Allow ES6.
17 | "esnext": true,
18 |
19 | /*
20 | * ENFORCING OPTIONS
21 | * =================
22 | */
23 |
24 | // Force all variable names to use either camelCase style or UPPER_CASE
25 | // with underscores.
26 | "camelcase": true,
27 |
28 | // Prohibit use of == and != in favor of === and !==.
29 | "eqeqeq": true,
30 |
31 | // Enforce tab width of 2 spaces.
32 | "indent": 2,
33 |
34 | // Prohibit use of a variable before it is defined.
35 | "latedef": true,
36 |
37 | // Enforce line length to 100 characters
38 | "maxlen": 100,
39 |
40 | // Require capitalized names for constructor functions.
41 | "newcap": true,
42 |
43 | // Enforce use of single quotation marks for strings.
44 | "quotmark": "single",
45 |
46 | // Enforce placing 'use strict' at the top function scope
47 | "strict": true,
48 |
49 | // Prohibit use of explicitly undeclared variables.
50 | "undef": true,
51 |
52 | // Warn when variables are defined but never used.
53 | "unused": true,
54 |
55 | /*
56 | * RELAXING OPTIONS
57 | * =================
58 | */
59 |
60 | // Suppress warnings about == null comparisons.
61 | "eqnull": true
62 | }
63 |
--------------------------------------------------------------------------------
/linters/README.md:
--------------------------------------------------------------------------------
1 | ## `.eslintrc`
2 |
3 | 我們的 `.eslintrc` 需要有下列的 NPM 資源包:
4 |
5 | ```
6 | npm install --save-dev \
7 | eslint-config-airbnb \
8 | eslint \
9 | babel-eslint \
10 | eslint-plugin-react
11 | ```
12 |
--------------------------------------------------------------------------------
/linters/SublimeLinter/SublimeLinter.sublime-settings:
--------------------------------------------------------------------------------
1 | /**
2 | * Airbnb JSHint settings for use with SublimeLinter and Sublime Text 2.
3 | *
4 | * 1. Install SublimeLinter at https://github.com/SublimeLinter/SublimeLinter
5 | * 2. Open user preferences for the SublimeLinter package in Sublime Text 2
6 | * * For Mac OS X go to _Sublime Text 2_ > _Preferences_ > _Package Settings_ > _SublimeLinter_ > _Settings - User_
7 | * 3. Paste the contents of this file into your settings file
8 | * 4. Save the settings file
9 | *
10 | * @version 0.3.0
11 | * @see https://github.com/SublimeLinter/SublimeLinter
12 | * @see http://www.jshint.com/docs/
13 | */
14 | {
15 | "jshint_options":
16 | {
17 | /*
18 | * ENVIRONMENTS
19 | * =================
20 | */
21 |
22 | // Define globals exposed by modern browsers.
23 | "browser": true,
24 |
25 | // Define globals exposed by jQuery.
26 | "jquery": true,
27 |
28 | // Define globals exposed by Node.js.
29 | "node": true,
30 |
31 | /*
32 | * ENFORCING OPTIONS
33 | * =================
34 | */
35 |
36 | // Force all variable names to use either camelCase style or UPPER_CASE
37 | // with underscores.
38 | "camelcase": true,
39 |
40 | // Prohibit use of == and != in favor of === and !==.
41 | "eqeqeq": true,
42 |
43 | // Suppress warnings about == null comparisons.
44 | "eqnull": true,
45 |
46 | // Enforce tab width of 2 spaces.
47 | "indent": 2,
48 |
49 | // Prohibit use of a variable before it is defined.
50 | "latedef": true,
51 |
52 | // Require capitalized names for constructor functions.
53 | "newcap": true,
54 |
55 | // Enforce use of single quotation marks for strings.
56 | "quotmark": "single",
57 |
58 | // Prohibit trailing whitespace.
59 | "trailing": true,
60 |
61 | // Prohibit use of explicitly undeclared variables.
62 | "undef": true,
63 |
64 | // Warn when variables are defined but never used.
65 | "unused": true,
66 |
67 | // Enforce line length to 80 characters
68 | "maxlen": 80,
69 |
70 | // Enforce placing 'use strict' at the top function scope
71 | "strict": true
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "airbnb-style",
3 | "version": "2.0.0",
4 | "description": "A mostly reasonable approach to JavaScript.",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "publish-all": "npm publish && cd ./packages/eslint-config-airbnb && npm publish"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/airbnb/javascript.git"
12 | },
13 | "keywords": [
14 | "style guide",
15 | "lint",
16 | "airbnb",
17 | "es6",
18 | "es2015",
19 | "react",
20 | "jsx"
21 | ],
22 | "author": "Harrison Shoff (https://twitter.com/hshoff)",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/airbnb/javascript/issues"
26 | },
27 | "homepage": "https://github.com/airbnb/javascript"
28 | }
29 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "rules": {
4 | // disable requiring trailing commas because it might be nice to revert to
5 | // being JSON at some point, and I don't want to make big changes now.
6 | "comma-dangle": 0
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 6.0.2 / 2016-02-22
2 | ==================
3 | - [fix] disable [`no-confusing-arrow`][no-confusing-arrow] due to an `eslint` bug ([#752](https://github.com/airbnb/javascript/issues/752))
4 |
5 | 6.0.1 / 2016-02-21
6 | ==================
7 | - [fix] disable [`newline-per-chained-call`][newline-per-chained-call] due to an `eslint` bug ([#748](https://github.com/airbnb/javascript/issues/748))
8 |
9 | 6.0.0 / 2016-02-21
10 | ==================
11 | - [breaking] enable [`array-callback-return`][array-callback-return]
12 | - [breaking] enable [`no-confusing-arrow`][no-confusing-arrow]
13 | - [breaking] enable [`no-new-symbol`][no-new-symbol]
14 | - [breaking] enable [`no-restricted-imports`][no-restricted-imports]
15 | - [breaking] enable [`no-useless-constructor`][no-useless-constructor]
16 | - [breaking] enable [`prefer-rest-params`][prefer-rest-params]
17 | - [breaking] enable [`template-curly-spacing`][template-curly-spacing]
18 | - [breaking] enable [`newline-per-chained-call`][newline-per-chained-call]
19 | - [breaking] enable [`one-var-declaration-per-line`][one-var-declaration-per-line]
20 | - [breaking] enable [`no-self-assign`][no-self-assign]
21 | - [breaking] enable [`no-whitespace-before-property`][no-whitespace-before-property]
22 | - [breaking] [react] enable [`react/jsx-space-before-closing`][react/jsx-space-before-closing]
23 | - [breaking] [react] enable `static-methods` at top of [`react/sort-comp`][react/sort-comp]
24 | - [breaking] [react] don't `ignoreTranspilerName` for [`react/display-name`][react/display-name]
25 | - [peer+dev deps] update `eslint`, `eslint-plugin-react` ([#730](https://github.com/airbnb/javascript/issues/730))
26 |
27 | 5.0.1 / 2016-02-13
28 | ==================
29 | - [fix] `eslint` peerDep should not include breaking changes
30 |
31 | 5.0.0 / 2016-02-03
32 | ==================
33 | - [breaking] disallow unneeded ternary expressions
34 | - [breaking] Avoid lexical declarations in case/default clauses
35 | - [dev deps] update `babel-tape-runner`, `eslint-plugin-react`, `react`, `tape`
36 |
37 | 4.0.0 / 2016-01-22
38 | ==================
39 | - [breaking] require outer IIFE wrapping; flesh out guide section
40 | - [minor] Add missing [`arrow-body-style`][arrow-body-style], [`prefer-template`][prefer-template] rules ([#678](https://github.com/airbnb/javascript/issues/678))
41 | - [minor] Add [`prefer-arrow-callback`][prefer-arrow-callback] to ES6 rules (to match the guide) ([#677](https://github.com/airbnb/javascript/issues/677))
42 | - [Tests] run `npm run lint` as part of tests; fix errors
43 | - [Tests] use `parallelshell` to parallelize npm run-scripts
44 |
45 | 3.1.0 / 2016-01-07
46 | ==================
47 | - [minor] Allow multiple stateless components in a single file
48 |
49 | 3.0.2 / 2016-01-06
50 | ==================
51 | - [fix] Ignore URLs in [`max-len`][max-len] ([#664](https://github.com/airbnb/javascript/issues/664))
52 |
53 | 3.0.1 / 2016-01-06
54 | ==================
55 | - [fix] because we use babel, keywords should not be quoted
56 |
57 | 3.0.0 / 2016-01-04
58 | ==================
59 | - [breaking] enable [`quote-props`][quote-props] rule ([#632](https://github.com/airbnb/javascript/issues/632))
60 | - [breaking] Define a max line length of 100 characters ([#639](https://github.com/airbnb/javascript/issues/639))
61 | - [breaking] [react] Minor cleanup for the React styleguide, add [`react/jsx-no-bind`][react/jsx-no-bind] ([#619](https://github.com/airbnb/javascript/issues/619))
62 | - [breaking] update best-practices config to prevent parameter object manipulation ([#627](https://github.com/airbnb/javascript/issues/627))
63 | - [minor] Enable [`react/no-is-mounted`][react/no-is-mounted] rule (#635, #633)
64 | - [minor] Sort [`react/prefer-es6-class`][react/prefer-es6-class] alphabetically ([#634](https://github.com/airbnb/javascript/issues/634))
65 | - [minor] enable [`react/prefer-es6-class`][react/prefer-es6-class] rule
66 | - Permit strict mode in "legacy" config
67 | - [react] add missing rules from `eslint-plugin-react` (enforcing where necessary) ([#581](https://github.com/airbnb/javascript/issues/581))
68 | - [dev deps] update `eslint-plugin-react`
69 |
70 | 2.1.1 / 2015-12-15
71 | ==================
72 | - [fix] Remove deprecated [`react/jsx-quotes`][react/jsx-quotes] ([#622](https://github.com/airbnb/javascript/issues/622))
73 |
74 | 2.1.0 / 2015-12-15
75 | ==================
76 | - [fix] use `require.resolve` to allow nested `extend`s ([#582](https://github.com/airbnb/javascript/issues/582))
77 | - [new] enable [`object-shorthand`][object-shorthand] rule ([#621](https://github.com/airbnb/javascript/issues/621))
78 | - [new] enable [`arrow-spacing`][arrow-spacing] rule ([#517](https://github.com/airbnb/javascript/issues/517))
79 | - [docs] flesh out react rule defaults ([#618](https://github.com/airbnb/javascript/issues/618))
80 |
81 | 2.0.0 / 2015-12-03
82 | ==================
83 | - [breaking] [`space-before-function-paren`][space-before-function-paren]: require function spacing: `function (` ([#605](https://github.com/airbnb/javascript/issues/605))
84 | - [breaking] [`indent`][indent]: Fix switch statement indentation rule ([#606](https://github.com/airbnb/javascript/issues/606))
85 | - [breaking] [`array-bracket-spacing`][array-bracket-spacing], [`computed-property-spacing`][computed-property-spacing]: disallow spacing inside brackets ([#594](https://github.com/airbnb/javascript/issues/594))
86 | - [breaking] [`object-curly-spacing`][object-curly-spacing]: require padding inside curly braces ([#594](https://github.com/airbnb/javascript/issues/594))
87 | - [breaking] [`space-in-parens`][space-in-parens]: disallow spaces in parens ([#594](https://github.com/airbnb/javascript/issues/594))
88 |
89 | 1.0.2 / 2015-11-25
90 | ==================
91 | - [breaking] [`no-multiple-empty-lines`][no-multiple-empty-lines]: only allow 1 blank line at EOF ([#578](https://github.com/airbnb/javascript/issues/578))
92 | - [new] `restParams`: enable rest params ([#592](https://github.com/airbnb/javascript/issues/592))
93 |
94 | 1.0.1 / 2015-11-25
95 | ==================
96 | - *erroneous publish*
97 |
98 | 1.0.0 / 2015-11-08
99 | ==================
100 | - require `eslint` `v1.0.0` or higher
101 | - remove `babel-eslint` dependency
102 |
103 | 0.1.1 / 2015-11-05
104 | ==================
105 | - remove [`id-length`][id-length] rule ([#569](https://github.com/airbnb/javascript/issues/569))
106 | - enable [`no-mixed-spaces-and-tabs`][no-mixed-spaces-and-tabs] ([#539](https://github.com/airbnb/javascript/issues/539))
107 | - enable [`no-const-assign`][no-const-assign] ([#560](https://github.com/airbnb/javascript/issues/560))
108 | - enable [`space-before-keywords`][space-before-keywords] ([#554](https://github.com/airbnb/javascript/issues/554))
109 |
110 | 0.1.0 / 2015-11-05
111 | ==================
112 | - switch to modular rules files courtesy the [eslint-config-default][ecd] project and [@taion][taion]. [PR][pr-modular]
113 | - export `eslint-config-airbnb/legacy` for ES5-only users. `eslint-config-airbnb/legacy` does not require the `babel-eslint` parser. [PR][pr-legacy]
114 |
115 | 0.0.9 / 2015-09-24
116 | ==================
117 | - add rule [`no-undef`][no-undef]
118 | - add rule [`id-length`][id-length]
119 |
120 | 0.0.8 / 2015-08-21
121 | ==================
122 | - now has a changelog
123 | - now is modular (see instructions above for with react and without react versions)
124 |
125 | 0.0.7 / 2015-07-30
126 | ==================
127 | - TODO: fill in
128 |
129 |
130 | [ecd]: https://github.com/walmartlabs/eslint-config-defaults
131 | [taion]: https://github.com/taion
132 | [pr-modular]: https://github.com/airbnb/javascript/pull/526
133 | [pr-legacy]: https://github.com/airbnb/javascript/pull/527
134 |
135 | [array-bracket-spacing]: http://eslint.org/docs/rules/array-bracket-spacing
136 | [array-callback-return]: http://eslint.org/docs/rules/array-callback-return
137 | [arrow-body-style]: http://eslint.org/docs/rules/arrow-body-style
138 | [arrow-spacing]: http://eslint.org/docs/rules/arrow-spacing
139 | [computed-property-spacing]: http://eslint.org/docs/rules/computed-property-spacing
140 | [id-length]: http://eslint.org/docs/rules/id-length
141 | [indent]: http://eslint.org/docs/rules/indent
142 | [max-len]: http://eslint.org/docs/rules/max-len
143 | [newline-per-chained-call]: http://eslint.org/docs/rules/newline-per-chained-call
144 | [no-confusing-arrow]: http://eslint.org/docs/rules/no-confusing-arrow
145 | [no-const-assign]: http://eslint.org/docs/rules/no-const-assign
146 | [no-mixed-spaces-and-tabs]: http://eslint.org/docs/rules/no-mixed-spaces-and-tabs
147 | [no-multiple-empty-lines]: http://eslint.org/docs/rules/no-multiple-empty-lines
148 | [no-new-symbol]: http://eslint.org/docs/rules/no-new-symbol
149 | [no-restricted-imports]: http://eslint.org/docs/rules/no-restricted-imports
150 | [no-self-assign]: http://eslint.org/docs/rules/no-self-assign
151 | [no-undef]: http://eslint.org/docs/rules/no-undef
152 | [no-useless-constructor]: http://eslint.org/docs/rules/no-useless-constructor
153 | [no-whitespace-before-property]: http://eslint.org/docs/rules/no-whitespace-before-property
154 | [object-curly-spacing]: http://eslint.org/docs/rules/object-curly-spacing
155 | [object-shorthand]: http://eslint.org/docs/rules/object-shorthand
156 | [one-var-declaration-per-line]: http://eslint.org/docs/rules/one-var-declaration-per-line
157 | [prefer-arrow-callback]: http://eslint.org/docs/rules/prefer-arrow-callback
158 | [prefer-rest-params]: http://eslint.org/docs/rules/prefer-rest-params
159 | [prefer-template]: http://eslint.org/docs/rules/prefer-template
160 | [quote-props]: http://eslint.org/docs/rules/quote-props
161 | [space-before-function-paren]: http://eslint.org/docs/rules/space-before-function-paren
162 | [space-before-keywords]: http://eslint.org/docs/rules/space-before-keywords
163 | [space-in-parens]: http://eslint.org/docs/rules/space-in-parens
164 | [template-curly-spacing]: http://eslint.org/docs/rules/template-curly-spacing
165 |
166 | [react/jsx-space-before-closing]: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md
167 | [react/sort-comp]: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md
168 | [react/display-name]: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md
169 | [react/jsx-no-bind]: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
170 | [react/no-is-mounted]: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md
171 | [react/prefer-es6-class]: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md
172 | [react/jsx-quotes]: https://github.com/yannickcr/eslint-plugin-react/blob/f817e37beddddc84b4788969f07c524fa7f0823b/docs/rules/jsx-quotes.md
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/README.md:
--------------------------------------------------------------------------------
1 | # eslint-config-airbnb
2 |
3 | [](http://badge.fury.io/js/eslint-config-airbnb)
4 |
5 | This package provides Airbnb's .eslintrc as an extensible shared config.
6 |
7 | ## Usage
8 |
9 | We export three ESLint configurations for your usage.
10 |
11 | ### eslint-config-airbnb
12 |
13 | Our default export contains all of our ESLint rules, including EcmaScript 6+
14 | and React. It requires `eslint` and `eslint-plugin-react`.
15 |
16 | 1. `npm install --save-dev eslint-config-airbnb eslint-plugin-react eslint`
17 | 2. add `"extends": "airbnb"` to your .eslintrc
18 |
19 | ### eslint-config-airbnb/base
20 |
21 | Lints ES6+ but does not lint React. Requires `eslint`.
22 |
23 | 1. `npm install --save-dev eslint-config-airbnb eslint`
24 | 2. add `"extends": "airbnb/base"` to your .eslintrc
25 |
26 | ### eslint-config-airbnb/legacy
27 |
28 | Lints ES5 and below. Only requires `eslint`.
29 |
30 | 1. `npm install --save-dev eslint-config-airbnb eslint`
31 | 2. add `"extends": "airbnb/legacy"` to your .eslintrc
32 |
33 | See [Airbnb's Javascript styleguide](https://github.com/airbnb/javascript) and
34 | the [ESlint config docs](http://eslint.org/docs/user-guide/configuring#extending-configuration-files)
35 | for more information.
36 |
37 | ## Improving this config
38 |
39 | Consider adding test cases if you're making complicated rules changes, like
40 | anything involving regexes. Perhaps in a distant future, we could use literate
41 | programming to structure our README as test cases for our .eslintrc?
42 |
43 | You can run tests with `npm test`.
44 |
45 | You can make sure this module lints with itself using `npm run lint`.
46 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/base.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'eslint-config-airbnb/legacy',
4 | 'eslint-config-airbnb/rules/es6',
5 | ].map(require.resolve),
6 | rules: {}
7 | };
8 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'eslint-config-airbnb/base',
4 | 'eslint-config-airbnb/rules/strict',
5 | 'eslint-config-airbnb/rules/react',
6 | ].map(require.resolve),
7 | rules: {}
8 | };
9 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/legacy.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'eslint-config-airbnb/rules/best-practices',
4 | 'eslint-config-airbnb/rules/errors',
5 | 'eslint-config-airbnb/rules/legacy',
6 | 'eslint-config-airbnb/rules/node',
7 | 'eslint-config-airbnb/rules/style',
8 | 'eslint-config-airbnb/rules/variables'
9 | ].map(require.resolve),
10 | env: {
11 | browser: true,
12 | node: true,
13 | amd: false,
14 | mocha: false,
15 | jasmine: false
16 | },
17 | ecmaFeatures: {},
18 | globals: {},
19 | rules: {}
20 | };
21 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/node_modules/eslint-config-airbnb:
--------------------------------------------------------------------------------
1 | ..
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-config-airbnb",
3 | "version": "6.0.2",
4 | "description": "Airbnb's ESLint config, following our styleguide",
5 | "main": "index.js",
6 | "scripts": {
7 | "lint": "eslint .",
8 | "tests-only": "babel-tape-runner ./test/test-*.js",
9 | "test": "parallelshell 'npm run lint' 'npm run tests-only'"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/airbnb/javascript"
14 | },
15 | "keywords": [
16 | "eslint",
17 | "eslintconfig",
18 | "config",
19 | "airbnb",
20 | "javascript",
21 | "styleguide"
22 | ],
23 | "author": "Jake Teton-Landis (https://twitter.com/@jitl)",
24 | "contributors": [
25 | {
26 | "name": "Jake Teton-Landis",
27 | "url": "https://twitter.com/jitl"
28 | },
29 | {
30 | "name": "Jordan Harband",
31 | "email": "ljharb@gmail.com",
32 | "url": "http://ljharb.codes"
33 | },
34 | {
35 | "name": "Harrison Shoff",
36 | "url": "https://twitter.com/hshoff"
37 | }
38 | ],
39 | "license": "MIT",
40 | "bugs": {
41 | "url": "https://github.com/airbnb/javascript/issues"
42 | },
43 | "homepage": "https://github.com/airbnb/javascript",
44 | "devDependencies": {
45 | "babel-tape-runner": "^1.3.1",
46 | "eslint": "^2.3.0",
47 | "eslint-plugin-react": "^4.1.0",
48 | "react": "^0.14.7",
49 | "tape": "^4.5.0",
50 | "parallelshell": "^2.0.0"
51 | },
52 | "peerDependencies": {
53 | "eslint": "^2.2.0",
54 | "eslint-plugin-react": "^4.0.0"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "quote-props": 0
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/best-practices.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'rules': {
3 | // enforces getter/setter pairs in objects
4 | 'accessor-pairs': 0,
5 | // enforces return statements in callbacks of array's methods
6 | // http://eslint.org/docs/rules/array-callback-return
7 | 'array-callback-return': 2,
8 | // treat var statements as if they were block scoped
9 | 'block-scoped-var': 2,
10 | // specify the maximum cyclomatic complexity allowed in a program
11 | 'complexity': [0, 11],
12 | // require return statements to either always or never specify values
13 | 'consistent-return': 2,
14 | // specify curly brace conventions for all control statements
15 | 'curly': [2, 'multi-line'],
16 | // require default case in switch statements
17 | 'default-case': 2,
18 | // encourages use of dot notation whenever possible
19 | 'dot-notation': [2, { 'allowKeywords': true }],
20 | // enforces consistent newlines before or after dots
21 | 'dot-location': 0,
22 | // require the use of === and !==
23 | 'eqeqeq': 2,
24 | // make sure for-in loops have an if statement
25 | 'guard-for-in': 2,
26 | // Blacklist certain identifiers to prevent them being used
27 | // http://eslint.org/docs/rules/id-blacklist
28 | 'id-blacklist': 0,
29 | // disallow the use of alert, confirm, and prompt
30 | 'no-alert': 1,
31 | // disallow use of arguments.caller or arguments.callee
32 | 'no-caller': 2,
33 | // disallow lexical declarations in case/default clauses
34 | // http://eslint.org/docs/rules/no-case-declarations.html
35 | 'no-case-declarations': 2,
36 | // disallow division operators explicitly at beginning of regular expression
37 | 'no-div-regex': 0,
38 | // disallow else after a return in an if
39 | 'no-else-return': 2,
40 | // disallow Unnecessary Labels
41 | // http://eslint.org/docs/rules/no-extra-label
42 | 'no-extra-label': 2,
43 | // disallow comparisons to null without a type-checking operator
44 | 'no-eq-null': 0,
45 | // disallow use of eval()
46 | 'no-eval': 2,
47 | // disallow adding to native types
48 | 'no-extend-native': 2,
49 | // disallow unnecessary function binding
50 | 'no-extra-bind': 2,
51 | // disallow fallthrough of case statements
52 | 'no-fallthrough': 2,
53 | // disallow the use of leading or trailing decimal points in numeric literals
54 | 'no-floating-decimal': 2,
55 | // disallow the type conversions with shorter notations
56 | 'no-implicit-coercion': 0,
57 | // disallow use of eval()-like methods
58 | 'no-implied-eval': 2,
59 | // disallow this keywords outside of classes or class-like objects
60 | 'no-invalid-this': 0,
61 | // disallow usage of __iterator__ property
62 | 'no-iterator': 2,
63 | // disallow use of labels for anything other then loops and switches
64 | 'no-labels': [2, { 'allowLoop': false, 'allowSwitch': false }],
65 | // disallow unnecessary nested blocks
66 | 'no-lone-blocks': 2,
67 | // disallow creation of functions within loops
68 | 'no-loop-func': 2,
69 | // disallow use of multiple spaces
70 | 'no-multi-spaces': 2,
71 | // disallow use of multiline strings
72 | 'no-multi-str': 2,
73 | // disallow reassignments of native objects
74 | 'no-native-reassign': 2,
75 | // disallow use of new operator when not part of the assignment or comparison
76 | 'no-new': 2,
77 | // disallow use of new operator for Function object
78 | 'no-new-func': 2,
79 | // disallows creating new instances of String, Number, and Boolean
80 | 'no-new-wrappers': 2,
81 | // disallow use of (old style) octal literals
82 | 'no-octal': 2,
83 | // disallow use of octal escape sequences in string literals, such as
84 | // var foo = 'Copyright \251';
85 | 'no-octal-escape': 2,
86 | // disallow reassignment of function parameters
87 | // disallow parameter object manipulation
88 | // rule: http://eslint.org/docs/rules/no-param-reassign.html
89 | 'no-param-reassign': [2, { 'props': true }],
90 | // disallow use of process.env
91 | 'no-process-env': 0,
92 | // disallow usage of __proto__ property
93 | 'no-proto': 2,
94 | // disallow declaring the same variable more then once
95 | 'no-redeclare': 2,
96 | // disallow use of assignment in return statement
97 | 'no-return-assign': 2,
98 | // disallow use of `javascript:` urls.
99 | 'no-script-url': 2,
100 | // disallow comparisons where both sides are exactly the same
101 | 'no-self-compare': 2,
102 | // disallow use of comma operator
103 | 'no-sequences': 2,
104 | // restrict what can be thrown as an exception
105 | 'no-throw-literal': 2,
106 | // disallow unmodified conditions of loops
107 | // http://eslint.org/docs/rules/no-unmodified-loop-condition
108 | 'no-unmodified-loop-condition': 0,
109 | // disallow usage of expressions in statement position
110 | 'no-unused-expressions': 2,
111 | // disallow unused labels
112 | // http://eslint.org/docs/rules/no-unused-labels
113 | 'no-unused-labels': 2,
114 | // disallow unnecessary .call() and .apply()
115 | 'no-useless-call': 0,
116 | // disallow use of void operator
117 | 'no-void': 0,
118 | // disallow usage of configurable warning terms in comments: e.g. todo
119 | 'no-warning-comments': [0, { 'terms': ['todo', 'fixme', 'xxx'], 'location': 'start' }],
120 | // disallow use of the with statement
121 | 'no-with': 2,
122 | // require use of the second argument for parseInt()
123 | 'radix': 2,
124 | // requires to declare all vars on top of their containing scope
125 | 'vars-on-top': 2,
126 | // require immediate function invocation to be wrapped in parentheses
127 | // http://eslint.org/docs/rules/wrap-iife.html
128 | 'wrap-iife': [2, 'outside'],
129 | // require or disallow Yoda conditions
130 | 'yoda': 2
131 | }
132 | };
133 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/errors.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'rules': {
3 | // disallow assignment in conditional expressions
4 | 'no-cond-assign': [2, 'always'],
5 | // disallow use of console
6 | 'no-console': 1,
7 | // disallow use of constant expressions in conditions
8 | 'no-constant-condition': 1,
9 | // disallow control characters in regular expressions
10 | 'no-control-regex': 2,
11 | // disallow use of debugger
12 | 'no-debugger': 1,
13 | // disallow duplicate arguments in functions
14 | 'no-dupe-args': 2,
15 | // disallow duplicate keys when creating object literals
16 | 'no-dupe-keys': 2,
17 | // disallow a duplicate case label.
18 | 'no-duplicate-case': 2,
19 | // disallow the use of empty character classes in regular expressions
20 | 'no-empty-character-class': 2,
21 | // disallow empty statements
22 | 'no-empty': 2,
23 | // disallow assigning to the exception in a catch block
24 | 'no-ex-assign': 2,
25 | // disallow double-negation boolean casts in a boolean context
26 | 'no-extra-boolean-cast': 0,
27 | // disallow unnecessary parentheses
28 | 'no-extra-parens': [2, 'functions'],
29 | // disallow unnecessary semicolons
30 | 'no-extra-semi': 2,
31 | // disallow overwriting functions written as function declarations
32 | 'no-func-assign': 2,
33 | // disallow function or variable declarations in nested blocks
34 | 'no-inner-declarations': 2,
35 | // disallow invalid regular expression strings in the RegExp constructor
36 | 'no-invalid-regexp': 2,
37 | // disallow irregular whitespace outside of strings and comments
38 | 'no-irregular-whitespace': 2,
39 | // disallow negation of the left operand of an in expression
40 | 'no-negated-in-lhs': 2,
41 | // disallow the use of object properties of the global object (Math and JSON) as functions
42 | 'no-obj-calls': 2,
43 | // disallow multiple spaces in a regular expression literal
44 | 'no-regex-spaces': 2,
45 | // disallow sparse arrays
46 | 'no-sparse-arrays': 2,
47 | // disallow unreachable statements after a return, throw, continue, or break statement
48 | 'no-unreachable': 2,
49 | // disallow comparisons with the value NaN
50 | 'use-isnan': 2,
51 | // ensure JSDoc comments are valid
52 | 'valid-jsdoc': 0,
53 | // ensure that the results of typeof are compared against a valid string
54 | 'valid-typeof': 2,
55 | // Avoid code that looks like two expressions but is actually one
56 | 'no-unexpected-multiline': 0
57 | }
58 | };
59 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/es6.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'env': {
3 | 'es6': true
4 | },
5 | 'parserOptions': {
6 | 'ecmaVersion': 6,
7 | 'sourceType': 'module',
8 | 'ecmaFeatures': {
9 | 'jsx': true,
10 | 'generators': false,
11 | 'objectLiteralDuplicateProperties': false
12 | }
13 | },
14 | 'rules': {
15 | // enforces no braces where they can be omitted
16 | // http://eslint.org/docs/rules/arrow-body-style
17 | 'arrow-body-style': [2, 'as-needed'],
18 | // require parens in arrow function arguments
19 | 'arrow-parens': 0,
20 | // require space before/after arrow function's arrow
21 | // https://github.com/eslint/eslint/blob/master/docs/rules/arrow-spacing.md
22 | 'arrow-spacing': [2, { 'before': true, 'after': true }],
23 | // require trailing commas in multiline object literals
24 | 'comma-dangle': [2, 'always-multiline'],
25 | // verify super() callings in constructors
26 | 'constructor-super': 0,
27 | // enforce the spacing around the * in generator functions
28 | 'generator-star-spacing': 0,
29 | // disallow modifying variables of class declarations
30 | 'no-class-assign': 0,
31 | // disallow arrow functions where they could be confused with comparisons
32 | // http://eslint.org/docs/rules/no-confusing-arrow
33 | 'no-confusing-arrow': 0,
34 | // disallow modifying variables that are declared using const
35 | 'no-const-assign': 2,
36 | // disallow symbol constructor
37 | // http://eslint.org/docs/rules/no-new-symbol
38 | 'no-new-symbol': 2,
39 | // disallow specific globals
40 | 'no-restricted-globals': 0,
41 | // disallow specific imports
42 | // http://eslint.org/docs/rules/no-restricted-imports
43 | 'no-restricted-imports': 0,
44 | // disallow to use this/super before super() calling in constructors.
45 | 'no-this-before-super': 0,
46 | // require let or const instead of var
47 | 'no-var': 2,
48 | // disallow unnecessary constructor
49 | // http://eslint.org/docs/rules/no-useless-constructor
50 | 'no-useless-constructor': 2,
51 | // require method and property shorthand syntax for object literals
52 | // https://github.com/eslint/eslint/blob/master/docs/rules/object-shorthand.md
53 | 'object-shorthand': [2, 'always'],
54 | // suggest using arrow functions as callbacks
55 | 'prefer-arrow-callback': 2,
56 | // suggest using of const declaration for variables that are never modified after declared
57 | 'prefer-const': 2,
58 | // suggest using the spread operator instead of .apply()
59 | 'prefer-spread': 0,
60 | // suggest using Reflect methods where applicable
61 | 'prefer-reflect': 0,
62 | // use rest parameters instead of arguments
63 | // http://eslint.org/docs/rules/prefer-rest-params
64 | 'prefer-rest-params': 2,
65 | // suggest using template literals instead of string concatenation
66 | // http://eslint.org/docs/rules/prefer-template
67 | 'prefer-template': 2,
68 | // disallow generator functions that do not have yield
69 | 'require-yield': 0,
70 | // import sorting
71 | // http://eslint.org/docs/rules/sort-imports
72 | 'sort-imports': 0,
73 | // enforce usage of spacing in template strings
74 | // http://eslint.org/docs/rules/template-curly-spacing
75 | 'template-curly-spacing': 2,
76 | // enforce spacing around the * in yield* expressions
77 | // http://eslint.org/docs/rules/yield-star-spacing
78 | 'yield-star-spacing': [2, 'after']
79 | }
80 | };
81 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/legacy.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'rules': {
3 | // disallow trailing commas in object literals
4 | 'comma-dangle': [2, 'never'],
5 | // specify the maximum depth that blocks can be nested
6 | 'max-depth': [0, 4],
7 | // limits the number of parameters that can be used in the function declaration.
8 | 'max-params': [0, 3],
9 | // specify the maximum number of statement allowed in a function
10 | 'max-statements': [0, 10],
11 | // disallow use of bitwise operators
12 | 'no-bitwise': 0,
13 | // disallow use of unary operators, ++ and --
14 | 'no-plusplus': 0
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/node.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'env': {
3 | 'node': true
4 | },
5 | 'rules': {
6 | // enforce return after a callback
7 | 'callback-return': 0,
8 | // enforces error handling in callbacks (node environment)
9 | 'handle-callback-err': 0,
10 | // disallow mixing regular variable and require declarations
11 | 'no-mixed-requires': [0, false],
12 | // disallow use of new operator with the require function
13 | 'no-new-require': 0,
14 | // disallow string concatenation with __dirname and __filename
15 | 'no-path-concat': 0,
16 | // disallow process.exit()
17 | 'no-process-exit': 0,
18 | // restrict usage of specified node modules
19 | 'no-restricted-modules': 0,
20 | // disallow use of synchronous methods (off by default)
21 | 'no-sync': 0
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/react.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'plugins': [
3 | 'react'
4 | ],
5 | 'ecmaFeatures': {
6 | 'jsx': true
7 | },
8 | // View link below for react rules documentation
9 | // https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules
10 | 'rules': {
11 | // Prevent missing displayName in a React component definition
12 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md
13 | 'react/display-name': [0, { 'ignoreTranspilerName': false }],
14 | // Forbid certain propTypes (any, array, object)
15 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-prop-types.md
16 | 'react/forbid-prop-types': [0, { 'forbid': ['any', 'array', 'object'] }],
17 | // Enforce boolean attributes notation in JSX
18 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
19 | 'react/jsx-boolean-value': [2, 'never'],
20 | // Validate closing bracket location in JSX
21 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md
22 | 'react/jsx-closing-bracket-location': [2, 'line-aligned'],
23 | // Enforce or disallow spaces inside of curly braces in JSX attributes
24 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md
25 | 'react/jsx-curly-spacing': [0, 'never', { 'allowMultiline': true }],
26 | // Enforce event handler naming conventions in JSX
27 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
28 | 'react/jsx-handler-names': [0, {
29 | 'eventHandlerPrefix': 'handle',
30 | 'eventHandlerPropPrefix': 'on',
31 | }],
32 | // Validate props indentation in JSX
33 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md
34 | 'react/jsx-indent-props': [2, 2],
35 | // Validate JSX has key prop when in array or iterator
36 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md
37 | 'react/jsx-key': 0,
38 | // Limit maximum of props on a single line in JSX
39 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md
40 | 'react/jsx-max-props-per-line': [0, { 'maximum': 1 }],
41 | // Prevent usage of .bind() and arrow functions in JSX props
42 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
43 | 'react/jsx-no-bind': 2,
44 | // Prevent duplicate props in JSX
45 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md
46 | 'react/jsx-no-duplicate-props': [0, { 'ignoreCase': false }],
47 | // Prevent usage of unwrapped JSX strings
48 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md
49 | 'react/jsx-no-literals': 0,
50 | // Disallow undeclared variables in JSX
51 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md
52 | 'react/jsx-no-undef': 2,
53 | // Enforce PascalCase for user-defined JSX components
54 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md
55 | 'react/jsx-pascal-case': 0,
56 | // Enforce propTypes declarations alphabetical sorting
57 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md
58 | 'react/sort-prop-types': [0, {
59 | 'ignoreCase': false,
60 | 'callbacksLast': false,
61 | }],
62 | // Enforce props alphabetical sorting
63 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md
64 | 'react/jsx-sort-props': [0, {
65 | 'ignoreCase': false,
66 | 'callbacksLast': false,
67 | }],
68 | // Prevent React to be incorrectly marked as unused
69 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md
70 | 'react/jsx-uses-react': [2, { 'pragma': 'React' }],
71 | // Prevent variables used in JSX to be incorrectly marked as unused
72 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md
73 | 'react/jsx-uses-vars': 2,
74 | // Prevent usage of dangerous JSX properties
75 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md
76 | 'react/no-danger': 0,
77 | // Prevent usage of deprecated methods
78 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md
79 | 'react/no-deprecated': [1, { 'react': '0.14.0' }],
80 | // Prevent usage of setState in componentDidMount
81 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md
82 | 'react/no-did-mount-set-state': [2, 'allow-in-func'],
83 | // Prevent usage of setState in componentDidUpdate
84 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md
85 | 'react/no-did-update-set-state': [2, 'allow-in-func'],
86 | // Prevent direct mutation of this.state
87 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md
88 | 'react/no-direct-mutation-state': 0,
89 | // Prevent usage of isMounted
90 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md
91 | 'react/no-is-mounted': 2,
92 | // Prevent multiple component definition per file
93 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md
94 | 'react/no-multi-comp': [2, { 'ignoreStateless': true }],
95 | // Prevent usage of setState
96 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md
97 | 'react/no-set-state': 0,
98 | // Prevent using string references
99 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md
100 | 'react/no-string-refs': 0,
101 | // Prevent usage of unknown DOM property
102 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md
103 | 'react/no-unknown-property': 2,
104 | // Require ES6 class declarations over React.createClass
105 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md
106 | 'react/prefer-es6-class': [2, 'always'],
107 | // Prevent missing props validation in a React component definition
108 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md
109 | 'react/prop-types': [2, { 'ignore': [], 'customValidators': [] }],
110 | // Prevent missing React when using JSX
111 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md
112 | 'react/react-in-jsx-scope': 2,
113 | // Restrict file extensions that may be required
114 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-extension.md
115 | 'react/require-extension': [0, { 'extensions': ['.jsx'] }],
116 | // Prevent extra closing tags for components without children
117 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
118 | 'react/self-closing-comp': 2,
119 | // Enforce spaces before the closing bracket of self-closing JSX elements
120 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md
121 | 'react/jsx-space-before-closing': [2, 'always'],
122 | // Enforce component methods order
123 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md
124 | 'react/sort-comp': [2, {
125 | 'order': [
126 | 'static-methods',
127 | 'lifecycle',
128 | '/^on.+$/',
129 | '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/',
130 | 'everything-else',
131 | '/^render.+$/',
132 | 'render'
133 | ]
134 | }],
135 | // Prevent missing parentheses around multilines JSX
136 | // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md
137 | 'react/wrap-multilines': [2, {
138 | declaration: true,
139 | assignment: true,
140 | return: true
141 | }],
142 | }
143 | };
144 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/strict.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'rules': {
3 | // babel inserts `'use strict';` for us
4 | 'strict': [2, 'never']
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/style.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'rules': {
3 | // enforce spacing inside array brackets
4 | 'array-bracket-spacing': [2, 'never'],
5 | // enforce one true brace style
6 | 'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
7 | // require camel case names
8 | 'camelcase': [2, { 'properties': 'never' }],
9 | // enforce spacing before and after comma
10 | 'comma-spacing': [2, { 'before': false, 'after': true }],
11 | // enforce one true comma style
12 | 'comma-style': [2, 'last'],
13 | // disallow padding inside computed properties
14 | 'computed-property-spacing': [2, 'never'],
15 | // enforces consistent naming when capturing the current execution context
16 | 'consistent-this': 0,
17 | // enforce newline at the end of file, with no multiple empty lines
18 | 'eol-last': 2,
19 | // require function expressions to have a name
20 | 'func-names': 1,
21 | // enforces use of function declarations or expressions
22 | 'func-style': 0,
23 | // this option enforces minimum and maximum identifier lengths
24 | // (variable names, property names etc.)
25 | 'id-length': 0,
26 | // this option sets a specific tab width for your code
27 | // https://github.com/eslint/eslint/blob/master/docs/rules/indent.md
28 | 'indent': [2, 2, { 'SwitchCase': 1, 'VariableDeclarator': 1 }],
29 | // specify whether double or single quotes should be used in JSX attributes
30 | // http://eslint.org/docs/rules/jsx-quotes
31 | 'jsx-quotes': [2, 'prefer-double'],
32 | // enforces spacing between keys and values in object literal properties
33 | 'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
34 | // require a space before & after certain keywords
35 | 'keyword-spacing': [2, {
36 | 'before': true,
37 | 'after': true,
38 | 'overrides': {
39 | 'return': { 'after': true },
40 | 'throw': { 'after': true },
41 | 'case': { 'after': true }
42 | }
43 | }],
44 | // enforces empty lines around comments
45 | 'lines-around-comment': 0,
46 | // disallow mixed 'LF' and 'CRLF' as linebreaks
47 | 'linebreak-style': 0,
48 | // specify the maximum length of a line in your program
49 | // https://github.com/eslint/eslint/blob/master/docs/rules/max-len.md
50 | 'max-len': [2, 100, 2, {
51 | 'ignoreUrls': true,
52 | 'ignoreComments': false
53 | }],
54 | // specify the maximum depth callbacks can be nested
55 | 'max-nested-callbacks': 0,
56 | // require a capital letter for constructors
57 | 'new-cap': [2, { 'newIsCap': true }],
58 | // disallow the omission of parentheses when invoking a constructor with no arguments
59 | 'new-parens': 0,
60 | // allow/disallow an empty newline after var statement
61 | 'newline-after-var': 0,
62 | // http://eslint.org/docs/rules/newline-before-return
63 | 'newline-before-return': 0,
64 | // enforces new line after each method call in the chain to make it
65 | // more readable and easy to maintain
66 | // http://eslint.org/docs/rules/newline-per-chained-call
67 | 'newline-per-chained-call': [0, { 'ignoreChainWithDepth': 3 }],
68 | // disallow use of the Array constructor
69 | 'no-array-constructor': 2,
70 | // disallow use of the continue statement
71 | 'no-continue': 0,
72 | // disallow comments inline after code
73 | 'no-inline-comments': 0,
74 | // disallow if as the only statement in an else block
75 | 'no-lonely-if': 0,
76 | // disallow mixed spaces and tabs for indentation
77 | 'no-mixed-spaces-and-tabs': 2,
78 | // disallow multiple empty lines and only one newline at the end
79 | 'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
80 | // disallow nested ternary expressions
81 | 'no-nested-ternary': 2,
82 | // disallow use of the Object constructor
83 | 'no-new-object': 2,
84 | // disallow space between function identifier and application
85 | 'no-spaced-func': 2,
86 | // disallow the use of ternary operators
87 | 'no-ternary': 0,
88 | // disallow trailing whitespace at the end of lines
89 | 'no-trailing-spaces': 2,
90 | // disallow dangling underscores in identifiers
91 | 'no-underscore-dangle': 0,
92 | // disallow the use of Boolean literals in conditional expressions
93 | // also, prefer `a || b` over `a ? a : b`
94 | // http://eslint.org/docs/rules/no-unneeded-ternary
95 | 'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
96 | // disallow whitespace before properties
97 | // http://eslint.org/docs/rules/no-whitespace-before-property
98 | 'no-whitespace-before-property': 2,
99 | // require padding inside curly braces
100 | 'object-curly-spacing': [2, 'always'],
101 | // allow just one var statement per function
102 | 'one-var': [2, 'never'],
103 | // require a newline around variable declaration
104 | // http://eslint.org/docs/rules/one-var-declaration-per-line
105 | 'one-var-declaration-per-line': [2, 'always'],
106 | // require assignment operator shorthand where possible or prohibit it entirely
107 | 'operator-assignment': 0,
108 | // enforce operators to be placed before or after line breaks
109 | 'operator-linebreak': 0,
110 | // enforce padding within blocks
111 | 'padded-blocks': [2, 'never'],
112 | // require quotes around object literal property names
113 | // http://eslint.org/docs/rules/quote-props.html
114 | 'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
115 | // specify whether double or single quotes should be used
116 | 'quotes': [2, 'single', 'avoid-escape'],
117 | // require identifiers to match the provided regular expression
118 | 'id-match': 0,
119 | // enforce spacing before and after semicolons
120 | 'semi-spacing': [2, { 'before': false, 'after': true }],
121 | // require or disallow use of semicolons instead of ASI
122 | 'semi': [2, 'always'],
123 | // sort variables within the same declaration block
124 | 'sort-vars': 0,
125 | // require or disallow space before blocks
126 | 'space-before-blocks': 2,
127 | // require or disallow space before function opening parenthesis
128 | // https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md
129 | 'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
130 | // require or disallow spaces inside parentheses
131 | 'space-in-parens': [2, 'never'],
132 | // require spaces around operators
133 | 'space-infix-ops': 2,
134 | // Require or disallow spaces before/after unary operators
135 | 'space-unary-ops': 0,
136 | // require or disallow a space immediately following the // or /* in a comment
137 | 'spaced-comment': [2, 'always', {
138 | 'exceptions': ['-', '+'],
139 | 'markers': ['=', '!'] // space here to support sprockets directives
140 | }],
141 | // require regex literals to be wrapped in parentheses
142 | 'wrap-regex': 0
143 | }
144 | };
145 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/rules/variables.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'rules': {
3 | // enforce or disallow variable initializations at definition
4 | 'init-declarations': 0,
5 | // disallow the catch clause parameter name being the same as a variable in the outer scope
6 | 'no-catch-shadow': 0,
7 | // disallow deletion of variables
8 | 'no-delete-var': 2,
9 | // disallow var and named functions in global scope
10 | // http://eslint.org/docs/rules/no-implicit-globals
11 | 'no-implicit-globals': 0,
12 | // disallow labels that share a name with a variable
13 | 'no-label-var': 0,
14 | // disallow self assignment
15 | // http://eslint.org/docs/rules/no-self-assign
16 | 'no-self-assign': 2,
17 | // disallow shadowing of names such as arguments
18 | 'no-shadow-restricted-names': 2,
19 | // disallow declaration of variables already declared in the outer scope
20 | 'no-shadow': 2,
21 | // disallow use of undefined when initializing variables
22 | 'no-undef-init': 0,
23 | // disallow use of undeclared variables unless mentioned in a /*global */ block
24 | 'no-undef': 2,
25 | // disallow use of undefined variable
26 | 'no-undefined': 0,
27 | // disallow declaration of variables that are not used in the code
28 | 'no-unused-vars': [2, { 'vars': 'local', 'args': 'after-used' }],
29 | // disallow use of variables before they are defined
30 | 'no-use-before-define': 2
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | // disabled because I find it tedious to write tests while following this
4 | // rule
5 | "no-shadow": 0,
6 | // tests uses `t` for tape
7 | "id-length": [2, {"min": 2, "properties": "never", "exceptions": ["t"]}]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/test/test-base.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import test from 'tape';
4 |
5 | const files = {
6 | base: require('../base')
7 | };
8 |
9 | fs.readdirSync(path.join(__dirname, '../rules')).forEach(name => {
10 | if (name === 'react.js') {
11 | return;
12 | }
13 |
14 | files[name] = require(`../rules/${name}`);
15 | });
16 |
17 | Object.keys(files).forEach(name => {
18 | const config = files[name];
19 |
20 | test(`${name}: does not reference react`, t => {
21 | t.plan(2);
22 |
23 | t.notOk(config.plugins, 'plugins is unspecified');
24 |
25 | // scan rules for react/ and fail if any exist
26 | const reactRuleIds = Object.keys(config.rules)
27 | .filter(ruleId => ruleId.indexOf('react/') === 0);
28 | t.deepEquals(reactRuleIds, [], 'there are no react/ rules');
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/test/test-react-order.js:
--------------------------------------------------------------------------------
1 | import test from 'tape';
2 | import { CLIEngine } from 'eslint';
3 | import eslintrc from '../';
4 | import reactRules from '../rules/react';
5 |
6 | const cli = new CLIEngine({
7 | useEslintrc: false,
8 | baseConfig: eslintrc,
9 |
10 | // This rule fails when executing on text.
11 | rules: { indent: 0 },
12 | });
13 |
14 | function lint(text) {
15 | // @see http://eslint.org/docs/developer-guide/nodejs-api.html#executeonfiles
16 | // @see http://eslint.org/docs/developer-guide/nodejs-api.html#executeontext
17 | const linter = cli.executeOnText(text);
18 | return linter.results[0];
19 | }
20 |
21 | function wrapComponent(body) {
22 | return `
23 | import React from 'react';
24 | export default class MyComponent extends React.Component {
25 | ${body}
26 | }
27 | `;
28 | }
29 |
30 | test('validate react prop order', t => {
31 | t.test('make sure our eslintrc has React linting dependencies', t => {
32 | t.plan(1);
33 | t.equal(reactRules.plugins[0], 'react', 'uses eslint-plugin-react');
34 | });
35 |
36 | t.test('passes a good component', t => {
37 | t.plan(3);
38 | const result = lint(wrapComponent(`
39 | componentWillMount() {}
40 | componentDidMount() {}
41 | setFoo() {}
42 | getFoo() {}
43 | setBar() {}
44 | someMethod() {}
45 | renderDogs() {}
46 | render() { return ; }
47 | `));
48 |
49 | t.notOk(result.warningCount, 'no warnings');
50 | t.notOk(result.errorCount, 'no errors');
51 | t.deepEquals(result.messages, [], 'no messages in results');
52 | });
53 |
54 | t.test('order: when random method is first', t => {
55 | t.plan(2);
56 | const result = lint(wrapComponent(`
57 | someMethod() {}
58 | componentWillMount() {}
59 | componentDidMount() {}
60 | setFoo() {}
61 | getFoo() {}
62 | setBar() {}
63 | renderDogs() {}
64 | render() { return ; }
65 | `));
66 |
67 | t.ok(result.errorCount, 'fails');
68 | t.equal(result.messages[0].ruleId, 'react/sort-comp', 'fails due to sort');
69 | });
70 |
71 | t.test('order: when random method after lifecycle methods', t => {
72 | t.plan(2);
73 | const result = lint(wrapComponent(`
74 | componentWillMount() {}
75 | componentDidMount() {}
76 | someMethod() {}
77 | setFoo() {}
78 | getFoo() {}
79 | setBar() {}
80 | renderDogs() {}
81 | render() { return ; }
82 | `));
83 |
84 | t.ok(result.errorCount, 'fails');
85 | t.equal(result.messages[0].ruleId, 'react/sort-comp', 'fails due to sort');
86 | });
87 | });
88 |
--------------------------------------------------------------------------------
/react/README.md:
--------------------------------------------------------------------------------
1 | # Airbnb React/JSX Style Guide
2 |
3 | *一份彙整了在 React 及 JSX 中被普遍使用的風格指南。*
4 |
5 |
6 | ## 目錄
7 |
8 | 1. [基本規範](#basic-rules)
9 | 1. [Class vs `React.createClass` vs stateless](#class-vs-reactcreateclass-vs-stateless)
10 | 1. [命名](#naming)
11 | 1. [宣告](#declaration)
12 | 1. [對齊](#alignment)
13 | 1. [引號](#quotes)
14 | 1. [空格](#spacing)
15 | 1. [Props](#props)
16 | 1. [括號](#parentheses)
17 | 1. [標籤](#tags)
18 | 1. [方法](#methods)
19 | 1. [排序](#ordering)
20 | 1. [`isMounted`](#ismounted)
21 |
22 |
23 | ## 基本規範
24 |
25 | - 一個檔案只包含一個 React 元件。
26 | - 不過,一個檔案可以有多個 [Stateless 或 Pure Components](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions)。eslint: [`react/no-multi-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless).
27 | - 總是使用 JSX 語法。
28 | - 請別使用 `React.createElement`,除非你初始化 app 的檔案不是 JSX。
29 |
30 | ## Class vs `React.createClass` vs stateless
31 |
32 | - 如果你有內部 state 及 / 或 refs,使用 `class extends React.Component` 勝於 `React.createClass`,除非你有一個非常好的理由才使用 mixins。eslint: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md)
33 |
34 | ```javascript
35 | // bad
36 | const Listing = React.createClass({
37 | // ...
38 | render() {
39 | return {this.state.hello}
;
40 | }
41 | });
42 |
43 | // good
44 | class Listing extends React.Component {
45 | // ...
46 | render() {
47 | return {this.state.hello}
;
48 | }
49 | }
50 | ```
51 |
52 | 如果你不使用 state 或 refs,使用一般函式(不是箭頭函式)勝於 classes:
53 |
54 | ```javascript
55 |
56 | // bad
57 | class Listing extends React.Component {
58 | render() {
59 | return {this.props.hello}
;
60 | }
61 | }
62 |
63 | // bad (因為箭頭函式沒有「name」屬性)
64 | const Listing = ({ hello }) => (
65 | {hello}
66 | );
67 |
68 | // good
69 | function Listing({ hello }) {
70 | return {hello}
;
71 | }
72 | ```
73 |
74 |
75 | ## 命名
76 |
77 | - **副檔名**:React 元件的副檔名請使用 `jsx`。
78 | - **檔案名稱**:檔案名稱請使用帕斯卡命名法。例如:`ReservationCard.jsx`。
79 | - **參考命名規範**: React 元件請使用帕斯卡命名法,元件的實例則使用駝峰式大小寫。eslint: [`react/jsx-pascal-case`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md)
80 |
81 | ```javascript
82 | // bad
83 | import reservationCard from './ReservationCard';
84 |
85 | // good
86 | import ReservationCard from './ReservationCard';
87 |
88 | // bad
89 | const ReservationItem = ;
90 |
91 | // good
92 | const reservationItem = ;
93 | ```
94 |
95 | - **元件命名規範**:檔案名稱須和元件名稱一致。例如,`ReservationCard.jsx` 的參考名稱必須為 `ReservationCard`。但對於目錄的根元件請使用 `index.jsx` 作為檔案名稱,並使用目錄名作為元件的名稱:
96 |
97 | ```javascript
98 | // bad
99 | import Footer from './Footer/Footer';
100 |
101 | // bad
102 | import Footer from './Footer/index';
103 |
104 | // good
105 | import Footer from './Footer';
106 | ```
107 |
108 |
109 |
110 | ## 宣告
111 |
112 | - 不要使用 `displayName` 來命名元件。請使用參考來命名元件。
113 |
114 | ```javascript
115 | // bad
116 | export default React.createClass({
117 | displayName: 'ReservationCard',
118 | // stuff goes here
119 | });
120 |
121 | // good
122 | export default class ReservationCard extends React.Component {
123 | }
124 | ```
125 |
126 |
127 | ## 對齊
128 |
129 | - JSX 語法請遵循以下的對齊風格。eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md)
130 |
131 | ```javascript
132 | // bad
133 |
135 |
136 | // good
137 |
141 |
142 | // 如果 props 適合放在同一行,就將它們放在同一行上
143 |
144 |
145 | // 通常子元素必須進行縮排
146 |
150 |
151 |
152 | ```
153 |
154 |
155 | ## 引號
156 |
157 | - 總是在 JSX 的屬性使用雙引號(`"`),但是所有的 JS 請使用單引號。eslint: [`jsx-quotes`](http://eslint.org/docs/rules/jsx-quotes)
158 |
159 | > 為什麼?JSX 屬性[不能包含跳脫的引號](http://eslint.org/docs/rules/jsx-quotes),所以雙引號可以更容易輸入像 `"don't"` 的連接詞。
160 | > 一般的 HTML 屬性通常也使用雙引號而不是單引號,所以 JSX 屬性借鏡了這個慣例。
161 |
162 | ```javascript
163 | // bad
164 |
165 |
166 | // good
167 |
168 |
169 | // bad
170 |
171 |
172 | // good
173 |
174 | ```
175 |
176 |
177 | ## 空格
178 |
179 | - 總是在自身結尾標籤前加上一個空格。
180 |
181 | ```javascript
182 | // bad
183 |
184 |
185 | // very bad
186 |
187 |
188 | // bad
189 |
191 |
192 | // good
193 |
194 | ```
195 |
196 |
197 | ## Props
198 |
199 | - 總是使用駝峰式大小寫命名 prop。
200 |
201 | ```javascript
202 | // bad
203 |
207 |
208 | // good
209 |
213 | ```
214 |
215 | - Omit the value of the prop when it is explicitly `true`. eslint: [`react/jsx-boolean-value`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md)
216 |
217 | ```javascript
218 | // bad
219 |
222 |
223 | // good
224 |
227 | ```
228 |
229 |
230 | ## 括號
231 |
232 | - 當 JSX 的標籤有多行時請使用括號將它們包起來:eslint: [`react/wrap-multilines`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md)
233 |
234 | ```javascript
235 | // bad
236 | render() {
237 | return
238 |
239 | ;
240 | }
241 |
242 | // good
243 | render() {
244 | return (
245 |
246 |
247 |
248 | );
249 | }
250 |
251 | // good, 當只有一行
252 | render() {
253 | const body = hello
;
254 | return {body};
255 | }
256 | ```
257 |
258 |
259 | ## 標籤
260 |
261 | - 沒有子標籤時總是使用自身結尾標籤。eslint: [`react/self-closing-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md)
262 |
263 | ```javascript
264 | // bad
265 |
266 |
267 | // good
268 |
269 | ```
270 |
271 | - 如果你的元件擁有多行屬性,結尾標籤請放在新的一行。eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md)
272 |
273 | ```javascript
274 | // bad
275 |
278 |
279 | // good
280 |
284 | ```
285 |
286 |
287 | ## 方法
288 |
289 | - Bind event handlers for the render method in the constructor. eslint: [`react/jsx-no-bind`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md)
290 |
291 | > Why? A bind call in the render path creates a brand new function on every single render.
292 |
293 | ```javascript
294 | // bad
295 | class extends React.Component {
296 | onClickDiv() {
297 | // do stuff
298 | }
299 |
300 | render() {
301 | return
302 | }
303 | }
304 |
305 | // good
306 | class extends React.Component {
307 | constructor(props) {
308 | super(props);
309 |
310 | this.onClickDiv = this.onClickDiv.bind(this);
311 | }
312 |
313 | onClickDiv() {
314 | // do stuff
315 | }
316 |
317 | render() {
318 | return
319 | }
320 | }
321 | ```
322 |
323 | - React 元件的內部方法不要使用底線當作前綴。
324 |
325 | ```javascript
326 | // bad
327 | React.createClass({
328 | _onClickSubmit() {
329 | // do stuff
330 | },
331 |
332 | // other stuff
333 | });
334 |
335 | // good
336 | class extends React.Component {
337 | onClickSubmit() {
338 | // do stuff
339 | }
340 |
341 | // other stuff
342 | }
343 | ```
344 |
345 |
346 | ## 排序
347 |
348 | - `class extends React.Component` 的排序:
349 |
350 | 1. optional `static` methods
351 | 1. `constructor`
352 | 1. `getChildContext`
353 | 1. `componentWillMount`
354 | 1. `componentDidMount`
355 | 1. `componentWillReceiveProps`
356 | 1. `shouldComponentUpdate`
357 | 1. `componentWillUpdate`
358 | 1. `componentDidUpdate`
359 | 1. `componentWillUnmount`
360 | 1. *clickHandlers 或 eventHandlers* 像是 `onClickSubmit()` 或 `onChangeDescription()`
361 | 1. *getter methods for `render`* 像是 `getSelectReason()` 或 `getFooterContent()`
362 | 1. *Optional render methods* 像是 `renderNavigation()` 或 `renderProfilePicture()`
363 | 1. `render`
364 |
365 | - How to define `propTypes`, `defaultProps`, `contextTypes`, etc...
366 |
367 | ```javascript
368 | import React, { PropTypes } from 'react';
369 |
370 | const propTypes = {
371 | id: PropTypes.number.isRequired,
372 | url: PropTypes.string.isRequired,
373 | text: PropTypes.string,
374 | };
375 |
376 | const defaultProps = {
377 | text: 'Hello World',
378 | };
379 |
380 | class Link extends React.Component {
381 | static methodsAreOk() {
382 | return true;
383 | }
384 |
385 | render() {
386 | return {this.props.text}
387 | }
388 | }
389 |
390 | Link.propTypes = propTypes;
391 | Link.defaultProps = defaultProps;
392 |
393 | export default Link;
394 | ```
395 |
396 | - `React.createClass` 的排序:eslint: [`react/sort-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md)
397 |
398 | 1. `displayName`
399 | 1. `propTypes`
400 | 1. `contextTypes`
401 | 1. `childContextTypes`
402 | 1. `mixins`
403 | 1. `statics`
404 | 1. `defaultProps`
405 | 1. `getDefaultProps`
406 | 1. `getInitialState`
407 | 1. `getChildContext`
408 | 1. `componentWillMount`
409 | 1. `componentDidMount`
410 | 1. `componentWillReceiveProps`
411 | 1. `shouldComponentUpdate`
412 | 1. `componentWillUpdate`
413 | 1. `componentDidUpdate`
414 | 1. `componentWillUnmount`
415 | 1. *clickHandlers 或 eventHandlers* 像是 `onClickSubmit()` 或 `onChangeDescription()`
416 | 1. *getter methods for `render`* 像是 `getSelectReason()` 或 `getFooterContent()`
417 | 1. *Optional render methods* 像是 `renderNavigation()` 或 `renderProfilePicture()`
418 | 1. `render`
419 |
420 | ## `isMounted`
421 |
422 | - 切勿使用`isMounted`。eslint: [`react/no-is-mounted`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md)
423 |
424 | > 為什麼?[`isMounted` 是個 anti-pattern][anti-pattern],在 ES6 classes 不可使用,並且被正式棄用。
425 |
426 | [anti-pattern]: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
427 |
428 | **[⬆ 回到頂端](#table-of-contents)**
429 |
--------------------------------------------------------------------------------