├── .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. [註解](#comments)
36 | 1. [空格](#whitespace)
37 | 1. [逗號](#commas)
38 | 1. [分號](#semicolons)
39 | 1. [型別轉換](#type-casting--coercion)
40 | 1. [命名規則](#naming-conventions)
41 | 1. [存取器](#accessors)
42 | 1. [事件](#events)
43 | 1. [jQuery](#jquery)
44 | 1. [ECMAScript 5 相容性](#ecmascript-5-compatibility)
45 | 1. [ECMAScript 6 風格](#ecmascript-6-styles)
46 | 1. [測試](#testing)
47 | 1. [效能](#performance)
48 | 1. [資源](#resources)
49 | 1. [誰在使用](#in-the-wild)
50 | 1. [翻譯](#translation)
51 | 1. [JavaScript 風格指南](#the-javascript-style-guide-guide)
52 | 1. [和我們討論 Javascript](#chat-with-us-about-javascript)
53 | 1. [貢獻者](#contributors)
54 | 1. [授權許可](#license)
55 |
56 |
57 | ## 資料型態
58 |
59 | - [1.1](#1.1) **基本**:你可以直接存取基本資料型態。
60 |
61 | + `字串`
62 | + `數字`
63 | + `布林`
64 | + `null`
65 | + `undefined`
66 |
67 | ```javascript
68 | const foo = 1;
69 | let bar = foo;
70 |
71 | bar = 9;
72 |
73 | console.log(foo, bar); // => 1, 9
74 | ```
75 | - [1.2](#1.2) **複合**:你需要透過引用的方式存取複合資料型態。
76 |
77 | + `物件`
78 | + `陣列`
79 | + `函式`
80 |
81 | ```javascript
82 | const foo = [1, 2];
83 | const bar = foo;
84 |
85 | bar[0] = 9;
86 |
87 | console.log(foo[0], bar[0]); // => 9, 9
88 | ```
89 |
90 | **[⬆ 回到頂端](#table-of-contents)**
91 |
92 |
93 | ## 參考
94 |
95 | - [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)
96 |
97 | > 為什麼?因為這能確保你無法對參考重新賦值,也不會讓你的程式碼有錯誤或難以理解。
98 |
99 | ```javascript
100 | // bad
101 | var a = 1;
102 | var b = 2;
103 |
104 | // good
105 | const a = 1;
106 | const b = 2;
107 | ```
108 |
109 | - [2.2](#2.2) 如果你需要可變動的參考,使用 `let` 代替 `var`。eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) jscs: [`disallowVar`](http://jscs.info/rule/disallowVar)
110 |
111 | > 為什麼?因為 `let` 的作用域是在區塊內,而不像 `var` 是在函式內。
112 |
113 | ```javascript
114 | // bad
115 | var count = 1;
116 | if (true) {
117 | count += 1;
118 | }
119 |
120 | // good, use the let.
121 | let count = 1;
122 | if (true) {
123 | count += 1;
124 | }
125 | ```
126 |
127 | - [2.3](#2.3) 請注意,`let` 與 `const` 的作用域都只在區塊內。
128 |
129 | ```javascript
130 | // const 及 let 只存在於他們被定義的區塊內。
131 | {
132 | let a = 1;
133 | const b = 1;
134 | }
135 | console.log(a); // ReferenceError
136 | console.log(b); // ReferenceError
137 | ```
138 |
139 | **[⬆ 回到頂端](#table-of-contents)**
140 |
141 |
142 | ## 物件
143 |
144 | - [3.1](#3.1) 使用簡潔的語法建立物件。eslint rules: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html).
145 |
146 | ```javascript
147 | // bad
148 | const item = new Object();
149 |
150 | // good
151 | const item = {};
152 | ```
153 |
154 | - [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)
155 |
156 | ```javascript
157 | // bad
158 | const superman = {
159 | default: { clark: 'kent' },
160 | private: true,
161 | };
162 |
163 | // good
164 | const superman = {
165 | defaults: { clark: 'kent' },
166 | hidden: true,
167 | };
168 | ```
169 |
170 | - [3.3](#3.3) 使用同義詞取代保留字。jscs: [`disallowIdentifierNames`](http://jscs.info/rule/disallowIdentifierNames)
171 |
172 | ```javascript
173 | // bad
174 | const superman = {
175 | class: 'alien',
176 | };
177 |
178 | // bad
179 | const superman = {
180 | klass: 'alien',
181 | };
182 |
183 | // good
184 | const superman = {
185 | type: 'alien',
186 | };
187 | ```
188 |
189 |
190 | - [3.4](#3.4) 建立具有動態屬性名稱的物件時請使用可被計算的屬性名稱。
191 |
192 | > 為什麼?因為這樣能夠讓你在同一個地方定義所有的物件屬性。
193 |
194 | ```javascript
195 |
196 | function getKey(k) {
197 | return `a key named ${k}`;
198 | }
199 |
200 | // bad
201 | const obj = {
202 | id: 5,
203 | name: 'San Francisco',
204 | };
205 | obj[getKey('enabled')] = true;
206 |
207 | // good
208 | const obj = {
209 | id: 5,
210 | name: 'San Francisco',
211 | [getKey('enabled')]: true,
212 | };
213 | ```
214 |
215 |
216 | - [3.5](#3.5) 使用物件方法的簡寫。eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals)
217 |
218 | ```javascript
219 | // bad
220 | const atom = {
221 | value: 1,
222 |
223 | addValue: function (value) {
224 | return atom.value + value;
225 | },
226 | };
227 |
228 | // good
229 | const atom = {
230 | value: 1,
231 |
232 | addValue(value) {
233 | return atom.value + value;
234 | },
235 | };
236 | ```
237 |
238 |
239 | - [3.6](#3.6) 使用屬性值的簡寫。eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals)
240 |
241 | > 為什麼?因為寫起來更短且更有描述性。
242 |
243 | ```javascript
244 | const lukeSkywalker = 'Luke Skywalker';
245 |
246 | // bad
247 | const obj = {
248 | lukeSkywalker: lukeSkywalker,
249 | };
250 |
251 | // good
252 | const obj = {
253 | lukeSkywalker,
254 | };
255 | ```
256 |
257 | - [3.7](#3.7) 請在物件宣告的開頭將簡寫的屬性分組。
258 |
259 | > 為什麼?因為這樣能夠很簡單的看出哪些屬性是使用簡寫。
260 |
261 | ```javascript
262 | const anakinSkywalker = 'Anakin Skywalker';
263 | const lukeSkywalker = 'Luke Skywalker';
264 |
265 | // bad
266 | const obj = {
267 | episodeOne: 1,
268 | twoJediWalkIntoACantina: 2,
269 | lukeSkywalker,
270 | episodeThree: 3,
271 | mayTheFourth: 4,
272 | anakinSkywalker,
273 | };
274 |
275 | // good
276 | const obj = {
277 | lukeSkywalker,
278 | anakinSkywalker,
279 | episodeOne: 1,
280 | twoJediWalkIntoACantina: 2,
281 | episodeThree: 3,
282 | mayTheFourth: 4,
283 | };
284 | ```
285 |
286 | - [3.8](#3.8) 只在無效的鍵加上引號。eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects)
287 |
288 | > 為什麼?整體來說,我們認為這在主觀上更容易閱讀。它會改善語法高亮,也能讓多數的 JS 引擎更容易最佳化。
289 |
290 | ```javascript
291 | // bad
292 | const bad = {
293 | 'foo': 3,
294 | 'bar': 4,
295 | 'data-blah': 5,
296 | };
297 |
298 | // good
299 | const good = {
300 | foo: 3,
301 | bar: 4,
302 | 'data-blah': 5,
303 | };
304 | ```
305 |
306 | **[⬆ 回到頂端](#table-of-contents)**
307 |
308 |
309 | ## 陣列
310 |
311 | - [4.1](#4.1) 使用簡潔的語法建立陣列。eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html)
312 |
313 | ```javascript
314 | // bad
315 | const items = new Array();
316 |
317 | // good
318 | const items = [];
319 | ```
320 |
321 | - [4.2](#4.2) 如果你不知道陣列的長度請使用 Array#push。
322 |
323 | ```javascript
324 | const someStack = [];
325 |
326 | // bad
327 | someStack[someStack.length] = 'abracadabra';
328 |
329 | // good
330 | someStack.push('abracadabra');
331 | ```
332 |
333 |
334 | - [4.3](#4.3) 使用陣列的擴展運算子 `...` 來複製陣列。
335 |
336 | ```javascript
337 | // bad
338 | const len = items.length;
339 | const itemsCopy = [];
340 | let i;
341 |
342 | for (i = 0; i < len; i++) {
343 | itemsCopy[i] = items[i];
344 | }
345 |
346 | // good
347 | const itemsCopy = [...items];
348 | ```
349 | - [4.4](#4.4) 如果要轉換一個像陣列的物件至陣列,可以使用 Array#from。
350 |
351 | ```javascript
352 | const foo = document.querySelectorAll('.foo');
353 | const nodes = Array.from(foo);
354 | ```
355 |
356 | - [4.5](#4.5) 在陣列方法的回呼使用 return 宣告。若函式本體是如 [8.2](#8.2) 的單一語法,那麼省略 return 是可以的。eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return)
357 |
358 | ```javascript
359 | // good
360 | [1, 2, 3].map((x) => {
361 | const y = x + 1;
362 | return x * y;
363 | });
364 |
365 | // good
366 | [1, 2, 3].map(x => x + 1);
367 |
368 | // bad
369 | const flat = {};
370 | [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
371 | const flatten = memo.concat(item);
372 | flat[index] = memo.concat(item);
373 | });
374 |
375 | // good
376 | const flat = {};
377 | [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
378 | const flatten = memo.concat(item);
379 | flat[index] = flatten;
380 | return flatten;
381 | });
382 |
383 | // bad
384 | inbox.filter((msg) => {
385 | const { subject, author } = msg;
386 | if (subject === 'Mockingbird') {
387 | return author === 'Harper Lee';
388 | } else {
389 | return false;
390 | }
391 | });
392 |
393 | // good
394 | inbox.filter((msg) => {
395 | const { subject, author } = msg;
396 | if (subject === 'Mockingbird') {
397 | return author === 'Harper Lee';
398 | }
399 |
400 | return false;
401 | });
402 | ```
403 |
404 | **[⬆ 回到頂端](#table-of-contents)**
405 |
406 |
407 | ## 解構子
408 |
409 | - [5.1](#5.1) 存取或使用多屬性的物件時,請使用物件解構子。jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring)
410 |
411 | > 為什麼?因為解構子能夠節省你對這些屬性建立暫時的參考。
412 |
413 | ```javascript
414 | // bad
415 | function getFullName(user) {
416 | const firstName = user.firstName;
417 | const lastName = user.lastName;
418 |
419 | return `${firstName} ${lastName}`;
420 | }
421 |
422 | // good
423 | function getFullName(user) {
424 | const { firstName, lastName } = user;
425 | return `${firstName} ${lastName}`;
426 | }
427 |
428 | // best
429 | function getFullName({ firstName, lastName }) {
430 | return `${firstName} ${lastName}`;
431 | }
432 | ```
433 |
434 | - [5.2](#5.2) 使用陣列解構子。jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring)
435 |
436 | ```javascript
437 | const arr = [1, 2, 3, 4];
438 |
439 | // bad
440 | const first = arr[0];
441 | const second = arr[1];
442 |
443 | // good
444 | const [first, second] = arr;
445 | ```
446 |
447 | - [5.3](#5.3) 需要回傳多個值時請使用物件解構子,而不是陣列解構子。
448 |
449 | > 為什麼?因為你可以增加新的屬性或改變排序且不須更動呼叫的位置。
450 |
451 | ```javascript
452 | // bad
453 | function processInput(input) {
454 | // 這時神奇的事情出現了
455 | return [left, right, top, bottom];
456 | }
457 |
458 | // 呼叫時必須考慮回傳資料的順序。
459 | const [left, __, top] = processInput(input);
460 |
461 | // good
462 | function processInput(input) {
463 | // 這時神奇的事情出現了
464 | return { left, right, top, bottom };
465 | }
466 |
467 | // 呼叫時只需選擇需要的資料
468 | const { left, right } = processInput(input);
469 | ```
470 |
471 |
472 | **[⬆ 回到頂端](#table-of-contents)**
473 |
474 |
475 | ## 字串
476 |
477 | - [6.1](#6.1) 字串請使用單引號 `''`。eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks)
478 |
479 | ```javascript
480 | // bad
481 | const name = "Capt. Janeway";
482 |
483 | // good
484 | const name = 'Capt. Janeway';
485 | ```
486 |
487 | - [6.2](#6.2) 如果字串超過 100 個字元,請使用字串連接符號換行。
488 | - [6.3](#6.3) 注意:過度的長字串連接可能會影響效能。[jsPerf](http://jsperf.com/ya-string-concat) 及[討論串](https://github.com/airbnb/javascript/issues/40)。
489 |
490 | ```javascript
491 | // bad
492 | 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.';
493 |
494 | // bad
495 | const errorMessage = 'This is a super long error that was thrown because \
496 | of Batman. When you stop to think about how Batman had anything to do \
497 | with this, you would get nowhere \
498 | fast.';
499 |
500 | // good
501 | const errorMessage = 'This is a super long error that was thrown because ' +
502 | 'of Batman. When you stop to think about how Batman had anything to do ' +
503 | 'with this, you would get nowhere fast.';
504 | ```
505 |
506 |
507 | - [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)
508 |
509 | > 為什麼?因為模板字串更有可讀性,正確的換行符號及字串插值功能讓語法更簡潔。
510 |
511 | ```javascript
512 | // bad
513 | function sayHi(name) {
514 | return 'How are you, ' + name + '?';
515 | }
516 |
517 | // bad
518 | function sayHi(name) {
519 | return ['How are you, ', name, '?'].join();
520 | }
521 |
522 | // bad
523 | function sayHi(name) {
524 | return `How are you, ${ name }?`;
525 | }
526 |
527 | // good
528 | function sayHi(name) {
529 | return `How are you, ${name}?`;
530 | }
531 | ```
532 | - [6.5](#6.5) 千萬不要在字串中使用 `eval()`,會造成許多的漏洞。
533 |
534 | **[⬆ 回到頂端](#table-of-contents)**
535 |
536 |
537 | ## 函式
538 |
539 | - [7.1](#7.1) 使用函式宣告而不是函式表達式。jscs: [`requireFunctionDeclarations`](http://jscs.info/rule/requireFunctionDeclarations)
540 |
541 | > 為什麼?因為函式宣告是可命名的,所以他們在呼叫堆疊中更容易被識別。此外,函式宣告自身都會被提升,而函式表達式則只有參考會被提升。這個規則使得[箭頭函式](#arrow-functions)可以完全取代函式表達式。
542 |
543 | ```javascript
544 | // bad
545 | const foo = function () {
546 | };
547 |
548 | // good
549 | function foo() {
550 | }
551 | ```
552 |
553 | - [7.2](#7.2) 立即函式:eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE)
554 |
555 | > 為什麼?一個立即函式是個獨立的單元-將函式及呼叫函式的括號包起來明確表示這一點。注意在模組世界的任何地方,你都不需要使用立即函式。
556 |
557 | ```javascript
558 | // 立即函式(IIFE)
559 | (function () {
560 | console.log('Welcome to the Internet. Please follow me.');
561 | }());
562 | ```
563 |
564 | - [7.3](#7.3) 絕對不要在非函式的區塊(if、while 等等)宣告函式。你可以將函式賦予至變數解決這個問題。瀏覽器會允許你這麼做,但不同瀏覽器產生的結果可能會不同。eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html)
565 |
566 | - [7.4](#7.4) **注意:**ECMA-262 將`區塊`定義為陳述式。函式宣告則不是陳述式。[閱讀 ECMA-262 關於這個問題的說明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。
567 |
568 | ```javascript
569 | // bad
570 | if (currentUser) {
571 | function test() {
572 | console.log('Nope.');
573 | }
574 | }
575 |
576 | // good
577 | let test;
578 | if (currentUser) {
579 | test = () => {
580 | console.log('Yup.');
581 | };
582 | }
583 | ```
584 |
585 | - [7.5](#7.5) 請勿將參數命名為 `arguments`,這樣會將覆蓋掉函式作用域傳來的 `arguments`。
586 |
587 | ```javascript
588 | // bad
589 | function nope(name, options, arguments) {
590 | // ...stuff...
591 | }
592 |
593 | // good
594 | function yup(name, options, args) {
595 | // ...stuff...
596 | }
597 | ```
598 |
599 |
600 | - [7.6](#7.6) 絕對不要使用 `arguments`,可以選擇使用 rest 語法 `...` 替代。[`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params)
601 |
602 | > 為什麼?使用 `...` 能夠明確指出你要將參數傳入哪個變數。再加上 rest 參數是一個真正的陣列,而不像 `arguments` 似陣列而非陣列。
603 |
604 | ```javascript
605 | // bad
606 | function concatenateAll() {
607 | const args = Array.prototype.slice.call(arguments);
608 | return args.join('');
609 | }
610 |
611 | // good
612 | function concatenateAll(...args) {
613 | return args.join('');
614 | }
615 | ```
616 |
617 |
618 | - [7.7](#7.7) 使用預設參數的語法,而不是變動函式的參數。
619 |
620 | ```javascript
621 | // really bad
622 | function handleThings(opts) {
623 | // 不!我們不該變動函式的參數。
624 | // Double bad: 如果 opt 是 false ,那們它就會被設定為一個物件,
625 | // 或許你想要這麼做,但是這樣可能會造成一些 Bug。
626 | opts = opts || {};
627 | // ...
628 | }
629 |
630 | // still bad
631 | function handleThings(opts) {
632 | if (opts === void 0) {
633 | opts = {};
634 | }
635 | // ...
636 | }
637 |
638 | // good
639 | function handleThings(opts = {}) {
640 | // ...
641 | }
642 | ```
643 |
644 | - [7.8](#7.8) 使用預設參數時請避免副作用。
645 |
646 | > 為什麼?因為這樣會讓思緒混淆。
647 |
648 | ```javascript
649 | var b = 1;
650 | // bad
651 | function count(a = b++) {
652 | console.log(a);
653 | }
654 | count(); // 1
655 | count(); // 2
656 | count(3); // 3
657 | count(); // 3
658 | ```
659 |
660 | - [7.9](#7.9) 永遠將預設參數放置於最後。
661 |
662 | ```javascript
663 | // bad
664 | function handleThings(opts = {}, name) {
665 | // ...
666 | }
667 |
668 | // good
669 | function handleThings(name, opts = {}) {
670 | // ...
671 | }
672 | ```
673 |
674 | - [7.10](#7.10) 千萬別使用建構函式去建立一個新的函式。
675 |
676 | > 為什麼?透過這種方式建立一個函數來計算字串類似於 eval(),會造成許多的漏洞。
677 |
678 | ```javascript
679 | // bad
680 | var add = new Function('a', 'b', 'return a + b');
681 |
682 | // still bad
683 | var subtract = Function('a', 'b', 'return a - b');
684 | ```
685 |
686 | - [7.11](#7.11) 在函式的標示後放置空格。
687 |
688 | > 為什麼?一致性較好,而且你不應該在新增或刪除名稱時增加或減少空格。
689 |
690 | ```javascript
691 | // bad
692 | const f = function(){};
693 | const g = function (){};
694 | const h = function() {};
695 |
696 | // good
697 | const x = function () {};
698 | const y = function a() {};
699 | ```
700 |
701 | - [7.12](#7.12) 切勿變更參數。eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html)
702 |
703 | > 為什麼?操作作為參數傳入的物件可能導致變數產生原呼叫者不期望的副作用。
704 |
705 | ```javascript
706 | // bad
707 | function f1(obj) {
708 | obj.key = 1;
709 | };
710 |
711 | // good
712 | function f2(obj) {
713 | const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
714 | };
715 | ```
716 |
717 | - [7.13](#7.13) 切勿重新賦值給參數。eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html)
718 |
719 | > 為什麼?將參數重新賦值可能導致意外的行為,尤其在存取 `arguments` 物件時。它可能會引起最佳化的問題,尤其在 V8。
720 |
721 | ```javascript
722 | // bad
723 | function f1(a) {
724 | a = 1;
725 | }
726 |
727 | function f2(a) {
728 | if (!a) { a = 1; }
729 | }
730 |
731 | // good
732 | function f3(a) {
733 | const b = a || 1;
734 | }
735 |
736 | function f4(a = 1) {
737 | }
738 | ```
739 |
740 | **[⬆ 回到頂端](#table-of-contents)**
741 |
742 |
743 | ## 箭頭函式
744 |
745 | - [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)
746 |
747 | > 為什麼?它會在有 `this` 的內部建立了一個新版本的函式,通常功能都是你所想像的,而且語法更為簡潔。
748 |
749 | > 為什麼不?如果你已經有一個相當複雜的函式時,或許你該將邏輯都移到一個函式宣告上。
750 |
751 | ```javascript
752 | // bad
753 | [1, 2, 3].map(function (x) {
754 | const y = x + 1;
755 | return x * y;
756 | });
757 |
758 | // good
759 | [1, 2, 3].map((x) => {
760 | const y = x + 1;
761 | return x * y;
762 | });
763 | ```
764 |
765 | - [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)
766 |
767 | > 為什麼?因為語法修飾。這樣能夠在多個函式鏈結在一起的時候更易讀。
768 |
769 | > 為什麼不?如果你打算回傳一個物件。
770 |
771 | ```javascript
772 | // bad
773 | [1, 2, 3].map(number => {
774 | const nextNumber = number + 1;
775 | `A string containing the ${nextNumber}.`;
776 | });
777 |
778 | // good
779 | [1, 2, 3].map(number => `A string containing the ${number}.`);
780 |
781 | // good
782 | [1, 2, 3].map((number) => {
783 | const nextNumber = number + 1;
784 | return `A string containing the ${nextNumber}.`;
785 | });
786 | ```
787 |
788 | - [8.3](#8.3) 如果表達式跨了多行,請將它們包在括號中增加可讀性。
789 |
790 | > 為什麼?這麼做更清楚的表達函式的開始與結束的位置。
791 |
792 | ```js
793 | // bad
794 | [1, 2, 3].map(number => 'As time went by, the string containing the ' +
795 | `${number} became much longer. So we needed to break it over multiple ` +
796 | 'lines.'
797 | );
798 |
799 | // good
800 | [1, 2, 3].map(number => (
801 | `As time went by, the string containing the ${number} became much ` +
802 | 'longer. So we needed to break it over multiple lines.'
803 | ));
804 | ```
805 |
806 |
807 | - [8.4](#8.4) 如果你的函式只使用一個參數,那麼可以很隨意的省略括號。否則請在參數兩側加上括號。eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam)
808 |
809 | > 為什麼?減少視覺上的混亂。
810 |
811 | ```js
812 | // bad
813 | [1, 2, 3].map((x) => x * x);
814 |
815 | // good
816 | [1, 2, 3].map(x => x * x);
817 |
818 | // good
819 | [1, 2, 3].map(number => (
820 | `A long string with the ${number}. It’s so long that we’ve broken it ` +
821 | 'over multiple lines!'
822 | ));
823 |
824 | // bad
825 | [1, 2, 3].map(x => {
826 | const y = x + 1;
827 | return x * y;
828 | });
829 |
830 | // good
831 | [1, 2, 3].map((x) => {
832 | const y = x + 1;
833 | return x * y;
834 | });
835 | ```
836 |
837 | - [8.5](#8.5) 避免混淆箭頭函式語法(`=>`)及比較運算子(`<=`、`>=`)。eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow)
838 |
839 | ```js
840 | // bad
841 | const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
842 |
843 | // bad
844 | const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
845 |
846 | // good
847 | const itemHeight = item => { return item.height > 256 ? item.largeSize : item.smallSize; }
848 | ```
849 |
850 | **[⬆ 回到頂端](#table-of-contents)**
851 |
852 |
853 | ## 建構子
854 |
855 | - [9.1](#9.1) 總是使用 `class`。避免直接操作 `prototype`。
856 |
857 | > 為什麼?因為 `class` 語法更簡潔且更易讀。
858 |
859 | ```javascript
860 | // bad
861 | function Queue(contents = []) {
862 | this._queue = [...contents];
863 | }
864 | Queue.prototype.pop = function () {
865 | const value = this._queue[0];
866 | this._queue.splice(0, 1);
867 | return value;
868 | }
869 |
870 |
871 | // good
872 | class Queue {
873 | constructor(contents = []) {
874 | this._queue = [...contents];
875 | }
876 | pop() {
877 | const value = this._queue[0];
878 | this._queue.splice(0, 1);
879 | return value;
880 | }
881 | }
882 | ```
883 |
884 | - [9.2](#9.2) 使用 `extends` 繼承。
885 |
886 | > 為什麼?因為他是一個內建繼承原型方法的方式,且不會破壞 `instanceof`。
887 |
888 | ```javascript
889 | // bad
890 | const inherits = require('inherits');
891 | function PeekableQueue(contents) {
892 | Queue.apply(this, contents);
893 | }
894 | inherits(PeekableQueue, Queue);
895 | PeekableQueue.prototype.peek = function () {
896 | return this._queue[0];
897 | }
898 |
899 | // good
900 | class PeekableQueue extends Queue {
901 | peek() {
902 | return this._queue[0];
903 | }
904 | }
905 | ```
906 |
907 | - [9.3](#9.3) 方法可以回傳 `this` 幫助方法鏈結。
908 |
909 | ```javascript
910 | // bad
911 | Jedi.prototype.jump = function () {
912 | this.jumping = true;
913 | return true;
914 | };
915 |
916 | Jedi.prototype.setHeight = function (height) {
917 | this.height = height;
918 | };
919 |
920 | const luke = new Jedi();
921 | luke.jump(); // => true
922 | luke.setHeight(20); // => undefined
923 |
924 | // good
925 | class Jedi {
926 | jump() {
927 | this.jumping = true;
928 | return this;
929 | }
930 |
931 | setHeight(height) {
932 | this.height = height;
933 | return this;
934 | }
935 | }
936 |
937 | const luke = new Jedi();
938 |
939 | luke.jump()
940 | .setHeight(20);
941 | ```
942 |
943 |
944 | - [9.4](#9.4) 可以寫一個 toString() 的方法,但是請確保它可以正常執行且沒有函式副作用。
945 |
946 | ```javascript
947 | class Jedi {
948 | constructor(options = {}) {
949 | this.name = options.name || 'no name';
950 | }
951 |
952 | getName() {
953 | return this.name;
954 | }
955 |
956 | toString() {
957 | return `Jedi - ${this.getName()}`;
958 | }
959 | }
960 | ```
961 |
962 | - [9.5](#9.5) 若類別沒有指定建構子,那它會擁有預設的建構子。一個空的建構子函式或只委派給父類別是不必要的。[`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor)
963 |
964 | ```javascript
965 | // bad
966 | class Jedi {
967 | constructor() {}
968 |
969 | getName() {
970 | return this.name;
971 | }
972 | }
973 |
974 | // bad
975 | class Rey extends Jedi {
976 | constructor(...args) {
977 | super(...args);
978 | }
979 | }
980 |
981 | // good
982 | class Rey extends Jedi {
983 | constructor(...args) {
984 | super(...args);
985 | this.name = 'Rey';
986 | }
987 | }
988 | ```
989 |
990 | **[⬆ 回到頂端](#table-of-contents)**
991 |
992 |
993 |
994 | ## 模組
995 |
996 | - [10.1](#10.1) 總是使用模組(`import`/`export`)勝過一個非標準模組的系統。你可以編譯為喜歡的模組系統。
997 |
998 | > 為什麼?模組就是未來的趨勢,讓我們現在就開始前往未來吧。
999 |
1000 | ```javascript
1001 | // bad
1002 | const AirbnbStyleGuide = require('./AirbnbStyleGuide');
1003 | module.exports = AirbnbStyleGuide.es6;
1004 |
1005 | // ok
1006 | import AirbnbStyleGuide from './AirbnbStyleGuide';
1007 | export default AirbnbStyleGuide.es6;
1008 |
1009 | // best
1010 | import { es6 } from './AirbnbStyleGuide';
1011 | export default es6;
1012 | ```
1013 |
1014 | - [10.2](#10.2) 請別使用萬用字元引入。
1015 |
1016 | > 為什麼?這樣能夠確保你只有一個預設導出。
1017 |
1018 | ```javascript
1019 | // bad
1020 | import * as AirbnbStyleGuide from './AirbnbStyleGuide';
1021 |
1022 | // good
1023 | import AirbnbStyleGuide from './AirbnbStyleGuide';
1024 | ```
1025 |
1026 | - [10.3](#10.3) 然後也不要在引入的地方導出。
1027 |
1028 | > 為什麼?雖然一行程式相當的簡明,但是讓引入及導出各自有明確的方式能夠讓事情保持一致。
1029 |
1030 | ```javascript
1031 | // bad
1032 | // filename es6.js
1033 | export { es6 as default } from './airbnbStyleGuide';
1034 |
1035 | // good
1036 | // filename es6.js
1037 | import { es6 } from './AirbnbStyleGuide';
1038 | export default es6;
1039 | ```
1040 |
1041 | **[⬆ 回到頂端](#table-of-contents)**
1042 |
1043 |
1044 | ## 迭代器及產生器
1045 |
1046 | - [11.1](#11.1) 不要使用迭代器。更好的做法是使用 JavaScript 的高階函式,像是 `map()` 及 `reduce()`,替代如 `for-of ` 的迴圈語法。eslint: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html)
1047 |
1048 | > 為什麼?這加強了我們不變的規則。處理純函式的回傳值讓程式碼更易讀,勝過它所造成的函式副作用。
1049 |
1050 | eslint rules: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html).
1051 |
1052 | ```javascript
1053 | const numbers = [1, 2, 3, 4, 5];
1054 |
1055 | // bad
1056 | let sum = 0;
1057 | for (let num of numbers) {
1058 | sum += num;
1059 | }
1060 |
1061 | sum === 15;
1062 |
1063 | // good
1064 | let sum = 0;
1065 | numbers.forEach(num => sum += num);
1066 | sum === 15;
1067 |
1068 | // best (使用 javascript 的高階函式)
1069 | const sum = numbers.reduce((total, num) => total + num, 0);
1070 | sum === 15;
1071 | ```
1072 |
1073 | - [11.2](#11.2) 現在還不要使用產生器。
1074 |
1075 | > 為什麼?因為它現在編譯至 ES5 還沒有編譯得非常好。
1076 |
1077 | **[⬆ 回到頂端](#table-of-contents)**
1078 |
1079 |
1080 | ## 屬性
1081 |
1082 | - [12.1](#12.1) 使用點 `.` 來存取屬性。eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation)
1083 |
1084 | ```javascript
1085 | const luke = {
1086 | jedi: true,
1087 | age: 28,
1088 | };
1089 |
1090 | // bad
1091 | const isJedi = luke['jedi'];
1092 |
1093 | // good
1094 | const isJedi = luke.jedi;
1095 | ```
1096 |
1097 | - [12.2](#12.2) 需要帶參數存取屬性時請使用中括號 `[]`。
1098 |
1099 | ```javascript
1100 | const luke = {
1101 | jedi: true,
1102 | age: 28,
1103 | };
1104 |
1105 | function getProp(prop) {
1106 | return luke[prop];
1107 | }
1108 |
1109 | const isJedi = getProp('jedi');
1110 | ```
1111 |
1112 | **[⬆ 回到頂端](#table-of-contents)**
1113 |
1114 |
1115 | ## 變數
1116 |
1117 | - [13.1](#13.1) 為了避免污染全域的命名空間,請使用 `const` 來宣告變數,如果不這麼做將會產生全域變數。Captain Planet warned us of that.
1118 |
1119 | ```javascript
1120 | // bad
1121 | superPower = new SuperPower();
1122 |
1123 | // good
1124 | const superPower = new SuperPower();
1125 | ```
1126 |
1127 | - [13.2](#13.2) 每個變數只使用一個 `const` 來宣告。eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl)
1128 |
1129 | > 為什麼?因為這樣更容易增加新的變數宣告,而且你也不用擔心替換 `;` 為 `,` 及加入的標點符號不同的問題。
1130 |
1131 | ```javascript
1132 | // bad
1133 | const items = getItems(),
1134 | goSportsTeam = true,
1135 | dragonball = 'z';
1136 |
1137 | // bad
1138 | // (比較上述例子找出錯誤)
1139 | const items = getItems(),
1140 | goSportsTeam = true;
1141 | dragonball = 'z';
1142 |
1143 | // good
1144 | const items = getItems();
1145 | const goSportsTeam = true;
1146 | const dragonball = 'z';
1147 | ```
1148 |
1149 | - [13.3](#13.3) 將所有的 `const` 及 `let` 分組。
1150 |
1151 | > 為什麼?當你需要根據之前已賦值的變數來賦值給未賦值變數時相當有幫助。
1152 |
1153 | ```javascript
1154 | // bad
1155 | let i, len, dragonball,
1156 | items = getItems(),
1157 | goSportsTeam = true;
1158 |
1159 | // bad
1160 | let i;
1161 | const items = getItems();
1162 | let dragonball;
1163 | const goSportsTeam = true;
1164 | let len;
1165 |
1166 | // good
1167 | const goSportsTeam = true;
1168 | const items = getItems();
1169 | let dragonball;
1170 | let i;
1171 | let length;
1172 | ```
1173 |
1174 | - [13.4](#13.4) 在你需要的地方賦值給變數,但是請把它們放在合理的位置。
1175 |
1176 | > 為什麼?因為 `let` 及 `const` 是在區塊作用域內,而不是函式作用域。
1177 |
1178 | ```javascript
1179 | // bad - unnecessary function call
1180 | function checkName(hasName) {
1181 | const name = getName();
1182 |
1183 | if (hasName === 'test') {
1184 | return false;
1185 | }
1186 |
1187 | if (name === 'test') {
1188 | this.setName('');
1189 | return false;
1190 | }
1191 |
1192 | return name;
1193 | }
1194 |
1195 | // good
1196 | function checkName(hasName) {
1197 | if (hasName === 'test') {
1198 | return false;
1199 | }
1200 |
1201 | const name = getName();
1202 |
1203 | if (name === 'test') {
1204 | this.setName('');
1205 | return false;
1206 | }
1207 |
1208 | return name;
1209 | }
1210 | ```
1211 |
1212 | **[⬆ 回到頂端](#table-of-contents)**
1213 |
1214 |
1215 | ## 提升
1216 |
1217 | - [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)是相當重要的。
1218 |
1219 | ```javascript
1220 | // 我們知道這樣是行不通的
1221 | // (假設沒有名為 notDefined 的全域變數)
1222 | function example() {
1223 | console.log(notDefined); // => throws a ReferenceError
1224 | }
1225 |
1226 | // 由於變數提升的關係,
1227 | // 你在引用變數後再宣告變數是行得通的。
1228 | // 注:賦予給變數的 `true` 並不會被提升。
1229 | function example() {
1230 | console.log(declaredButNotAssigned); // => undefined
1231 | var declaredButNotAssigned = true;
1232 | }
1233 |
1234 | // 直譯器會將宣告的變數提升至作用域的最頂層,
1235 | // 表示我們可以將這個例子改寫成以下:
1236 | function example() {
1237 | let declaredButNotAssigned;
1238 | console.log(declaredButNotAssigned); // => undefined
1239 | declaredButNotAssigned = true;
1240 | }
1241 |
1242 | // 使用 const 及 let
1243 | function example() {
1244 | console.log(declaredButNotAssigned); // => throws a ReferenceError
1245 | console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
1246 | const declaredButNotAssigned = true;
1247 | }
1248 | ```
1249 |
1250 | - [14.2](#14.2) 賦予匿名函式的變數會被提升,但函式內容並不會。
1251 |
1252 | ```javascript
1253 | function example() {
1254 | console.log(anonymous); // => undefined
1255 |
1256 | anonymous(); // => TypeError anonymous is not a function
1257 |
1258 | var anonymous = function () {
1259 | console.log('anonymous function expression');
1260 | };
1261 | }
1262 | ```
1263 |
1264 | - [14.3](#14.3) 賦予命名函式的變數會被提升,但函式內容及函式名稱並不會。
1265 |
1266 | ```javascript
1267 | function example() {
1268 | console.log(named); // => undefined
1269 |
1270 | named(); // => TypeError named is not a function
1271 |
1272 | superPower(); // => ReferenceError superPower is not defined
1273 |
1274 | var named = function superPower() {
1275 | console.log('Flying');
1276 | };
1277 | }
1278 |
1279 | // 當函式名稱和變數名稱相同時也是如此。
1280 | function example() {
1281 | console.log(named); // => undefined
1282 |
1283 | named(); // => TypeError named is not a function
1284 |
1285 | var named = function named() {
1286 | console.log('named');
1287 | }
1288 | }
1289 | ```
1290 |
1291 | - [14.4](#14.4) 宣告函式的名稱及函式內容都會被提升。
1292 |
1293 | ```javascript
1294 | function example() {
1295 | superPower(); // => Flying
1296 |
1297 | function superPower() {
1298 | console.log('Flying');
1299 | }
1300 | }
1301 | ```
1302 |
1303 | - 想瞭解更多訊息,請參考 [Ben Cherry](http://www.adequatelygood.com/) 的 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/)。
1304 |
1305 | **[⬆ 回到頂端](#table-of-contents)**
1306 |
1307 |
1308 | ## 條件式與等號
1309 |
1310 | ## Comparison Operators & Equality
1311 |
1312 | - [15.1](#15.1) 請使用 `===` 和 `!==` ,別使用 `==` 及 `!=` 。eslint: [`eqeqeq`](http://eslint.org/docs/rules/eqeqeq.html)
1313 |
1314 | - [15.2](#15.2) 像是 `if` 的條件語法內會使用 `ToBoolean` 的抽象方法強轉類型,並遵循以下規範:
1315 |
1316 | + **物件** 轉換為 **true**
1317 | + **Undefined** 轉換為 **false**
1318 | + **Null** 轉換為 **false**
1319 | + **布林** 轉換為 **該布林值**
1320 | + **數字** 如果是 **+0, -0, 或 NaN** 則轉換為 **false**,其他的皆為 **true**
1321 | + **字串** 如果是空字串 `''` 則轉換為 **false**,其他的皆為 **true**
1322 |
1323 | ```javascript
1324 | if ([0] && []) {
1325 | // true
1326 | // 陣列(即使為空)為一個物件,所以轉換為 true
1327 | }
1328 | ```
1329 |
1330 | - [15.3](#15.3) 使用簡短的方式。
1331 |
1332 | ```javascript
1333 | // bad
1334 | if (name !== '') {
1335 | // ...stuff...
1336 | }
1337 |
1338 | // good
1339 | if (name) {
1340 | // ...stuff...
1341 | }
1342 |
1343 | // bad
1344 | if (collection.length > 0) {
1345 | // ...stuff...
1346 | }
1347 |
1348 | // good
1349 | if (collection.length) {
1350 | // ...stuff...
1351 | }
1352 | ```
1353 |
1354 | - [15.4](#15.4) 想瞭解更多訊息請參考 Angus Croll 的 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108)。
1355 | - [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`).
1356 | - [15.5](#15.5) 若 `case` 與 `default` 包含了宣告語法(例如:`let`、`const`、`function` 及 `class`)時使用大括號來建立區塊。
1357 |
1358 | > 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.
1359 | > 為什麼?宣告語法可以在整個 `switch` 區塊中可見,但是只在進入該 `case` 時初始化。當多個 `case` 語法時會導致嘗試定義相同事情的問題。
1360 |
1361 | eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html).
1362 |
1363 | ```javascript
1364 | // bad
1365 | switch (foo) {
1366 | case 1:
1367 | let x = 1;
1368 | break;
1369 | case 2:
1370 | const y = 2;
1371 | break;
1372 | case 3:
1373 | function f() {}
1374 | break;
1375 | default:
1376 | class C {}
1377 | }
1378 |
1379 | // good
1380 | switch (foo) {
1381 | case 1: {
1382 | let x = 1;
1383 | break;
1384 | }
1385 | case 2: {
1386 | const y = 2;
1387 | break;
1388 | }
1389 | case 3: {
1390 | function f() {}
1391 | break;
1392 | }
1393 | case 4:
1394 | bar();
1395 | break;
1396 | default: {
1397 | class C {}
1398 | }
1399 | }
1400 | ```
1401 |
1402 | - [15.6](#15.6) 不應該使用巢狀的三元運算子,且通常應該使用單行來表示。
1403 |
1404 | eslint rules: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html).
1405 |
1406 | ```javascript
1407 | // bad
1408 | const foo = maybe1 > maybe2
1409 | ? "bar"
1410 | : value1 > value2 ? "baz" : null;
1411 |
1412 | // better
1413 | const maybeNull = value1 > value2 ? 'baz' : null;
1414 |
1415 | const foo = maybe1 > maybe2
1416 | ? 'bar'
1417 | : maybeNull;
1418 |
1419 | // best
1420 | const maybeNull = value1 > value2 ? 'baz' : null;
1421 |
1422 | const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
1423 | ```
1424 |
1425 | - [15.7](#15.7) 避免不必要的三元運算子語法。
1426 |
1427 | eslint rules: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html).
1428 |
1429 | ```javascript
1430 | // bad
1431 | const foo = a ? a : b;
1432 | const bar = c ? true : false;
1433 | const baz = c ? false : true;
1434 |
1435 | // good
1436 | const foo = a || b;
1437 | const bar = !!c;
1438 | const baz = !c;
1439 | ```
1440 |
1441 | **[⬆ 回到頂端](#table-of-contents)**
1442 |
1443 |
1444 | ## 區塊
1445 |
1446 | - [16.1](#16.1) 多行區塊請使用大括號刮起來。
1447 |
1448 | ```javascript
1449 | // bad
1450 | if (test)
1451 | return false;
1452 |
1453 | // good
1454 | if (test) return false;
1455 |
1456 | // good
1457 | if (test) {
1458 | return false;
1459 | }
1460 |
1461 | // bad
1462 | function foo() { return false; }
1463 |
1464 | // good
1465 | function bar() {
1466 | return false;
1467 | }
1468 | ```
1469 |
1470 | - [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)
1471 |
1472 | ```javascript
1473 | // bad
1474 | if (test) {
1475 | thing1();
1476 | thing2();
1477 | }
1478 | else {
1479 | thing3();
1480 | }
1481 |
1482 | // good
1483 | if (test) {
1484 | thing1();
1485 | thing2();
1486 | } else {
1487 | thing3();
1488 | }
1489 | ```
1490 |
1491 |
1492 | **[⬆ 回到頂端](#table-of-contents)**
1493 |
1494 |
1495 | ## 註解
1496 |
1497 | - [17.1](#17.1) 多行註解請使用 `/** ... */` ,包含描述,指定類型以及參數值還有回傳值。
1498 |
1499 | ```javascript
1500 | // bad
1501 | // make() 根據傳入的 tag 名稱回傳一個新的元件
1502 | //
1503 | // @param {String} tag
1504 | // @return {Element} element
1505 | function make(tag) {
1506 |
1507 | // ...stuff...
1508 |
1509 | return element;
1510 | }
1511 |
1512 | // good
1513 | /**
1514 | * make() 根據傳入的 tag 名稱回傳一個新的元件
1515 | *
1516 | * @param {String} tag
1517 | * @return {Element} element
1518 | */
1519 | function make(tag) {
1520 |
1521 | // ...stuff...
1522 |
1523 | return element;
1524 | }
1525 | ```
1526 |
1527 | - [17.2](#17.2) 單行註解請使用 `//`。在欲註解的上方新增一行進行註解。在註解的上方空一行,除非他在區塊的第一行。
1528 |
1529 | ```javascript
1530 | // bad
1531 | const active = true; // 當目前分頁
1532 |
1533 | // good
1534 | // is current tab
1535 | const active = true;
1536 |
1537 | // bad
1538 | function getType() {
1539 | console.log('fetching type...');
1540 | // 設定預設的類型為 'no type'
1541 | const type = this._type || 'no type';
1542 |
1543 | return type;
1544 | }
1545 |
1546 | // good
1547 | function getType() {
1548 | console.log('fetching type...');
1549 |
1550 | // 設定預設的類型為 'no type'
1551 | const type = this._type || 'no type';
1552 |
1553 | return type;
1554 | }
1555 |
1556 | // also good
1557 | function getType() {
1558 | // set the default type to 'no type'
1559 | const type = this._type || 'no type';
1560 |
1561 | return type;
1562 | }
1563 | ```
1564 |
1565 | - [17.3](#17.3) 在註解前方加上 `FIXME` 或 `TODO` 可以幫助其他開發人員快速瞭解這是一個需要重新討論的問題,或是一個等待解決的問題。和一般的註解不同,他們是可被執行的。對應的動作為 `FIXME -- 重新討論並解決` 或 `TODO -- 必須執行`。
1566 |
1567 | - [17.4](#17.4) 使用 `// FIXME:` 標注問題。
1568 |
1569 | ```javascript
1570 | class Calculator extends Abacus {
1571 | constructor() {
1572 | super();
1573 |
1574 | // FIXME: 不該在這使用全域變數
1575 | total = 0;
1576 | }
1577 | }
1578 | ```
1579 |
1580 | - [17.5](#17.5) 使用 `// TODO:` 標注問題的解決方式。
1581 |
1582 | ```javascript
1583 | class Calculator extends Abacus {
1584 | constructor() {
1585 | super();
1586 |
1587 | // TODO: total 應該可被傳入的參數所修改
1588 | this.total = 0;
1589 | }
1590 | }
1591 | ```
1592 |
1593 | **[⬆ 回到頂端](#table-of-contents)**
1594 |
1595 |
1596 | ## 空格
1597 |
1598 | - [18.1](#18.1) 將 Tab 設定為兩個空格。eslint: [`indent`](http://eslint.org/docs/rules/indent.html) jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation)
1599 |
1600 | ```javascript
1601 | // bad
1602 | function foo() {
1603 | ∙∙∙∙const name;
1604 | }
1605 |
1606 | // bad
1607 | function bar() {
1608 | ∙const name;
1609 | }
1610 |
1611 | // good
1612 | function baz() {
1613 | ∙∙const name;
1614 | }
1615 | ```
1616 |
1617 | - [18.2](#18.2) 在大括號前加一個空格。eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements)
1618 |
1619 | ```javascript
1620 | // bad
1621 | function test(){
1622 | console.log('test');
1623 | }
1624 |
1625 | // good
1626 | function test() {
1627 | console.log('test');
1628 | }
1629 |
1630 | // bad
1631 | dog.set('attr',{
1632 | age: '1 year',
1633 | breed: 'Bernese Mountain Dog',
1634 | });
1635 |
1636 | // good
1637 | dog.set('attr', {
1638 | age: '1 year',
1639 | breed: 'Bernese Mountain Dog',
1640 | });
1641 | ```
1642 |
1643 | - [18.3](#18.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)
1644 |
1645 | ```javascript
1646 | // bad
1647 | if(isJedi) {
1648 | fight ();
1649 | }
1650 |
1651 | // good
1652 | if (isJedi) {
1653 | fight();
1654 | }
1655 |
1656 | // bad
1657 | function fight () {
1658 | console.log ('Swooosh!');
1659 | }
1660 |
1661 | // good
1662 | function fight() {
1663 | console.log('Swooosh!');
1664 | }
1665 | ```
1666 |
1667 | - [18.4](#18.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)
1668 |
1669 | ```javascript
1670 | // bad
1671 | const x=y+5;
1672 |
1673 | // good
1674 | const x = y + 5;
1675 | ```
1676 |
1677 | - [18.5](#18.5) 在檔案的最尾端加上一行空白行。
1678 |
1679 | ```javascript
1680 | // bad
1681 | (function (global) {
1682 | // ...stuff...
1683 | })(this);
1684 | ```
1685 |
1686 | ```javascript
1687 | // bad
1688 | (function (global) {
1689 | // ...stuff...
1690 | })(this);↵
1691 | ↵
1692 | ```
1693 |
1694 | ```javascript
1695 | // good
1696 | (function (global) {
1697 | // ...stuff...
1698 | })(this);↵
1699 | ```
1700 |
1701 | - [18.6](#18.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)
1702 |
1703 | ```javascript
1704 | // bad
1705 | $('#items').find('.selected').highlight().end().find('.open').updateCount();
1706 |
1707 | // bad
1708 | $('#items').
1709 | find('.selected').
1710 | highlight().
1711 | end().
1712 | find('.open').
1713 | updateCount();
1714 |
1715 | // good
1716 | $('#items')
1717 | .find('.selected')
1718 | .highlight()
1719 | .end()
1720 | .find('.open')
1721 | .updateCount();
1722 |
1723 | // bad
1724 | const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
1725 | .attr('width', (radius + margin) * 2).append('svg:g')
1726 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
1727 | .call(tron.led);
1728 |
1729 | // good
1730 | const leds = stage.selectAll('.led')
1731 | .data(data)
1732 | .enter().append('svg:svg')
1733 | .classed('led', true)
1734 | .attr('width', (radius + margin) * 2)
1735 | .append('svg:g')
1736 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
1737 | .call(tron.led);
1738 |
1739 | // good
1740 | const leds = stage.selectAll('.led').data(data);
1741 | ```
1742 |
1743 | - [18.7](#18.7) 在區塊的結束及下個語法間加上空行。jscs: [`requirePaddingNewLinesAfterBlocks`](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks)
1744 |
1745 | ```javascript
1746 | // bad
1747 | if (foo) {
1748 | return bar;
1749 | }
1750 | return baz;
1751 |
1752 | // good
1753 | if (foo) {
1754 | return bar;
1755 | }
1756 |
1757 | return baz;
1758 |
1759 | // bad
1760 | const obj = {
1761 | foo() {
1762 | },
1763 | bar() {
1764 | },
1765 | };
1766 | return obj;
1767 |
1768 | // good
1769 | const obj = {
1770 | foo() {
1771 | },
1772 |
1773 | bar() {
1774 | },
1775 | };
1776 |
1777 | return obj;
1778 |
1779 | // bad
1780 | const arr = [
1781 | function foo() {
1782 | },
1783 | function bar() {
1784 | },
1785 | ];
1786 | return arr;
1787 |
1788 | // good
1789 | const arr = [
1790 | function foo() {
1791 | },
1792 |
1793 | function bar() {
1794 | },
1795 | ];
1796 |
1797 | return arr;
1798 | ```
1799 |
1800 | - [18.8](#18.8) 別在區塊中置放空行。eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks)
1801 |
1802 | ```javascript
1803 | // bad
1804 | function bar() {
1805 |
1806 | console.log(foo);
1807 |
1808 | }
1809 |
1810 | // also bad
1811 | if (baz) {
1812 |
1813 | console.log(qux);
1814 | } else {
1815 | console.log(foo);
1816 |
1817 | }
1818 |
1819 | // good
1820 | function bar() {
1821 | console.log(foo);
1822 | }
1823 |
1824 | // good
1825 | if (baz) {
1826 | console.log(qux);
1827 | } else {
1828 | console.log(foo);
1829 | }
1830 | ```
1831 |
1832 | - [18.9](#18.9) 不要在括號內的兩側置放空格。eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses)
1833 |
1834 | ```javascript
1835 | // bad
1836 | function bar( foo ) {
1837 | return foo;
1838 | }
1839 |
1840 | // good
1841 | function bar(foo) {
1842 | return foo;
1843 | }
1844 |
1845 | // bad
1846 | if ( foo ) {
1847 | console.log(foo);
1848 | }
1849 |
1850 | // good
1851 | if (foo) {
1852 | console.log(foo);
1853 | }
1854 | ```
1855 |
1856 | - [18.10](#18.10) 不要在中括號內的兩側置放空格。eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets)
1857 |
1858 | ```javascript
1859 | // bad
1860 | const foo = [ 1, 2, 3 ];
1861 | console.log(foo[ 0 ]);
1862 |
1863 | // good
1864 | const foo = [1, 2, 3];
1865 | console.log(foo[0]);
1866 | ```
1867 |
1868 | - [18.11](#18.11) 在大括號內的兩側置放空格。eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`disallowSpacesInsideObjectBrackets`](http://jscs.info/rule/
1869 |
1870 | ```javascript
1871 | // bad
1872 | const foo = {clark: 'kent'};
1873 |
1874 | // good
1875 | const foo = { clark: 'kent' };
1876 | ```
1877 |
1878 | - [18.12](#18.12) 避免一行的程式碼超過 100 字元(包含空白)。eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength)
1879 |
1880 | > 為什麼?這樣確保可讀性及維護性。
1881 |
1882 | ```javascript
1883 | // bad
1884 | const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';
1885 |
1886 | // bad
1887 | $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
1888 |
1889 | // good
1890 | const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
1891 | 'Whatever wizard constrains a helpful ally. The counterpart ascends!';
1892 |
1893 | // good
1894 | $.ajax({
1895 | method: 'POST',
1896 | url: 'https://airbnb.com/',
1897 | data: { name: 'John' },
1898 | })
1899 | .done(() => console.log('Congratulations!'))
1900 | .fail(() => console.log('You have failed this city.'));
1901 | ```
1902 |
1903 | **[⬆ 回到頂端](#table-of-contents)**
1904 |
1905 |
1906 | ## 逗號
1907 |
1908 | - [19.1](#19.1) 不要將逗號放在前方。eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak)
1909 |
1910 | ```javascript
1911 | // bad
1912 | const story = [
1913 | once
1914 | , upon
1915 | , aTime
1916 | ];
1917 |
1918 | // good
1919 | const story = [
1920 | once,
1921 | upon,
1922 | aTime,
1923 | ];
1924 |
1925 | // bad
1926 | const hero = {
1927 | firstName: 'Ada'
1928 | , lastName: 'Lovelace'
1929 | , birthYear: 1815
1930 | , superPower: 'computers'
1931 | };
1932 |
1933 | // good
1934 | const hero = {
1935 | firstName: 'Ada',
1936 | lastName: 'Lovelace',
1937 | birthYear: 1815,
1938 | superPower: 'computers',
1939 | };
1940 | ```
1941 |
1942 | - [19.2](#19.2) 增加結尾的逗號:**別懷疑**eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma)
1943 |
1944 | > 為什麼?這會讓 Git 的差異列表更乾淨。另外,Babel 轉譯器也會刪除結尾多餘的逗號,也就是說你完全不需要擔心在老舊的瀏覽器發生[多餘逗號的問題](es5/README.md#commas)。
1945 |
1946 | ```javascript
1947 | // bad - 不含多餘逗號的 git 差異列表
1948 | const hero = {
1949 | firstName: 'Florence',
1950 | - lastName: 'Nightingale'
1951 | + lastName: 'Nightingale',
1952 | + inventorOf: ['coxcomb graph', 'modern nursing']
1953 | };
1954 |
1955 | // good - 包含多餘逗號的 git 差異列表
1956 | const hero = {
1957 | firstName: 'Florence',
1958 | lastName: 'Nightingale',
1959 | + inventorOf: ['coxcomb chart', 'modern nursing'],
1960 | };
1961 |
1962 | // bad
1963 | const hero = {
1964 | firstName: 'Dana',
1965 | lastName: 'Scully'
1966 | };
1967 |
1968 | const heroes = [
1969 | 'Batman',
1970 | 'Superman'
1971 | ];
1972 |
1973 | // good
1974 | const hero = {
1975 | firstName: 'Dana',
1976 | lastName: 'Scully',
1977 | };
1978 |
1979 | const heroes = [
1980 | 'Batman',
1981 | 'Superman',
1982 | ];
1983 | ```
1984 |
1985 | **[⬆ 回到頂端](#table-of-contents)**
1986 |
1987 |
1988 | ## 分號
1989 |
1990 | - [20.1](#20.1) **對啦。**eslint: [`semi`](http://eslint.org/docs/rules/semi.html) jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons)
1991 |
1992 | ```javascript
1993 | // bad
1994 | (function () {
1995 | const name = 'Skywalker'
1996 | return name
1997 | })()
1998 |
1999 | // good
2000 | (() => {
2001 | const name = 'Skywalker';
2002 | return name;
2003 | }());
2004 |
2005 | // good(防止當兩個檔案含有立即函式需要合併時,函式被當成參數處理)
2006 | ;(() => {
2007 | const name = 'Skywalker';
2008 | return name;
2009 | }());
2010 | ```
2011 |
2012 | [瞭解更多](http://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214)。
2013 |
2014 | **[⬆ 回到頂端](#table-of-contents)**
2015 |
2016 |
2017 | ## 型別轉換
2018 |
2019 | - [21.1](#21.1) 在開頭的宣告進行強制型別轉換。
2020 | - [21.2](#21.2) 字串:
2021 |
2022 | ```javascript
2023 | // => this.reviewScore = 9;
2024 |
2025 | // bad
2026 | const totalScore = this.reviewScore + '';
2027 |
2028 | // good
2029 | const totalScore = String(this.reviewScore);
2030 | ```
2031 |
2032 | - [21.3](#21.3) 數字:使用 `Number` 做型別轉換,而 `parseInt` 則始終以基數解析字串。eslint: [`radix`](http://eslint.org/docs/rules/radix)
2033 |
2034 | ```javascript
2035 | const inputValue = '4';
2036 |
2037 | // bad
2038 | const val = new Number(inputValue);
2039 |
2040 | // bad
2041 | const val = +inputValue;
2042 |
2043 | // bad
2044 | const val = inputValue >> 0;
2045 |
2046 | // bad
2047 | const val = parseInt(inputValue);
2048 |
2049 | // good
2050 | const val = Number(inputValue);
2051 |
2052 | // good
2053 | const val = parseInt(inputValue, 10);
2054 | ```
2055 |
2056 | - [21.4](#21.4) 如果你因為某個原因在做些瘋狂的事情,但是 `parseInt` 是你的瓶頸,所以你對於[性能方面的原因](http://jsperf.com/coercion-vs-casting/3)而必須使用位元右移,請留下評論並解釋為什麼使用,及你做了哪些事情。
2057 |
2058 | ```javascript
2059 | // good
2060 | /**
2061 | * 使用 parseInt 導致我的程式變慢,改成使用
2062 | * 位元右移強制將字串轉為數字加快了他的速度。
2063 | */
2064 | const val = inputValue >> 0;
2065 | ```
2066 |
2067 | - [21.5](#21.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:
2068 |
2069 | ```javascript
2070 | 2147483647 >> 0 //=> 2147483647
2071 | 2147483648 >> 0 //=> -2147483648
2072 | 2147483649 >> 0 //=> -2147483647
2073 | ```
2074 |
2075 | - [21.6](#21.6) 布林:
2076 |
2077 | ```javascript
2078 | const age = 0;
2079 |
2080 | // bad
2081 | const hasAge = new Boolean(age);
2082 |
2083 | // good
2084 | const hasAge = Boolean(age);
2085 |
2086 | // good
2087 | const hasAge = !!age;
2088 | ```
2089 |
2090 | **[⬆ 回到頂端](#table-of-contents)**
2091 |
2092 |
2093 | ## 命名規則
2094 |
2095 | - [22.1](#22.1) 避免使用單一字母的名稱,讓你的名稱有解釋的含義。
2096 |
2097 | ```javascript
2098 | // bad
2099 | function q() {
2100 | // ...stuff...
2101 | }
2102 |
2103 | // good
2104 | function query() {
2105 | // ..stuff..
2106 | }
2107 | ```
2108 |
2109 | - [22.2](#22.2) 使用駝峰式大小寫命名物件,函式及實例。eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers)
2110 |
2111 | ```javascript
2112 | // bad
2113 | const OBJEcttsssss = {};
2114 | const this_is_my_object = {};
2115 | function c() {}
2116 |
2117 | // good
2118 | const thisIsMyObject = {};
2119 | function thisIsMyFunction() {}
2120 | ```
2121 |
2122 | - [22.3](#22.3) 使用帕斯卡命名法來命名建構子或類別。eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors)
2123 |
2124 | ```javascript
2125 | // bad
2126 | function user(options) {
2127 | this.name = options.name;
2128 | }
2129 |
2130 | const bad = new user({
2131 | name: 'nope',
2132 | });
2133 |
2134 | // good
2135 | class User {
2136 | constructor(options) {
2137 | this.name = options.name;
2138 | }
2139 | }
2140 |
2141 | const good = new User({
2142 | name: 'yup',
2143 | });
2144 | ```
2145 |
2146 | - [22.4](#22.4) 命名私有屬性時請在前面加底線 `_`。eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores)
2147 |
2148 | ```javascript
2149 | // bad
2150 | this.__firstName__ = 'Panda';
2151 | this.firstName_ = 'Panda';
2152 |
2153 | // good
2154 | this._firstName = 'Panda';
2155 | ```
2156 |
2157 | - [22.5](#22.5) 請別儲存 `this` 為參考。請使用箭頭函式或是 Function#bind。jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes)
2158 |
2159 | ```javascript
2160 | // bad
2161 | function foo() {
2162 | const self = this;
2163 | return function () {
2164 | console.log(self);
2165 | };
2166 | }
2167 |
2168 | // bad
2169 | function foo() {
2170 | const that = this;
2171 | return function () {
2172 | console.log(that);
2173 | };
2174 | }
2175 |
2176 | // good
2177 | function foo() {
2178 | return () => {
2179 | console.log(this);
2180 | };
2181 | }
2182 | ```
2183 |
2184 | - [22.6](#22.6) 如果你的檔案只有輸出一個類別,你的檔案名稱必須和你的類別名稱相同。
2185 |
2186 | ```javascript
2187 | // 檔案內容
2188 | class CheckBox {
2189 | // ...
2190 | }
2191 | export default CheckBox;
2192 |
2193 | // 在其他的檔案
2194 | // bad
2195 | import CheckBox from './checkBox';
2196 |
2197 | // bad
2198 | import CheckBox from './check_box';
2199 |
2200 | // good
2201 | import CheckBox from './CheckBox';
2202 | ```
2203 |
2204 | - [22.7](#22.7) 當你導出為預設的函式時請使用駝峰式大小寫。檔案名稱必須與你的函式名稱一致。
2205 |
2206 | ```javascript
2207 | function makeStyleGuide() {
2208 | }
2209 |
2210 | export default makeStyleGuide;
2211 | ```
2212 |
2213 | - [22.8](#22.8) 當你導出為單例 / 函式庫 / 空物件時請使用帕斯卡命名法。
2214 |
2215 | ```javascript
2216 | const AirbnbStyleGuide = {
2217 | es6: {
2218 | }
2219 | };
2220 |
2221 | export default AirbnbStyleGuide;
2222 | ```
2223 |
2224 |
2225 | **[⬆ 回到頂端](#table-of-contents)**
2226 |
2227 |
2228 | ## 存取器
2229 |
2230 | - [23.1](#23.1) 屬性的存取器函式不是必須的。
2231 | - [23.2](#23.2) 別使用 JavaScript 的 getters 或 setters,因為它們會導致意想不到的副作用,而且不易於測試、維護以及進行推測。取而代之,如果你要建立一個存取器函式,請使用 getVal() 及 setVal('hello')。
2232 |
2233 | ```javascript
2234 | // bad
2235 | dragon.age();
2236 |
2237 | // good
2238 | dragon.getAge();
2239 |
2240 | // bad
2241 | dragon.age(25);
2242 |
2243 | // good
2244 | dragon.setAge(25);
2245 | ```
2246 |
2247 | - [23.3](#23.3) 如果屬性是布林,請使用 `isVal()` 或 `hasVal()`。
2248 |
2249 | ```javascript
2250 | // bad
2251 | if (!dragon.age()) {
2252 | return false;
2253 | }
2254 |
2255 | // good
2256 | if (!dragon.hasAge()) {
2257 | return false;
2258 | }
2259 | ```
2260 |
2261 | - [23.4](#23.4) 可以建立 get() 及 set() 函式,但請保持一致。
2262 |
2263 | ```javascript
2264 | class Jedi {
2265 | constructor(options = {}) {
2266 | const lightsaber = options.lightsaber || 'blue';
2267 | this.set('lightsaber', lightsaber);
2268 | }
2269 |
2270 | set(key, val) {
2271 | this[key] = val;
2272 | }
2273 |
2274 | get(key) {
2275 | return this[key];
2276 | }
2277 | }
2278 | ```
2279 |
2280 | **[⬆ 回到頂端](#table-of-contents)**
2281 |
2282 |
2283 | ## 事件
2284 |
2285 | - [24.1](#24.1) 當需要對事件傳入資料時(不論是 DOM 事件或是其他私有事件),請傳入物件替代單一的資料。這樣可以使之後的開發人員直接加入其他的資料到事件裡,而不需更新該事件的處理器。例如,比較不好的做法:
2286 |
2287 | ```javascript
2288 | // bad
2289 | $(this).trigger('listingUpdated', listing.id);
2290 |
2291 | ...
2292 |
2293 | $(this).on('listingUpdated', (e, listingId) => {
2294 | // do something with listingId
2295 | });
2296 | ```
2297 |
2298 | 更好的做法:
2299 |
2300 | ```javascript
2301 | // good
2302 | $(this).trigger('listingUpdated', { listingId: listing.id });
2303 |
2304 | ...
2305 |
2306 | $(this).on('listingUpdated', (e, data) => {
2307 | // do something with data.listingId
2308 | });
2309 | ```
2310 |
2311 | **[⬆ 回到頂端](#table-of-contents)**
2312 |
2313 |
2314 | ## jQuery
2315 |
2316 | - [25.1](#25.1) jQuery 的物件請使用 `$` 當前綴。jscs: [`requireDollarBeforejQueryAssignment`](http://jscs.info/rule/requireDollarBeforejQueryAssignment)
2317 |
2318 | ```javascript
2319 | // bad
2320 | const sidebar = $('.sidebar');
2321 |
2322 | // good
2323 | const $sidebar = $('.sidebar');
2324 |
2325 | // good
2326 | const $sidebarBtn = $('.sidebar-btn');
2327 | ```
2328 |
2329 | - [25.2](#25.2) 快取 jQuery 的查詢。
2330 |
2331 | ```javascript
2332 | // bad
2333 | function setSidebar() {
2334 | $('.sidebar').hide();
2335 |
2336 | // ...stuff...
2337 |
2338 | $('.sidebar').css({
2339 | 'background-color': 'pink'
2340 | });
2341 | }
2342 |
2343 | // good
2344 | function setSidebar() {
2345 | const $sidebar = $('.sidebar');
2346 | $sidebar.hide();
2347 |
2348 | // ...stuff...
2349 |
2350 | $sidebar.css({
2351 | 'background-color': 'pink'
2352 | });
2353 | }
2354 | ```
2355 |
2356 | - [25.3](#25.3) DOM 的查詢請使用層遞的 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。[jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16)
2357 | - [25.4](#25.4) 對作用域內的 jQuery 物件使用 `find` 做查詢。
2358 |
2359 | ```javascript
2360 | // bad
2361 | $('ul', '.sidebar').hide();
2362 |
2363 | // bad
2364 | $('.sidebar').find('ul').hide();
2365 |
2366 | // good
2367 | $('.sidebar ul').hide();
2368 |
2369 | // good
2370 | $('.sidebar > ul').hide();
2371 |
2372 | // good
2373 | $sidebar.find('ul').hide();
2374 | ```
2375 |
2376 | **[⬆ 回到頂端](#table-of-contents)**
2377 |
2378 |
2379 | ## ECMAScript 5 相容性
2380 |
2381 | - [26.1](#26.1) 參考 [Kangax](https://twitter.com/kangax/) 的 ES5 [相容性列表](http://kangax.github.io/es5-compat-table/)。
2382 |
2383 | **[⬆ 回到頂端](#table-of-contents)**
2384 |
2385 |
2386 | ## ECMAScript 6 風格
2387 |
2388 | - [27.1](#27.1) 以下是連結到各個 ES6 特性的列表。
2389 |
2390 | 1. [箭頭函式](#arrow-functions)
2391 | 1. [類別](#constructors)
2392 | 1. [物件簡寫](#es6-object-shorthand)
2393 | 1. [簡潔物件](#es6-object-concise)
2394 | 1. [可計算的物件屬性](#es6-computed-properties)
2395 | 1. [模板字串](#es6-template-literals)
2396 | 1. [解構子](#destructuring)
2397 | 1. [預設參數](#es6-default-parameters)
2398 | 1. [剩餘參數(Rest)](#es6-rest)
2399 | 1. [陣列擴展](#es6-array-spreads)
2400 | 1. [Let 及 Const](#references)
2401 | 1. [迭代器及產生器](#iterators-and-generators)
2402 | 1. [模組](#modules)
2403 |
2404 | **[⬆ 回到頂端](#table-of-contents)**
2405 |
2406 |
2407 | ## 測試
2408 |
2409 | - [28.1](#28.1) **如題。**
2410 |
2411 | ```javascript
2412 | function foo() {
2413 | return true;
2414 | }
2415 | ```
2416 |
2417 | - [28.2](#28.2) **無題,不過很重要**:
2418 | - 不論你用哪個測試框架,你都應該撰寫測試!
2419 | - 力求撰寫許多的純函式,並盡量減少異常發生的機會。
2420 | - 要對 stubs 及 mocks 保持嚴謹——他們可以讓你的測試變得更加脆弱。
2421 | - 我們在 Airbnb 主要使用 [`mocha`](https://www.npmjs.com/package/mocha)。對小型或單獨的模組偶爾使用 [`tape`](https://www.npmjs.com/package/tape)。
2422 | - 努力達到 100% 的測試涵蓋率是個很好的目標,即使實現這件事是不切實際的。
2423 | - 每當你修復完一個 bug,_就撰寫回歸測試_。一個修復完的 bug 若沒有回歸測試,通常在未來肯定會再次發生損壞。
2424 |
2425 | **[⬆ 回到頂端](#table-of-contents)**
2426 |
2427 |
2428 | ## 效能
2429 |
2430 | - [On Layout & Web Performance](http://www.kellegous.com/j/2013/01/26/layout-performance/)
2431 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2)
2432 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost)
2433 | - [Bang Function](http://jsperf.com/bang-function)
2434 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13)
2435 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text)
2436 | - [Long String Concatenation](http://jsperf.com/ya-string-concat)
2437 | - Loading...
2438 |
2439 | **[⬆ 回到頂端](#table-of-contents)**
2440 |
2441 |
2442 | ## 資源
2443 |
2444 | **學習 ES6**
2445 |
2446 | - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html)
2447 | - [ExploringJS](http://exploringjs.com/)
2448 | - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/)
2449 | - [Comprehensive Overview of ES6 Features](http://es6-features.org/)
2450 |
2451 | **請讀這個**
2452 |
2453 | - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html)
2454 |
2455 | **工具**
2456 |
2457 | - Code Style Linters
2458 | + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc)
2459 | + [JSHint](http://jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc)
2460 | + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json)
2461 |
2462 | **其他的風格指南**
2463 |
2464 | - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)
2465 | - [jQuery Core Style Guidelines](http://contribute.jquery.org/style-guide/js/)
2466 | - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js)
2467 |
2468 | **其他風格**
2469 |
2470 | - [Naming this in nested functions](https://gist.github.com/cjohansen/4135065) - Christian Johansen
2471 | - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen
2472 | - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun
2473 | - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman
2474 |
2475 | **瞭解更多**
2476 |
2477 | - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll
2478 | - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer
2479 | - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz
2480 | - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban
2481 | - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock
2482 |
2483 | **書籍**
2484 |
2485 | - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford
2486 | - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov
2487 | - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz
2488 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders
2489 | - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas
2490 | - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw
2491 | - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig
2492 | - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch
2493 | - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault
2494 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg
2495 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
2496 | - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon
2497 | - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov
2498 | - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman
2499 | - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke
2500 | - [You Don't Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson
2501 |
2502 | **部落格**
2503 |
2504 | - [DailyJS](http://dailyjs.com/)
2505 | - [JavaScript Weekly](http://javascriptweekly.com/)
2506 | - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/)
2507 | - [Bocoup Weblog](https://bocoup.com/weblog)
2508 | - [Adequately Good](http://www.adequatelygood.com/)
2509 | - [NCZOnline](https://www.nczonline.net/)
2510 | - [Perfection Kills](http://perfectionkills.com/)
2511 | - [Ben Alman](http://benalman.com/)
2512 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/)
2513 | - [Dustin Diaz](http://dustindiaz.com/)
2514 | - [nettuts](http://code.tutsplus.com/?s=javascript)
2515 |
2516 | **Podcasts**
2517 |
2518 | - [JavaScript Jabber](https://devchat.tv/js-jabber/)
2519 |
2520 |
2521 | **[⬆ 回到頂端](#table-of-contents)**
2522 |
2523 |
2524 | ## 誰在使用
2525 |
2526 | 這是正在使用這份風格指南的組織列表。送一個 pull request 後我們會將你增加到列表上。
2527 |
2528 | - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript)
2529 | - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript)
2530 | - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript)
2531 | - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript)
2532 | - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript)
2533 | - **Avant**: [avantcredit/javascript](https://github.com/avantcredit/javascript)
2534 | - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript)
2535 | - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/)
2536 | - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript)
2537 | - **Brainshark**: [brainshark/javascript](https://github.com/brainshark/javascript)
2538 | - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript-style-guide)
2539 | - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide)
2540 | - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript)
2541 | - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript)
2542 | - **Ecosia**: [ecosia/javascript](https://github.com/ecosia/javascript)
2543 | - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide)
2544 | - **Evolution Gaming**: [evolution-gaming/javascript](https://github.com/evolution-gaming/javascript)
2545 | - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript)
2546 | - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md)
2547 | - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide)
2548 | - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript)
2549 | - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript)
2550 | - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style)
2551 | - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript)
2552 | - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript-style-guide)
2553 | - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript)
2554 | - **HubSpot**: [HubSpot/javascript](https://github.com/HubSpot/javascript)
2555 | - **Hyper**: [hyperoslo/javascript-playbook](https://github.com/hyperoslo/javascript-playbook/blob/master/style.md)
2556 | - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide)
2557 | - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript)
2558 | - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions)
2559 | - **JeopardyBot**: [kesne/jeopardy-bot](https://github.com/kesne/jeopardy-bot/blob/master/STYLEGUIDE.md)
2560 | - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript)
2561 | - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/Javascript-style-guide)
2562 | - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript)
2563 | - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript)
2564 | - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript)
2565 | - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript)
2566 | - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript)
2567 | - **Muber**: [muber/javascript](https://github.com/muber/javascript)
2568 | - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript)
2569 | - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript)
2570 | - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript)
2571 | - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript)
2572 | - **OutBoxSoft**: [OutBoxSoft/javascript](https://github.com/OutBoxSoft/javascript)
2573 | - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript)
2574 | - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide)
2575 | - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript)
2576 | - **React**: [/facebook/react/blob/master/CONTRIBUTING.md#style-guide](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#style-guide)
2577 | - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/blob/master/docs/javascript.md)
2578 | - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide)
2579 | - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide)
2580 | - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript)
2581 | - **Springload**: [springload/javascript](https://github.com/springload/javascript)
2582 | - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript)
2583 | - **Target**: [target/javascript](https://github.com/target/javascript)
2584 | - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript)
2585 | - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript)
2586 | - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide)
2587 | - **WeBox Studio**: [weboxstudio/javascript](https://github.com/weboxstudio/javascript)
2588 | - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript)
2589 | - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript)
2590 | - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript)
2591 |
2592 | **[⬆ 回到頂端](#table-of-contents)**
2593 |
2594 |
2595 | ## 翻譯
2596 |
2597 | This style guide is also available in other languages:
2598 |
2599 | -  **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide)
2600 | -  **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript)
2601 | -  **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide)
2602 | -  **Chinese (Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide)
2603 | -  **Chinese (Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript)
2604 | -  **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide)
2605 | -  **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide)
2606 | -  **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide)
2607 | -  **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide)
2608 | -  **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide)
2609 | -  **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript)
2610 | -  **Russian**: [uprock/javascript](https://github.com/uprock/javascript)
2611 | -  **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide)
2612 | -  **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide)
2613 |
2614 |
2615 | ## JavaScript 風格指南
2616 |
2617 | - [參考](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide)
2618 |
2619 |
2620 | ## 與我們討論 JavaScript
2621 |
2622 | - Find us on [gitter](https://gitter.im/airbnb/javascript).
2623 |
2624 |
2625 | ## 貢獻者
2626 |
2627 | - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors)
2628 |
2629 |
2630 | ## License
2631 |
2632 | (The MIT License)
2633 |
2634 | Copyright (c) 2014-2016 Airbnb
2635 |
2636 | Permission is hereby granted, free of charge, to any person obtaining
2637 | a copy of this software and associated documentation files (the
2638 | 'Software'), to deal in the Software without restriction, including
2639 | without limitation the rights to use, copy, modify, merge, publish,
2640 | distribute, sublicense, and/or sell copies of the Software, and to
2641 | permit persons to whom the Software is furnished to do so, subject to
2642 | the following conditions:
2643 |
2644 | The above copyright notice and this permission notice shall be
2645 | included in all copies or substantial portions of the Software.
2646 |
2647 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2648 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2649 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2650 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2651 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2652 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2653 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2654 |
2655 | **[⬆ 回到頂端](#table-of-contents)**
2656 |
2657 | ## Amendments
2658 |
2659 | 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.
2660 |
2661 | # };
2662 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------