├── .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 | [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) 6 | [![Gitter](https://badges.gitter.im/Join Chat.svg)](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 | - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) 2600 | - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) 2601 | - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) 2602 | - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) 2603 | - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese (Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) 2604 | - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) 2605 | - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) 2606 | - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) 2607 | - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) 2608 | - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) 2609 | - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) 2610 | - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) 2611 | - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) 2612 | - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **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 | [![Gitter](https://badges.gitter.im/Join Chat.svg)](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 = ''; 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 | - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) 1683 | - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) 1684 | - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) 1685 | - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) 1686 | - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese(Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) 1687 | - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) 1688 | - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) 1689 | - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) 1690 | - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) 1691 | - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) 1692 | - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) 1693 | - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) 1694 | - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) 1695 | - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **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 | [![npm version](https://badge.fury.io/js/eslint-config-airbnb.svg)](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 |