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