├── .gitignore ├── README.md ├── es5 └── README.md ├── linters ├── .eslintrc ├── README.md ├── SublimeLinter │ └── SublimeLinter.sublime-settings └── jshintrc ├── package.json ├── packages └── eslint-config-airbnb │ ├── index.js │ └── package.json └── react └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /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, Meteor edition 4 | 5 | *A mostly reasonable approach to JavaScript, with some Meteor flavor* 6 | 7 | All rules that are automatically linted have the ESLint logo, with a link to the rule, like so: linted by eslint 8 | 9 | ## Table of Contents 10 | 11 | 1. [Types](#types) 12 | 1. [References](#references) 13 | 1. [Objects](#objects) 14 | 1. [Arrays](#arrays) 15 | 1. [Destructuring](#destructuring) 16 | 1. [Strings](#strings) 17 | 1. [Functions](#functions) 18 | 1. [Arrow Functions](#arrow-functions) 19 | 1. [Constructors](#constructors) 20 | 1. [Modules](#modules) 21 | 1. [Iterators and Generators](#iterators-and-generators) 22 | 1. [Properties](#properties) 23 | 1. [Variables](#variables) 24 | 1. [Hoisting](#hoisting) 25 | 1. [Comparison Operators & Equality](#comparison-operators--equality) 26 | 1. [Blocks](#blocks) 27 | 1. [Comments](#comments) 28 | 1. [Whitespace](#whitespace) 29 | 1. [Commas](#commas) 30 | 1. [Semicolons](#semicolons) 31 | 1. [Type Casting & Coercion](#type-casting--coercion) 32 | 1. [Naming Conventions](#naming-conventions) 33 | 1. [Accessors](#accessors) 34 | 1. [Events](#events) 35 | 1. [jQuery](#jquery) 36 | 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility) 37 | 1. [ECMAScript 6 Styles](#ecmascript-6-styles) 38 | 1. [Testing (in AirBnB guide)](https://github.com/airbnb/javascript#testing) 39 | 1. [Performance (in AirBnB guide)](https://github.com/airbnb/javascript#performance) 40 | 1. [Resources (in AirBnB guide)](https://github.com/airbnb/javascript#resources) 41 | 1. [In the Wild (in AirBnB guide)](https://github.com/airbnb/javascript#in-the-wild) 42 | 1. [Translation (in AirBnB guide)](https://github.com/airbnb/javascript#translation) 43 | 1. [The JavaScript Style Guide Guide (in AirBnB guide)](https://github.com/airbnb/javascript#the-javascript-style-guide-guide) 44 | 1. [Chat With Us About Javascript (in AirBnB guide)](https://github.com/airbnb/javascript#chat-with-us-about-javascript) 45 | 1. [Contributors](#contributors) 46 | 1. [License](#license) 47 | 48 | ## Types 49 | 50 | This section has been eliminated in the Meteor version, because it does not specify any style rules. 51 | 52 | ## References 53 | 54 | - [2.1](#2.1) linted by eslint 55 | Use `const` for all of your references; avoid using `var`. 56 | 57 | > Why? This ensures that you can't reassign your references (mutation), which can lead to bugs and difficult to comprehend code. 58 | 59 | ```javascript 60 | // bad 61 | var a = 1; 62 | var b = 2; 63 | 64 | // good 65 | const a = 1; 66 | const b = 2; 67 | ``` 68 | 69 | - [2.2](#2.2) linted by eslint 70 | If you must mutate references, use `let` instead of `var`. 71 | 72 | > Why? `let` is block-scoped rather than function-scoped like `var`. 73 | 74 | ```javascript 75 | // bad 76 | var count = 1; 77 | if (true) { 78 | count += 1; 79 | } 80 | 81 | // good, use the let. 82 | let count = 1; 83 | if (true) { 84 | count += 1; 85 | } 86 | ``` 87 | 88 | - [2.3](#2.3) This section removed in the Meteor edition, because it does not specify any style rules. 89 | 90 | **[⬆ back to top](#table-of-contents)** 91 | 92 | ## Objects 93 | 94 | - [3.1](#3.1) linted by eslint 95 | Use the literal syntax for object creation. 96 | 97 | ```javascript 98 | // bad 99 | const item = new Object(); 100 | 101 | // good 102 | const item = {}; 103 | ``` 104 | 105 | - [3.2](#3.2) and [3.3](#3.3) eliminated in the Meteor edition because Babel can compile reserved words to work fine in old browsers. 106 | 107 | 108 | - [3.4](#3.4) Use computed property names when creating objects with dynamic property names. 109 | 110 | > Why? They allow you to define all the properties of an object in one place. 111 | 112 | ```javascript 113 | 114 | function getKey(k) { 115 | return `a key named ${k}`; 116 | } 117 | 118 | // bad 119 | const obj = { 120 | id: 5, 121 | name: 'San Francisco', 122 | }; 123 | obj[getKey('enabled')] = true; 124 | 125 | // good 126 | const obj = { 127 | id: 5, 128 | name: 'San Francisco', 129 | [getKey('enabled')]: true, 130 | }; 131 | ``` 132 | 133 | 134 | - [3.5](#3.5) Use object method shorthand. 135 | 136 | ```javascript 137 | // bad 138 | const atom = { 139 | value: 1, 140 | 141 | addValue: function (value) { 142 | return atom.value + value; 143 | }, 144 | }; 145 | 146 | // good 147 | const atom = { 148 | value: 1, 149 | 150 | addValue(value) { 151 | return atom.value + value; 152 | }, 153 | }; 154 | ``` 155 | 156 | 157 | - [3.6](#3.6) Use property value shorthand. 158 | 159 | > Why? It is shorter to write and descriptive. 160 | 161 | ```javascript 162 | const lukeSkywalker = 'Luke Skywalker'; 163 | 164 | // bad 165 | const obj = { 166 | lukeSkywalker: lukeSkywalker, 167 | }; 168 | 169 | // good 170 | const obj = { 171 | lukeSkywalker, 172 | }; 173 | ``` 174 | 175 | - [3.7](#3.7) Group your shorthand properties at the beginning of your object declaration. 176 | 177 | > Why? It's easier to tell which properties are using the shorthand. 178 | 179 | ```javascript 180 | const anakinSkywalker = 'Anakin Skywalker'; 181 | const lukeSkywalker = 'Luke Skywalker'; 182 | 183 | // bad 184 | const obj = { 185 | episodeOne: 1, 186 | twoJedisWalkIntoACantina: 2, 187 | lukeSkywalker, 188 | episodeThree: 3, 189 | mayTheFourth: 4, 190 | anakinSkywalker, 191 | }; 192 | 193 | // good 194 | const obj = { 195 | lukeSkywalker, 196 | anakinSkywalker, 197 | episodeOne: 1, 198 | twoJedisWalkIntoACantina: 2, 199 | episodeThree: 3, 200 | mayTheFourth: 4, 201 | }; 202 | ``` 203 | 204 | - [3.8](#3.8) Use object spreads `...` to copy objects. 205 | 206 | ```javascript 207 | // bad 208 | const copy = {}; 209 | Object.keys(obj).forEach((key) => { 210 | copy[key] = obj[key]; 211 | }); 212 | 213 | // bad 214 | const copy = {}; 215 | for (var key in obj) { 216 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 217 | copy[key] = obj[key]; 218 | } 219 | } 220 | 221 | // good 222 | const copy = { ...obj }; 223 | ``` 224 | 225 | **[⬆ back to top](#table-of-contents)** 226 | 227 | ## Arrays 228 | 229 | - [4.1](#4.1) linted by eslint 230 | Use the literal syntax for array creation. 231 | 232 | ```javascript 233 | // bad 234 | const items = new Array(); 235 | 236 | // good 237 | const items = []; 238 | ``` 239 | 240 | - [4.2](#4.2) Use Array#push instead of direct assignment to add items to an array. 241 | 242 | ```javascript 243 | const someStack = []; 244 | 245 | 246 | // bad 247 | someStack[someStack.length] = 'abracadabra'; 248 | 249 | // good 250 | someStack.push('abracadabra'); 251 | ``` 252 | 253 | 254 | - [4.3](#4.3) Use array spreads `...` to copy arrays. 255 | 256 | ```javascript 257 | // bad 258 | const len = items.length; 259 | const itemsCopy = []; 260 | let i; 261 | 262 | for (i = 0; i < len; i++) { 263 | itemsCopy[i] = items[i]; 264 | } 265 | 266 | // good 267 | const itemsCopy = [...items]; 268 | ``` 269 | - [4.4](#4.4) To convert an array-like object to an array, use Array#from. 270 | 271 | ```javascript 272 | const foo = document.querySelectorAll('.foo'); 273 | const nodes = Array.from(foo); 274 | ``` 275 | 276 | **[⬆ back to top](#table-of-contents)** 277 | 278 | ## Destructuring 279 | 280 | - [5.1](#5.1) Use object destructuring when accessing and using multiple properties of an object. 281 | 282 | > Why? Destructuring saves you from creating temporary references for those properties. 283 | 284 | ```javascript 285 | // bad 286 | function getFullName(user) { 287 | const firstName = user.firstName; 288 | const lastName = user.lastName; 289 | 290 | return `${firstName} ${lastName}`; 291 | } 292 | 293 | // good 294 | function getFullName(obj) { 295 | const { firstName, lastName } = obj; 296 | return `${firstName} ${lastName}`; 297 | } 298 | 299 | // best 300 | function getFullName({ firstName, lastName }) { 301 | return `${firstName} ${lastName}`; 302 | } 303 | ``` 304 | 305 | - [5.2](#5.2) Use array destructuring. 306 | 307 | ```javascript 308 | const arr = [1, 2, 3, 4]; 309 | 310 | // bad 311 | const first = arr[0]; 312 | const second = arr[1]; 313 | 314 | // good 315 | const [first, second] = arr; 316 | ``` 317 | 318 | - [5.3](#5.3) Use object destructuring for multiple return values, not array destructuring. 319 | 320 | > Why? You can add new properties over time or change the order of things without breaking call sites. 321 | 322 | ```javascript 323 | // bad 324 | function processInput(input) { 325 | // then a miracle occurs 326 | return [left, right, top, bottom]; 327 | } 328 | 329 | // the caller needs to think about the order of return data 330 | const [left, __, top] = processInput(input); 331 | 332 | // good 333 | function processInput(input) { 334 | // then a miracle occurs 335 | return { left, right, top, bottom }; 336 | } 337 | 338 | // the caller selects only the data they need 339 | const { left, right } = processInput(input); 340 | ``` 341 | 342 | 343 | **[⬆ back to top](#table-of-contents)** 344 | 345 | ## Strings 346 | 347 | - [6.1](#6.1) linted by eslint 348 | Use single quotes `''` for strings. 349 | 350 | ```javascript 351 | // bad 352 | const name = "Capt. Janeway"; 353 | 354 | // good 355 | const name = 'Capt. Janeway'; 356 | ``` 357 | 358 | - [6.2](#6.2) linted by eslint 359 | Single-line strings longer than 80 characters should be written across multiple lines using string concatenation. If you are writing a multiline string, use template strings as in [6.4](#6.4). This is a change in the Meteor edition. Note: If overused, long strings with concatenation could impact performance. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40). 360 | 361 | ```javascript 362 | // bad 363 | 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.'; 364 | 365 | // bad 366 | const errorMessage = 'This is a super long error that was thrown because \ 367 | of Batman. When you stop to think about how Batman had anything to do \ 368 | with this, you would get nowhere \ 369 | fast.'; 370 | 371 | // good 372 | const errorMessage = 'This is a super long error that was thrown because ' + 373 | 'of Batman. When you stop to think about how Batman had anything to do ' + 374 | 'with this, you would get nowhere fast.'; 375 | ``` 376 | 377 | - [6.3](#6.3) This non-rule merged as a note into the previous rule in the Meteor edition. 378 | 379 | 380 | - [6.4](#6.4) When programmatically building up strings, use template strings instead of concatenation. 381 | 382 | > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features. 383 | 384 | ```javascript 385 | // bad 386 | function sayHi(name) { 387 | return 'How are you, ' + name + '?'; 388 | } 389 | 390 | // bad 391 | function sayHi(name) { 392 | return ['How are you, ', name, '?'].join(); 393 | } 394 | 395 | // good 396 | function sayHi(name) { 397 | return `How are you, ${name}?`; 398 | } 399 | ``` 400 | 401 | **[⬆ back to top](#table-of-contents)** 402 | 403 | 404 | ## Functions 405 | 406 | - [7.1](#7.1) linted by eslint 407 | Use function declarations instead of function expressions. 408 | 409 | > Why? Function declarations are named, so they're easier to identify in call stacks. Also, the whole body of a function declaration is hoisted, whereas only the reference of a function expression is hoisted. This rule makes it possible to always use [Arrow Functions](#arrow-functions) in place of function expressions. 410 | 411 | ```javascript 412 | // bad 413 | const foo = function () { 414 | }; 415 | 416 | // good 417 | function foo() { 418 | } 419 | ``` 420 | 421 | - [7.2](#7.2) Removed in the Meteor edition. 422 | - [7.3](#7.3) linted by eslint 423 | Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. [7.4](#7.4) **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement. [Read ECMA-262's note on this issue](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). This note merged into the previous rule in the Meteor edition. 424 | 425 | ```javascript 426 | // bad 427 | if (currentUser) { 428 | function test() { 429 | console.log('Nope.'); 430 | } 431 | } 432 | 433 | // good 434 | let test; 435 | if (currentUser) { 436 | test = () => { 437 | console.log('Yup.'); 438 | }; 439 | } 440 | ``` 441 | 442 | - [7.5](#7.5) linted by eslint 443 | Never name a parameter `arguments`. This will take precedence over the `arguments` object that is given to every function scope. 444 | 445 | ```javascript 446 | // bad 447 | function nope(name, options, arguments) { 448 | // ...stuff... 449 | } 450 | 451 | // good 452 | function yup(name, options, args) { 453 | // ...stuff... 454 | } 455 | ``` 456 | 457 | 458 | - [7.6](#7.6) Never use `arguments`, opt to use rest syntax `...` instead. 459 | 460 | > Why? `...` is explicit about which arguments you want pulled. Plus rest arguments are a real Array and not Array-like like `arguments`. 461 | 462 | ```javascript 463 | // bad 464 | function concatenateAll() { 465 | const args = Array.prototype.slice.call(arguments); 466 | return args.join(''); 467 | } 468 | 469 | // good 470 | function concatenateAll(...args) { 471 | return args.join(''); 472 | } 473 | ``` 474 | 475 | 476 | - [7.7](#7.7) Use default parameter syntax rather than mutating function arguments. 477 | 478 | ```javascript 479 | // really bad 480 | function handleThings(opts) { 481 | // No! We shouldn't mutate function arguments. 482 | // Double bad: if opts is falsy it'll be set to an object which may 483 | // be what you want but it can introduce subtle bugs. 484 | opts = opts || {}; 485 | // ... 486 | } 487 | 488 | // still bad 489 | function handleThings(opts) { 490 | if (opts === void 0) { 491 | opts = {}; 492 | } 493 | // ... 494 | } 495 | 496 | // good 497 | function handleThings(opts = {}) { 498 | // ... 499 | } 500 | ``` 501 | 502 | - [7.8](#7.8) Avoid side effects with default parameters 503 | 504 | > Why? They are confusing to reason about. 505 | 506 | ```javascript 507 | var b = 1; 508 | // bad 509 | function count(a = b++) { 510 | console.log(a); 511 | } 512 | count(); // 1 513 | count(); // 2 514 | count(3); // 3 515 | count(); // 3 516 | ``` 517 | 518 | - [7.9](#7.9) Use argument spreads to interpolate 519 | arguments in function calls. 520 | 521 | ```javascript 522 | const prefix = [a, b]; 523 | const suffix = [c, d, e]; 524 | 525 | // bad 526 | prefix.push.apply(prefix, suffix); 527 | 528 | // good 529 | prefix.push(...suffix); 530 | 531 | // bad 532 | someFunction.apply(null, prefix.concat(suffix)); 533 | 534 | // good 535 | someFunction(...prefix, ...suffix); 536 | ``` 537 | 538 | The exception to this advice is when you really need to use a 539 | different, non-`null` value of `this`. Then `.apply` (or `.call`) is 540 | probably a better option. 541 | 542 | The same goes for `new` expressions. 543 | 544 | ```javascript 545 | class A { 546 | constructor(...args) { ... } 547 | } 548 | 549 | // really bad 550 | const instance = Object.create(A.prototype); 551 | A.prototype.constructor.apply(instance, prefix.concat(suffix)); 552 | 553 | // good 554 | const instance = new A(...prefix, ...suffix); 555 | ``` 556 | 557 | **[⬆ back to top](#table-of-contents)** 558 | 559 | ## Arrow Functions 560 | 561 | - [8.1](#8.1) When you must use function expressions (as when passing an anonymous function), use arrow function notation. 562 | 563 | > Why? It creates a version of the function that executes in the context of `this`, which is usually what you want, and is a more concise syntax. 564 | 565 | > Why not? If you have a fairly complicated function, you might move that logic out into its own function declaration. 566 | 567 | ```javascript 568 | // bad 569 | [1, 2, 3].map(function (x) { 570 | return x * x; 571 | }); 572 | 573 | // good 574 | [1, 2, 3].map((x) => { 575 | return x * x; 576 | }); 577 | ``` 578 | 579 | - [8.2](#8.2) If the function body fits on one line and there is only a single argument, feel free to omit the braces and parentheses, and use the implicit return. Otherwise, add the parentheses, braces, and use a `return` statement. 580 | 581 | > Why? Syntactic sugar. It reads well when multiple functions are chained together. 582 | 583 | > Why not? If you plan on returning an object. 584 | 585 | ```javascript 586 | // good 587 | [1, 2, 3].map(x => x * x); 588 | 589 | // good 590 | [1, 2, 3].reduce((total, n) => { 591 | return total + n; 592 | }, 0); 593 | ``` 594 | 595 | **[⬆ back to top](#table-of-contents)** 596 | 597 | 598 | ## Constructors 599 | 600 | - [9.1](#9.1) Always use `class`. Avoid manipulating `prototype` directly. 601 | 602 | > Why? `class` syntax is more concise and easier to reason about. 603 | 604 | ```javascript 605 | // bad 606 | function Queue(contents = []) { 607 | this._queue = [...contents]; 608 | } 609 | Queue.prototype.pop = function() { 610 | const value = this._queue[0]; 611 | this._queue.splice(0, 1); 612 | return value; 613 | } 614 | 615 | 616 | // good 617 | class Queue { 618 | constructor(contents = []) { 619 | this._queue = [...contents]; 620 | } 621 | pop() { 622 | const value = this._queue[0]; 623 | this._queue.splice(0, 1); 624 | return value; 625 | } 626 | } 627 | ``` 628 | 629 | - [9.2](#9.2) Use `extends` for inheritance. 630 | 631 | > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`. 632 | 633 | ```javascript 634 | // bad 635 | const inherits = require('inherits'); 636 | function PeekableQueue(contents) { 637 | Queue.apply(this, contents); 638 | } 639 | inherits(PeekableQueue, Queue); 640 | PeekableQueue.prototype.peek = function() { 641 | return this._queue[0]; 642 | } 643 | 644 | // good 645 | class PeekableQueue extends Queue { 646 | peek() { 647 | return this._queue[0]; 648 | } 649 | } 650 | ``` 651 | 652 | - [9.3](#9.3) Methods can return `this` to help with method chaining. 653 | 654 | ```javascript 655 | // bad 656 | Jedi.prototype.jump = function() { 657 | this.jumping = true; 658 | return true; 659 | }; 660 | 661 | Jedi.prototype.setHeight = function(height) { 662 | this.height = height; 663 | }; 664 | 665 | const luke = new Jedi(); 666 | luke.jump(); // => true 667 | luke.setHeight(20); // => undefined 668 | 669 | // good 670 | class Jedi { 671 | jump() { 672 | this.jumping = true; 673 | return this; 674 | } 675 | 676 | setHeight(height) { 677 | this.height = height; 678 | return this; 679 | } 680 | } 681 | 682 | const luke = new Jedi(); 683 | 684 | luke.jump() 685 | .setHeight(20); 686 | ``` 687 | 688 | 689 | - [9.4](#9.4) It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects. 690 | 691 | ```javascript 692 | class Jedi { 693 | contructor(options = {}) { 694 | this.name = options.name || 'no name'; 695 | } 696 | 697 | getName() { 698 | return this.name; 699 | } 700 | 701 | toString() { 702 | return `Jedi - ${this.getName()}`; 703 | } 704 | } 705 | ``` 706 | 707 | **[⬆ back to top](#table-of-contents)** 708 | 709 | 710 | ## Modules 711 | 712 | - [10.1](#10.1) Always use modules (`import`/`export`) over a non-standard module system. You can always transpile to your preferred module system. 713 | 714 | > Why? Modules are the future, let's start using the future now. 715 | 716 | ```javascript 717 | // bad 718 | const AirbnbStyleGuide = require('./AirbnbStyleGuide'); 719 | module.exports = AirbnbStyleGuide.es6; 720 | 721 | // ok 722 | import AirbnbStyleGuide from './AirbnbStyleGuide'; 723 | export default AirbnbStyleGuide.es6; 724 | 725 | // best 726 | import { es6 } from './AirbnbStyleGuide'; 727 | export default es6; 728 | ``` 729 | 730 | - [10.2](#10.2) Do not use wildcard imports. 731 | 732 | > Why? This makes sure you have a single default export. 733 | 734 | ```javascript 735 | // bad 736 | import * as AirbnbStyleGuide from './AirbnbStyleGuide'; 737 | 738 | // good 739 | import AirbnbStyleGuide from './AirbnbStyleGuide'; 740 | ``` 741 | 742 | - [10.3](#10.3) And do not export directly from an import. 743 | 744 | > Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent. 745 | 746 | ```javascript 747 | // bad 748 | // filename es6.js 749 | export { es6 as default } from './airbnbStyleGuide'; 750 | 751 | // good 752 | // filename es6.js 753 | import { es6 } from './AirbnbStyleGuide'; 754 | export default es6; 755 | ``` 756 | 757 | **[⬆ back to top](#table-of-contents)** 758 | 759 | ## Iterators and Generators 760 | 761 | - [11.1](#11.1) Don't use iterators. Prefer JavaScript's higher-order functions like `map()` and `reduce()` instead of loops like `for-of`. 762 | 763 | > Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side-effects. 764 | 765 | ```javascript 766 | const numbers = [1, 2, 3, 4, 5]; 767 | 768 | // bad 769 | let sum = 0; 770 | for (let num of numbers) { 771 | sum += num; 772 | } 773 | 774 | sum === 15; 775 | 776 | // good 777 | let sum = 0; 778 | numbers.forEach((num) => sum += num); 779 | sum === 15; 780 | 781 | // best (use the functional force) 782 | const sum = numbers.reduce((total, num) => total + num, 0); 783 | sum === 15; 784 | ``` 785 | 786 | - [11.2](#11.2) Don't use generators for now. 787 | 788 | > Why? They don't transpile well to ES5. 789 | 790 | **[⬆ back to top](#table-of-contents)** 791 | 792 | 793 | ## Properties 794 | 795 | - [12.1](#12.1) Use dot notation when accessing properties. 796 | 797 | ```javascript 798 | const luke = { 799 | jedi: true, 800 | age: 28, 801 | }; 802 | 803 | // bad 804 | const isJedi = luke['jedi']; 805 | 806 | // good 807 | const isJedi = luke.jedi; 808 | ``` 809 | 810 | - [12.2](#12.2) Use subscript notation `[]` when accessing properties with a variable. 811 | 812 | ```javascript 813 | const luke = { 814 | jedi: true, 815 | age: 28, 816 | }; 817 | 818 | function getProp(prop) { 819 | return luke[prop]; 820 | } 821 | 822 | const isJedi = getProp('jedi'); 823 | ``` 824 | 825 | **[⬆ back to top](#table-of-contents)** 826 | 827 | 828 | ## Variables 829 | 830 | - [13.1](#13.1) Always use `const` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. 831 | 832 | ```javascript 833 | // bad 834 | superPower = new SuperPower(); 835 | 836 | // good 837 | const superPower = new SuperPower(); 838 | ``` 839 | 840 | - [13.2](#13.2) Use one `const` declaration per variable. 841 | 842 | > Why? It's easier to add new variable declarations this way, and you never have to worry about swapping out a `;` for a `,` or introducing punctuation-only diffs. 843 | 844 | ```javascript 845 | // bad 846 | const items = getItems(), 847 | goSportsTeam = true, 848 | dragonball = 'z'; 849 | 850 | // bad 851 | // (compare to above, and try to spot the mistake) 852 | const items = getItems(), 853 | goSportsTeam = true; 854 | dragonball = 'z'; 855 | 856 | // good 857 | const items = getItems(); 858 | const goSportsTeam = true; 859 | const dragonball = 'z'; 860 | ``` 861 | 862 | - [13.3](#13.3) Group all your `const`s and then group all your `let`s. 863 | 864 | > Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. 865 | 866 | ```javascript 867 | // bad 868 | let i, len, dragonball, 869 | items = getItems(), 870 | goSportsTeam = true; 871 | 872 | // bad 873 | let i; 874 | const items = getItems(); 875 | let dragonball; 876 | const goSportsTeam = true; 877 | let len; 878 | 879 | // good 880 | const goSportsTeam = true; 881 | const items = getItems(); 882 | let dragonball; 883 | let i; 884 | let length; 885 | ``` 886 | 887 | - [13.4](#13.4) Assign variables where you need them, but place them in a reasonable place. 888 | 889 | > Why? `let` and `const` are block scoped and not function scoped. 890 | 891 | ```javascript 892 | // good 893 | function() { 894 | test(); 895 | console.log('doing stuff..'); 896 | 897 | //..other stuff.. 898 | 899 | const name = getName(); 900 | 901 | if (name === 'test') { 902 | return false; 903 | } 904 | 905 | return name; 906 | } 907 | 908 | // bad - unnessary function call 909 | function(hasName) { 910 | const name = getName(); 911 | 912 | if (!hasName) { 913 | return false; 914 | } 915 | 916 | this.setFirstName(name); 917 | 918 | return true; 919 | } 920 | 921 | // good 922 | function(hasName) { 923 | if (!hasName) { 924 | return false; 925 | } 926 | 927 | const name = getName(); 928 | this.setFirstName(name); 929 | 930 | return true; 931 | } 932 | ``` 933 | 934 | **[⬆ back to top](#table-of-contents)** 935 | 936 | 937 | ## Hoisting 938 | 939 | - [14.1](#14.1) [14.2](#14.2) [14.3](#14.3) [14.4](#14.4) Non-rules removed in the Meteor edition. 940 | 941 | 942 | - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaS cript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). 943 | 944 | **[⬆ back to top](#table-of-contents)** 945 | 946 | 947 | ## Comparison Operators & Equality 948 | 949 | - [15.1](#15.1) Use `===` and `!==` over `==` and `!=`. 950 | - [15.2](#15.2)/[15.3](#15.3)/[15.4](#15.4) 951 | Use shortcuts. Note: Conditional statements such as the `if` statement evaluate their expression using coercion with the `ToBoolean` abstract method and always follow the simple rules below. For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. (15.2, 15.3, and 15.4 merged in Meteor edition): 952 | 953 | + **Objects** evaluate to **true** 954 | + **Undefined** evaluates to **false** 955 | + **Null** evaluates to **false** 956 | + **Booleans** evaluate to **the value of the boolean** 957 | + **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true** 958 | + **Strings** evaluate to **false** if an empty string `''`, otherwise **true** 959 | 960 | ```javascript 961 | // bad 962 | if (name !== '') { 963 | // ...stuff... 964 | } 965 | 966 | // good 967 | if (name) { 968 | // ...stuff... 969 | } 970 | 971 | // bad 972 | if (collection.length > 0) { 973 | // ...stuff... 974 | } 975 | 976 | // good 977 | if (collection.length) { 978 | // ...stuff... 979 | } 980 | 981 | // note 982 | if ([0]) { 983 | // true 984 | // An array is an object, objects evaluate to true 985 | } 986 | ``` 987 | 988 | **[⬆ back to top](#table-of-contents)** 989 | 990 | 991 | ## Blocks 992 | 993 | - [16.1](#16.1) linted by eslint 994 | Use braces with all multi-line blocks. 995 | 996 | ```javascript 997 | // bad 998 | if (test) 999 | return false; 1000 | 1001 | // good 1002 | if (test) return false; 1003 | 1004 | // good 1005 | if (test) { 1006 | return false; 1007 | } 1008 | 1009 | // bad 1010 | function() { return false; } 1011 | 1012 | // good 1013 | function() { 1014 | return false; 1015 | } 1016 | ``` 1017 | 1018 | - [16.2](#16.2) linted by eslint 1019 | If you're using multi-line blocks with `if` and `else`, put `else` on the same line as your 1020 | `if` block's closing brace. 1021 | 1022 | ```javascript 1023 | // bad 1024 | if (test) { 1025 | thing1(); 1026 | thing2(); 1027 | } 1028 | else { 1029 | thing3(); 1030 | } 1031 | 1032 | // good 1033 | if (test) { 1034 | thing1(); 1035 | thing2(); 1036 | } else { 1037 | thing3(); 1038 | } 1039 | ``` 1040 | 1041 | 1042 | **[⬆ back to top](#table-of-contents)** 1043 | 1044 | 1045 | ## Comments 1046 | 1047 | - [17.1](#17.1) Use `/** ... */` for method doc comments. Include a description, specify types and values for all parameters and return values. In the Meteor edition, this rule has been updated to only refer to doc comments. 1048 | 1049 | ```javascript 1050 | // bad 1051 | // make() returns a new element 1052 | // based on the passed in tag name 1053 | // 1054 | // @param {String} tag 1055 | // @return {Element} element 1056 | function make(tag) { 1057 | 1058 | // ...stuff... 1059 | 1060 | return element; 1061 | } 1062 | 1063 | // good 1064 | /** 1065 | * make() returns a new element 1066 | * based on the passed in tag name 1067 | * 1068 | * @param {String} tag 1069 | * @return {Element} element 1070 | */ 1071 | function make(tag) { 1072 | 1073 | // ...stuff... 1074 | 1075 | return element; 1076 | } 1077 | ``` 1078 | 1079 | - [17.2](#17.2) Use `//` for all other comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment. Has been updated in Meteor edition to refer to all non-doc comments. 1080 | 1081 | ```javascript 1082 | // bad 1083 | const active = true; // is current tab 1084 | 1085 | // good 1086 | // is current tab 1087 | const active = true; 1088 | 1089 | // bad 1090 | function getType() { 1091 | console.log('fetching type...'); 1092 | // set the default type to 'no type' 1093 | const type = this._type || 'no type'; 1094 | 1095 | return type; 1096 | } 1097 | 1098 | // good 1099 | function getType() { 1100 | console.log('fetching type...'); 1101 | 1102 | // set the default type to 'no type' 1103 | const type = this._type || 'no type'; 1104 | 1105 | return type; 1106 | } 1107 | ``` 1108 | 1109 | - [17.3](#17.3) Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME -- need to figure this out` or `TODO -- need to implement`. 1110 | 1111 | - [17.4](#17.4) Use `// FIXME:` to annotate problems. 1112 | 1113 | ```javascript 1114 | class Calculator { 1115 | constructor() { 1116 | // FIXME: shouldn't use a global here 1117 | total = 0; 1118 | } 1119 | } 1120 | ``` 1121 | 1122 | - [17.5](#17.5) Use `// TODO:` to annotate solutions to problems. 1123 | 1124 | ```javascript 1125 | class Calculator { 1126 | constructor() { 1127 | // TODO: total should be configurable by an options param 1128 | this.total = 0; 1129 | } 1130 | } 1131 | ``` 1132 | 1133 | **[⬆ back to top](#table-of-contents)** 1134 | 1135 | 1136 | ## Whitespace 1137 | 1138 | - [18.1](#18.1) linted by eslint 1139 | Use soft tabs set to 2 spaces. 1140 | 1141 | ```javascript 1142 | // bad 1143 | function() { 1144 | ∙∙∙∙const name; 1145 | } 1146 | 1147 | // bad 1148 | function() { 1149 | ∙const name; 1150 | } 1151 | 1152 | // good 1153 | function() { 1154 | ∙∙const name; 1155 | } 1156 | ``` 1157 | 1158 | - [18.2](#18.2) linted by eslint 1159 | Place 1 space before the leading brace. 1160 | 1161 | ```javascript 1162 | // bad 1163 | function test(){ 1164 | console.log('test'); 1165 | } 1166 | 1167 | // good 1168 | function test() { 1169 | console.log('test'); 1170 | } 1171 | 1172 | // bad 1173 | dog.set('attr',{ 1174 | age: '1 year', 1175 | breed: 'Bernese Mountain Dog', 1176 | }); 1177 | 1178 | // good 1179 | dog.set('attr', { 1180 | age: '1 year', 1181 | breed: 'Bernese Mountain Dog', 1182 | }); 1183 | ``` 1184 | 1185 | - [18.3](#18.3) linted by eslint 1186 | Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space before the argument list in function calls and declarations. 1187 | 1188 | ```javascript 1189 | // bad 1190 | if(isJedi) { 1191 | fight (); 1192 | } 1193 | 1194 | // good 1195 | if (isJedi) { 1196 | fight(); 1197 | } 1198 | 1199 | // bad 1200 | function fight () { 1201 | console.log ('Swooosh!'); 1202 | } 1203 | 1204 | // good 1205 | function fight() { 1206 | console.log('Swooosh!'); 1207 | } 1208 | ``` 1209 | 1210 | - [18.4](#18.4) linted by eslint 1211 | Set off operators with spaces. 1212 | 1213 | ```javascript 1214 | // bad 1215 | const x=y+5; 1216 | 1217 | // good 1218 | const x = y + 5; 1219 | ``` 1220 | 1221 | - [18.5](#18.5) End files with a single newline character. 1222 | 1223 | ```javascript 1224 | // bad 1225 | (function(global) { 1226 | // ...stuff... 1227 | })(this); 1228 | ``` 1229 | 1230 | ```javascript 1231 | // bad 1232 | (function(global) { 1233 | // ...stuff... 1234 | })(this);↵ 1235 | ↵ 1236 | ``` 1237 | 1238 | ```javascript 1239 | // good 1240 | (function(global) { 1241 | // ...stuff... 1242 | })(this);↵ 1243 | ``` 1244 | 1245 | - [18.5](#18.5) Use indentation when making long method chains. Use a leading dot, which 1246 | emphasizes that the line is a method call, not a new statement. 1247 | 1248 | ```javascript 1249 | // bad 1250 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 1251 | 1252 | // bad 1253 | $('#items'). 1254 | find('.selected'). 1255 | highlight(). 1256 | end(). 1257 | find('.open'). 1258 | updateCount(); 1259 | 1260 | // good 1261 | $('#items') 1262 | .find('.selected') 1263 | .highlight() 1264 | .end() 1265 | .find('.open') 1266 | .updateCount(); 1267 | 1268 | // bad 1269 | const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) 1270 | .attr('width', (radius + margin) * 2).append('svg:g') 1271 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 1272 | .call(tron.led); 1273 | 1274 | // good 1275 | const leds = stage.selectAll('.led') 1276 | .data(data) 1277 | .enter().append('svg:svg') 1278 | .classed('led', true) 1279 | .attr('width', (radius + margin) * 2) 1280 | .append('svg:g') 1281 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 1282 | .call(tron.led); 1283 | ``` 1284 | 1285 | - [18.6](#18.6) Leave a blank line after blocks and before the next statement. 1286 | 1287 | ```javascript 1288 | // bad 1289 | if (foo) { 1290 | return bar; 1291 | } 1292 | return baz; 1293 | 1294 | // good 1295 | if (foo) { 1296 | return bar; 1297 | } 1298 | 1299 | return baz; 1300 | 1301 | // bad 1302 | const obj = { 1303 | foo() { 1304 | }, 1305 | bar() { 1306 | }, 1307 | }; 1308 | return obj; 1309 | 1310 | // good 1311 | const obj = { 1312 | foo() { 1313 | }, 1314 | 1315 | bar() { 1316 | }, 1317 | }; 1318 | 1319 | return obj; 1320 | ``` 1321 | 1322 | 1323 | **[⬆ back to top](#table-of-contents)** 1324 | 1325 | ## Commas 1326 | 1327 | - [19.1](#19.1) linted by eslint 1328 | Leading commas: **Nope.** 1329 | 1330 | ```javascript 1331 | // bad 1332 | const story = [ 1333 | once 1334 | , upon 1335 | , aTime 1336 | ]; 1337 | 1338 | // good 1339 | const story = [ 1340 | once, 1341 | upon, 1342 | aTime, 1343 | ]; 1344 | 1345 | // bad 1346 | const hero = { 1347 | firstName: 'Ada' 1348 | , lastName: 'Lovelace' 1349 | , birthYear: 1815 1350 | , superPower: 'computers' 1351 | }; 1352 | 1353 | // good 1354 | const hero = { 1355 | firstName: 'Ada', 1356 | lastName: 'Lovelace', 1357 | birthYear: 1815, 1358 | superPower: 'computers', 1359 | }; 1360 | ``` 1361 | 1362 | - [19.2](#19.2) linted by eslint 1363 | Additional trailing comma: **Yup.** 1364 | 1365 | > Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don't have to worry about the [trailing comma problem](es5/README.md#commas) in legacy browsers. 1366 | 1367 | ```javascript 1368 | // bad - git diff without trailing comma 1369 | const hero = { 1370 | firstName: 'Florence', 1371 | - lastName: 'Nightingale' 1372 | + lastName: 'Nightingale', 1373 | + inventorOf: ['coxcomb graph', 'mordern nursing'] 1374 | } 1375 | 1376 | // good - git diff with trailing comma 1377 | const hero = { 1378 | firstName: 'Florence', 1379 | lastName: 'Nightingale', 1380 | + inventorOf: ['coxcomb chart', 'mordern nursing'], 1381 | } 1382 | 1383 | // bad 1384 | const hero = { 1385 | firstName: 'Dana', 1386 | lastName: 'Scully' 1387 | }; 1388 | 1389 | const heroes = [ 1390 | 'Batman', 1391 | 'Superman' 1392 | ]; 1393 | 1394 | // good 1395 | const hero = { 1396 | firstName: 'Dana', 1397 | lastName: 'Scully', 1398 | }; 1399 | 1400 | const heroes = [ 1401 | 'Batman', 1402 | 'Superman', 1403 | ]; 1404 | ``` 1405 | 1406 | **[⬆ back to top](#table-of-contents)** 1407 | 1408 | 1409 | ## Semicolons 1410 | 1411 | - [20.1](#20.1) linted by eslint 1412 | **Yup.** 1413 | 1414 | ```javascript 1415 | // bad 1416 | (function() { 1417 | const name = 'Skywalker' 1418 | return name 1419 | })() 1420 | 1421 | // good 1422 | (() => { 1423 | const name = 'Skywalker'; 1424 | return name; 1425 | })(); 1426 | 1427 | // good (guards against the function becoming an argument when two files with IIFEs are concatenated) 1428 | ;(() => { 1429 | const name = 'Skywalker'; 1430 | return name; 1431 | })(); 1432 | ``` 1433 | 1434 | [Read more](http://stackoverflow.com/a/7365214/1712802). 1435 | 1436 | **[⬆ back to top](#table-of-contents)** 1437 | 1438 | 1439 | ## Type Casting & Coercion 1440 | 1441 | - [21.1](#21.1) Perform type coercion at the beginning of the statement. 1442 | - [21.2](#21.2) Strings: 1443 | 1444 | ```javascript 1445 | // => this.reviewScore = 9; 1446 | 1447 | // bad 1448 | const totalScore = this.reviewScore + ''; 1449 | 1450 | // good 1451 | const totalScore = String(this.reviewScore); 1452 | ``` 1453 | 1454 | - [21.3](#21.3) linted by eslint 1455 | Use `parseInt` to convert strings to numbers, and always with a radix argument. 1456 | 1457 | ```javascript 1458 | const inputValue = '4'; 1459 | 1460 | // bad 1461 | const val = new Number(inputValue); 1462 | 1463 | // bad 1464 | const val = inputValue >> 0; 1465 | 1466 | // bad 1467 | const val = parseInt(inputValue); 1468 | 1469 | // good 1470 | const val = parseInt(inputValue, 10); 1471 | ``` 1472 | 1473 | Use the `Number` constructor (without `new`) to coerce values that are 1474 | not strings to numbers, as in `Number(new Date) + 1000`. The unary `+` 1475 | operator is an acceptable shorthand for `Number(...)`, but only if the 1476 | expression is not involved in a larger expression, as in `+new Date`. 1477 | 1478 | Note that the `Number` constructor will return `NaN` if the value 1479 | cannot be converted to a number, including when the value is 1480 | undefined. If the output might be `NaN` (likely because the input 1481 | might be undefined), be sure to test for that possibility after 1482 | attempting the conversion: 1483 | 1484 | ```javascript 1485 | let val = Number(inputValue); 1486 | if (isNaN(val)) { 1487 | val = 0; 1488 | } 1489 | ``` 1490 | 1491 | Better yet, avoid the possibility of `NaN` through other means, such 1492 | as providing default values for optional function parameters. 1493 | 1494 | - [21.4](#21.4) If you have a very good reason for 1495 | using some other kind of coercion technique, be it for performance or 1496 | because you need a very specific output behavior, then you should 1497 | absolutely leave a comment justifying your choice. 1498 | 1499 | ```javascript 1500 | // good 1501 | /** 1502 | * parseInt was the reason my code was slow. 1503 | * Bitshifting the String to coerce it to a 1504 | * Number made it a lot faster. 1505 | */ 1506 | const val = inputValue >> 0; 1507 | 1508 | // good 1509 | // Truthy inputs must always be coerced to 1, and falsy inputs must 1510 | // always be cocerced to 0. 1511 | const zeroOrOne = inputValue ? 1 : 0; 1512 | ``` 1513 | 1514 | - [21.5](#21.5) **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](http://es5.github.io/#x4.3.19), but Bitshift operations always return a 32-bit integer ([source](http://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647: 1515 | 1516 | ```javascript 1517 | 2147483647 >> 0 //=> 2147483647 1518 | 2147483648 >> 0 //=> -2147483648 1519 | 2147483649 >> 0 //=> -2147483647 1520 | ``` 1521 | 1522 | - [21.6](#21.6) Booleans: 1523 | 1524 | ```javascript 1525 | const age = 0; 1526 | 1527 | // bad 1528 | const hasAge = new Boolean(age); 1529 | 1530 | // good 1531 | const hasAge = Boolean(age); 1532 | 1533 | // good 1534 | const hasAge = !!age; 1535 | ``` 1536 | 1537 | **[⬆ back to top](#table-of-contents)** 1538 | 1539 | 1540 | ## Naming Conventions 1541 | 1542 | - [22.1](#22.1) Avoid single letter names. Be descriptive with your naming. 1543 | 1544 | ```javascript 1545 | // bad 1546 | function q() { 1547 | // ...stuff... 1548 | } 1549 | 1550 | // good 1551 | function query() { 1552 | // ..stuff.. 1553 | } 1554 | ``` 1555 | 1556 | - [22.2](#22.2) linted by eslint 1557 | Use camelCase when naming objects, functions, and instances. 1558 | 1559 | ```javascript 1560 | // bad 1561 | const OBJEcttsssss = {}; 1562 | const this_is_my_object = {}; 1563 | function c() {} 1564 | 1565 | // good 1566 | const thisIsMyObject = {}; 1567 | function thisIsMyFunction() {} 1568 | ``` 1569 | 1570 | - [22.3](#22.3) Use PascalCase when naming constructors or classes. 1571 | 1572 | ```javascript 1573 | // bad 1574 | function user(options) { 1575 | this.name = options.name; 1576 | } 1577 | 1578 | const bad = new user({ 1579 | name: 'nope', 1580 | }); 1581 | 1582 | // good 1583 | class User { 1584 | constructor(options) { 1585 | this.name = options.name; 1586 | } 1587 | } 1588 | 1589 | const good = new User({ 1590 | name: 'yup', 1591 | }); 1592 | ``` 1593 | 1594 | - [22.4](#22.4) Use a leading underscore `_` when naming private properties. 1595 | 1596 | ```javascript 1597 | // bad 1598 | this.__firstName__ = 'Panda'; 1599 | this.firstName_ = 'Panda'; 1600 | 1601 | // good 1602 | this._firstName = 'Panda'; 1603 | ``` 1604 | 1605 | - [22.5](#22.5) Don't save references to `this`. Use arrow functions or Function#bind. 1606 | 1607 | ```javascript 1608 | // bad 1609 | function foo() { 1610 | const self = this; 1611 | return function() { 1612 | console.log(self); 1613 | }; 1614 | } 1615 | 1616 | // bad 1617 | function foo() { 1618 | const that = this; 1619 | return function() { 1620 | console.log(that); 1621 | }; 1622 | } 1623 | 1624 | // good 1625 | function foo() { 1626 | return () => { 1627 | console.log(this); 1628 | }; 1629 | } 1630 | ``` 1631 | 1632 | - [22.6](#22.6) If your file exports a single class, your filename should be exactly the name of the class. 1633 | ```javascript 1634 | // file contents 1635 | export default class CheckBox { 1636 | // ... 1637 | } 1638 | 1639 | // in some other file 1640 | // bad 1641 | import CheckBox from './checkBox'; 1642 | 1643 | // bad 1644 | import CheckBox from './check_box'; 1645 | 1646 | // good 1647 | import CheckBox from './CheckBox'; 1648 | ``` 1649 | 1650 | - [22.7](#22.7) Use camelCase when you export-default a function. Your filename should be identical to your function's name. 1651 | 1652 | ```javascript 1653 | function makeStyleGuide() { 1654 | } 1655 | 1656 | export default makeStyleGuide; 1657 | ``` 1658 | 1659 | - [22.8](#22.8) Use PascalCase when you export a singleton / function library / bare object. 1660 | 1661 | ```javascript 1662 | const AirbnbStyleGuide = { 1663 | es6: { 1664 | } 1665 | }; 1666 | 1667 | export default AirbnbStyleGuide; 1668 | ``` 1669 | 1670 | - [22.9](#22.9) Prefer `export`ing declarations 1671 | where they are declared, rather than at the end of the file: 1672 | 1673 | ```javascript 1674 | // bad 1675 | function createUser(name) { ... } 1676 | function getOrCreateUser(name) { ... } 1677 | // ... rest of file ... 1678 | export { 1679 | createUser, 1680 | getOrCreateUser, 1681 | } 1682 | 1683 | // good 1684 | export function createUser(name) { ... } 1685 | export function getOrCreateUser(name) { ... } 1686 | // ... rest of file ... 1687 | ``` 1688 | 1689 | This style ensures that the set of `export`s remains up-to-date as 1690 | declarations are added or removed. 1691 | 1692 | **[⬆ back to top](#table-of-contents)** 1693 | 1694 | 1695 | ## Accessors 1696 | 1697 | - [23.1](#23.1) Accessor functions for properties are not required. 1698 | - [23.2](#23.2) If you do make accessor functions use getVal() and setVal('hello'). 1699 | 1700 | ```javascript 1701 | // bad 1702 | dragon.age(); 1703 | 1704 | // good 1705 | dragon.getAge(); 1706 | 1707 | // bad 1708 | dragon.age(25); 1709 | 1710 | // good 1711 | dragon.setAge(25); 1712 | ``` 1713 | 1714 | - [23.3](#23.3) If the property is a boolean, use isVal() or hasVal(). 1715 | 1716 | ```javascript 1717 | // bad 1718 | if (!dragon.age()) { 1719 | return false; 1720 | } 1721 | 1722 | // good 1723 | if (!dragon.hasAge()) { 1724 | return false; 1725 | } 1726 | ``` 1727 | 1728 | - [23.4](#23.4) It's okay to create get() and set() functions, but be consistent. 1729 | 1730 | ```javascript 1731 | class Jedi { 1732 | constructor(options = {}) { 1733 | const lightsaber = options.lightsaber || 'blue'; 1734 | this.set('lightsaber', lightsaber); 1735 | } 1736 | 1737 | set(key, val) { 1738 | this[key] = val; 1739 | } 1740 | 1741 | get(key) { 1742 | return this[key]; 1743 | } 1744 | } 1745 | ``` 1746 | 1747 | - [23.5](#23.5) Strongly prefer accessor methods to 1748 | defining ES5 getter and setter properties: 1749 | 1750 | ```javascript 1751 | class Jedi { 1752 | constructor({ lightsaber = blue }) { 1753 | this._lightsaber = lightsaber; 1754 | } 1755 | 1756 | // bad; use a normal getter method instead 1757 | get lightsaber() { 1758 | return this._lightsaber; 1759 | } 1760 | 1761 | // bad; use a normal setter method instead 1762 | set lightsaber(newLightSaber) { 1763 | return this._lightsaber = newLightSaber; 1764 | } 1765 | } 1766 | ``` 1767 | 1768 | Not only is this style rarely more readable than the method 1769 | equivalent, performance can suffer dramatically when code relies on 1770 | specially-defined properties rather than just using normal properties. 1771 | 1772 | **[⬆ back to top](#table-of-contents)** 1773 | 1774 | 1775 | ## Events 1776 | 1777 | - [24.1](#24.1) When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of: 1778 | 1779 | ```javascript 1780 | // bad 1781 | $(this).trigger('listingUpdated', listing.id); 1782 | 1783 | ... 1784 | 1785 | $(this).on('listingUpdated', function(e, listingId) { 1786 | // do something with listingId 1787 | }); 1788 | ``` 1789 | 1790 | prefer: 1791 | 1792 | ```javascript 1793 | // good 1794 | $(this).trigger('listingUpdated', { listingId : listing.id }); 1795 | 1796 | ... 1797 | 1798 | $(this).on('listingUpdated', function(e, data) { 1799 | // do something with data.listingId 1800 | }); 1801 | ``` 1802 | 1803 | **[⬆ back to top](#table-of-contents)** 1804 | 1805 | 1806 | ## jQuery 1807 | 1808 | - [25.1](#25.1) Prefix jQuery object variables with a `$`. 1809 | 1810 | ```javascript 1811 | // bad 1812 | const sidebar = $('.sidebar'); 1813 | 1814 | // good 1815 | const $sidebar = $('.sidebar'); 1816 | ``` 1817 | 1818 | - [25.2](#25.2) Cache jQuery lookups. 1819 | 1820 | ```javascript 1821 | // bad 1822 | function setSidebar() { 1823 | $('.sidebar').hide(); 1824 | 1825 | // ...stuff... 1826 | 1827 | $('.sidebar').css({ 1828 | 'background-color': 'pink' 1829 | }); 1830 | } 1831 | 1832 | // good 1833 | function setSidebar() { 1834 | const $sidebar = $('.sidebar'); 1835 | $sidebar.hide(); 1836 | 1837 | // ...stuff... 1838 | 1839 | $sidebar.css({ 1840 | 'background-color': 'pink' 1841 | }); 1842 | } 1843 | ``` 1844 | 1845 | - [25.3](#25.3) For DOM queries use Cascading `$('.sidebar ul')` or parent > child `$('.sidebar > ul')`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) 1846 | - [25.4](#25.4) Use `find` with scoped jQuery object queries. 1847 | 1848 | ```javascript 1849 | // bad 1850 | $('ul', '.sidebar').hide(); 1851 | 1852 | // bad 1853 | $('.sidebar').find('ul').hide(); 1854 | 1855 | // good 1856 | $('.sidebar ul').hide(); 1857 | 1858 | // good 1859 | $('.sidebar > ul').hide(); 1860 | 1861 | // good 1862 | $sidebar.find('ul').hide(); 1863 | ``` 1864 | 1865 | **[⬆ back to top](#table-of-contents)** 1866 | 1867 | 1868 | ## ECMAScript 5 Compatibility 1869 | 1870 | - [26.1](#26.1) Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/). 1871 | 1872 | **[⬆ back to top](#table-of-contents)** 1873 | 1874 | ## ECMAScript 6 Styles 1875 | 1876 | - [27.1](#27.1) This is a collection of links to the various es6 features. 1877 | 1878 | 1. [Arrow Functions](#arrow-functions) 1879 | 1. [Classes](#constructors) 1880 | 1. [Object Shorthand](#es6-object-shorthand) 1881 | 1. [Object Concise](#es6-object-concise) 1882 | 1. [Object Computed Properties](#es6-computed-properties) 1883 | 1. [Template Strings](#es6-template-literals) 1884 | 1. [Destructuring](#destructuring) 1885 | 1. [Default Parameters](#es6-default-parameters) 1886 | 1. [Rest](#es6-rest) 1887 | 1. [Array Spreads](#es6-array-spreads) 1888 | 1. [Let and Const](#references) 1889 | 1. [Iterators and Generators](#iterators-and-generators) 1890 | 1. [Modules](#modules) 1891 | 1892 | **[⬆ back to top](#table-of-contents)** 1893 | 1894 | 1895 | ## Contributors 1896 | 1897 | - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) 1898 | 1899 | 1900 | ## License 1901 | 1902 | (The MIT License) 1903 | 1904 | Copyright (c) 2014 Airbnb 1905 | 1906 | Permission is hereby granted, free of charge, to any person obtaining 1907 | a copy of this software and associated documentation files (the 1908 | 'Software'), to deal in the Software without restriction, including 1909 | without limitation the rights to use, copy, modify, merge, publish, 1910 | distribute, sublicense, and/or sell copies of the Software, and to 1911 | permit persons to whom the Software is furnished to do so, subject to 1912 | the following conditions: 1913 | 1914 | The above copyright notice and this permission notice shall be 1915 | included in all copies or substantial portions of the Software. 1916 | 1917 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 1918 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1919 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1920 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 1921 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 1922 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 1923 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1924 | 1925 | **[⬆ back to top](#table-of-contents)** 1926 | 1927 | # }; 1928 | -------------------------------------------------------------------------------- /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 | *A mostly reasonable approach to JavaScript* 6 | 7 | 8 | ## Table of Contents 9 | 10 | 1. [Types](#types) 11 | 1. [Objects](#objects) 12 | 1. [Arrays](#arrays) 13 | 1. [Strings](#strings) 14 | 1. [Functions](#functions) 15 | 1. [Properties](#properties) 16 | 1. [Variables](#variables) 17 | 1. [Hoisting](#hoisting) 18 | 1. [Comparison Operators & Equality](#comparison-operators--equality) 19 | 1. [Blocks](#blocks) 20 | 1. [Comments](#comments) 21 | 1. [Whitespace](#whitespace) 22 | 1. [Commas](#commas) 23 | 1. [Semicolons](#semicolons) 24 | 1. [Type Casting & Coercion](#type-casting--coercion) 25 | 1. [Naming Conventions](#naming-conventions) 26 | 1. [Accessors](#accessors) 27 | 1. [Constructors](#constructors) 28 | 1. [Events](#events) 29 | 1. [Modules](#modules) 30 | 1. [jQuery](#jquery) 31 | 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility) 32 | 1. [Testing](#testing) 33 | 1. [Performance](#performance) 34 | 1. [Resources](#resources) 35 | 1. [In the Wild](#in-the-wild) 36 | 1. [Translation](#translation) 37 | 1. [The JavaScript Style Guide Guide](#the-javascript-style-guide-guide) 38 | 1. [Chat With Us About Javascript](#chat-with-us-about-javascript) 39 | 1. [Contributors](#contributors) 40 | 1. [License](#license) 41 | 42 | ## Types 43 | 44 | - **Primitives**: When you access a primitive type you work directly on its value. 45 | 46 | + `string` 47 | + `number` 48 | + `boolean` 49 | + `null` 50 | + `undefined` 51 | 52 | ```javascript 53 | var foo = 1; 54 | var bar = foo; 55 | 56 | bar = 9; 57 | 58 | console.log(foo, bar); // => 1, 9 59 | ``` 60 | - **Complex**: When you access a complex type you work on a reference to its value. 61 | 62 | + `object` 63 | + `array` 64 | + `function` 65 | 66 | ```javascript 67 | var foo = [1, 2]; 68 | var bar = foo; 69 | 70 | bar[0] = 9; 71 | 72 | console.log(foo[0], bar[0]); // => 9, 9 73 | ``` 74 | 75 | **[⬆ back to top](#table-of-contents)** 76 | 77 | ## Objects 78 | 79 | - Use the literal syntax for object creation. 80 | 81 | ```javascript 82 | // bad 83 | var item = new Object(); 84 | 85 | // good 86 | var item = {}; 87 | ``` 88 | 89 | - Don't use [reserved words](http://es5.github.io/#x7.6.1) as keys. It won't work in IE8. [More info](https://github.com/airbnb/javascript/issues/61). 90 | 91 | ```javascript 92 | // bad 93 | var superman = { 94 | default: { clark: 'kent' }, 95 | private: true 96 | }; 97 | 98 | // good 99 | var superman = { 100 | defaults: { clark: 'kent' }, 101 | hidden: true 102 | }; 103 | ``` 104 | 105 | - Use readable synonyms in place of reserved words. 106 | 107 | ```javascript 108 | // bad 109 | var superman = { 110 | class: 'alien' 111 | }; 112 | 113 | // bad 114 | var superman = { 115 | klass: 'alien' 116 | }; 117 | 118 | // good 119 | var superman = { 120 | type: 'alien' 121 | }; 122 | ``` 123 | 124 | **[⬆ back to top](#table-of-contents)** 125 | 126 | ## Arrays 127 | 128 | - Use the literal syntax for array creation. 129 | 130 | ```javascript 131 | // bad 132 | var items = new Array(); 133 | 134 | // good 135 | var items = []; 136 | ``` 137 | 138 | - Use Array#push instead of direct assignment to add items to an array. 139 | 140 | ```javascript 141 | var someStack = []; 142 | 143 | 144 | // bad 145 | someStack[someStack.length] = 'abracadabra'; 146 | 147 | // good 148 | someStack.push('abracadabra'); 149 | ``` 150 | 151 | - When you need to copy an array use Array#slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) 152 | 153 | ```javascript 154 | var len = items.length; 155 | var itemsCopy = []; 156 | var i; 157 | 158 | // bad 159 | for (i = 0; i < len; i++) { 160 | itemsCopy[i] = items[i]; 161 | } 162 | 163 | // good 164 | itemsCopy = items.slice(); 165 | ``` 166 | 167 | - To convert an array-like object to an array, use Array#slice. 168 | 169 | ```javascript 170 | function trigger() { 171 | var args = Array.prototype.slice.call(arguments); 172 | ... 173 | } 174 | ``` 175 | 176 | **[⬆ back to top](#table-of-contents)** 177 | 178 | 179 | ## Strings 180 | 181 | - Use single quotes `''` for strings. 182 | 183 | ```javascript 184 | // bad 185 | var name = "Bob Parr"; 186 | 187 | // good 188 | var name = 'Bob Parr'; 189 | 190 | // bad 191 | var fullName = "Bob " + this.lastName; 192 | 193 | // good 194 | var fullName = 'Bob ' + this.lastName; 195 | ``` 196 | 197 | - Strings longer than 80 characters should be written across multiple lines using string concatenation. 198 | - Note: If overused, long strings with concatenation could impact performance. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40). 199 | 200 | ```javascript 201 | // bad 202 | 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.'; 203 | 204 | // bad 205 | var errorMessage = 'This is a super long error that was thrown because \ 206 | of Batman. When you stop to think about how Batman had anything to do \ 207 | with this, you would get nowhere \ 208 | fast.'; 209 | 210 | // good 211 | var errorMessage = 'This is a super long error that was thrown because ' + 212 | 'of Batman. When you stop to think about how Batman had anything to do ' + 213 | 'with this, you would get nowhere fast.'; 214 | ``` 215 | 216 | - When programmatically building up a string, use Array#join instead of string concatenation. Mostly for IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). 217 | 218 | ```javascript 219 | var items; 220 | var messages; 221 | var length; 222 | var i; 223 | 224 | messages = [{ 225 | state: 'success', 226 | message: 'This one worked.' 227 | }, { 228 | state: 'success', 229 | message: 'This one worked as well.' 230 | }, { 231 | state: 'error', 232 | message: 'This one did not work.' 233 | }]; 234 | 235 | length = messages.length; 236 | 237 | // bad 238 | function inbox(messages) { 239 | items = ''; 246 | } 247 | 248 | // good 249 | function inbox(messages) { 250 | items = []; 251 | 252 | for (i = 0; i < length; i++) { 253 | // use direct assignment in this case because we're micro-optimizing. 254 | items[i] = '
  • ' + messages[i].message + '
  • '; 255 | } 256 | 257 | return ''; 258 | } 259 | ``` 260 | 261 | **[⬆ back to top](#table-of-contents)** 262 | 263 | 264 | ## Functions 265 | 266 | - Function expressions: 267 | 268 | ```javascript 269 | // anonymous function expression 270 | var anonymous = function() { 271 | return true; 272 | }; 273 | 274 | // named function expression 275 | var named = function named() { 276 | return true; 277 | }; 278 | 279 | // immediately-invoked function expression (IIFE) 280 | (function() { 281 | console.log('Welcome to the Internet. Please follow me.'); 282 | })(); 283 | ``` 284 | 285 | - Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. 286 | - **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement. [Read ECMA-262's note on this issue](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). 287 | 288 | ```javascript 289 | // bad 290 | if (currentUser) { 291 | function test() { 292 | console.log('Nope.'); 293 | } 294 | } 295 | 296 | // good 297 | var test; 298 | if (currentUser) { 299 | test = function test() { 300 | console.log('Yup.'); 301 | }; 302 | } 303 | ``` 304 | 305 | - Never name a parameter `arguments`. This will take precedence over the `arguments` object that is given to every function scope. 306 | 307 | ```javascript 308 | // bad 309 | function nope(name, options, arguments) { 310 | // ...stuff... 311 | } 312 | 313 | // good 314 | function yup(name, options, args) { 315 | // ...stuff... 316 | } 317 | ``` 318 | 319 | **[⬆ back to top](#table-of-contents)** 320 | 321 | 322 | 323 | ## Properties 324 | 325 | - Use dot notation when accessing properties. 326 | 327 | ```javascript 328 | var luke = { 329 | jedi: true, 330 | age: 28 331 | }; 332 | 333 | // bad 334 | var isJedi = luke['jedi']; 335 | 336 | // good 337 | var isJedi = luke.jedi; 338 | ``` 339 | 340 | - Use subscript notation `[]` when accessing properties with a variable. 341 | 342 | ```javascript 343 | var luke = { 344 | jedi: true, 345 | age: 28 346 | }; 347 | 348 | function getProp(prop) { 349 | return luke[prop]; 350 | } 351 | 352 | var isJedi = getProp('jedi'); 353 | ``` 354 | 355 | **[⬆ back to top](#table-of-contents)** 356 | 357 | 358 | ## Variables 359 | 360 | - Always use `var` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. 361 | 362 | ```javascript 363 | // bad 364 | superPower = new SuperPower(); 365 | 366 | // good 367 | var superPower = new SuperPower(); 368 | ``` 369 | 370 | - Use one `var` declaration per variable. 371 | It's easier to add new variable declarations this way, and you never have 372 | to worry about swapping out a `;` for a `,` or introducing punctuation-only 373 | diffs. 374 | 375 | ```javascript 376 | // bad 377 | var items = getItems(), 378 | goSportsTeam = true, 379 | dragonball = 'z'; 380 | 381 | // bad 382 | // (compare to above, and try to spot the mistake) 383 | var items = getItems(), 384 | goSportsTeam = true; 385 | dragonball = 'z'; 386 | 387 | // good 388 | var items = getItems(); 389 | var goSportsTeam = true; 390 | var dragonball = 'z'; 391 | ``` 392 | 393 | - Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. 394 | 395 | ```javascript 396 | // bad 397 | var i, len, dragonball, 398 | items = getItems(), 399 | goSportsTeam = true; 400 | 401 | // bad 402 | var i; 403 | var items = getItems(); 404 | var dragonball; 405 | var goSportsTeam = true; 406 | var len; 407 | 408 | // good 409 | var items = getItems(); 410 | var goSportsTeam = true; 411 | var dragonball; 412 | var length; 413 | var i; 414 | ``` 415 | 416 | - Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues. 417 | 418 | ```javascript 419 | // bad 420 | function() { 421 | test(); 422 | console.log('doing stuff..'); 423 | 424 | //..other stuff.. 425 | 426 | var name = getName(); 427 | 428 | if (name === 'test') { 429 | return false; 430 | } 431 | 432 | return name; 433 | } 434 | 435 | // good 436 | function() { 437 | var name = getName(); 438 | 439 | test(); 440 | console.log('doing stuff..'); 441 | 442 | //..other stuff.. 443 | 444 | if (name === 'test') { 445 | return false; 446 | } 447 | 448 | return name; 449 | } 450 | 451 | // bad - unnessary function call 452 | function() { 453 | var name = getName(); 454 | 455 | if (!arguments.length) { 456 | return false; 457 | } 458 | 459 | this.setFirstName(name); 460 | 461 | return true; 462 | } 463 | 464 | // good 465 | function() { 466 | var name; 467 | 468 | if (!arguments.length) { 469 | return false; 470 | } 471 | 472 | name = getName(); 473 | this.setFirstName(name); 474 | 475 | return true; 476 | } 477 | ``` 478 | 479 | **[⬆ back to top](#table-of-contents)** 480 | 481 | 482 | ## Hoisting 483 | 484 | - Variable declarations get hoisted to the top of their scope, but their assignment does not. 485 | 486 | ```javascript 487 | // we know this wouldn't work (assuming there 488 | // is no notDefined global variable) 489 | function example() { 490 | console.log(notDefined); // => throws a ReferenceError 491 | } 492 | 493 | // creating a variable declaration after you 494 | // reference the variable will work due to 495 | // variable hoisting. Note: the assignment 496 | // value of `true` is not hoisted. 497 | function example() { 498 | console.log(declaredButNotAssigned); // => undefined 499 | var declaredButNotAssigned = true; 500 | } 501 | 502 | // The interpreter is hoisting the variable 503 | // declaration to the top of the scope, 504 | // which means our example could be rewritten as: 505 | function example() { 506 | var declaredButNotAssigned; 507 | console.log(declaredButNotAssigned); // => undefined 508 | declaredButNotAssigned = true; 509 | } 510 | ``` 511 | 512 | - Anonymous function expressions hoist their variable name, but not the function assignment. 513 | 514 | ```javascript 515 | function example() { 516 | console.log(anonymous); // => undefined 517 | 518 | anonymous(); // => TypeError anonymous is not a function 519 | 520 | var anonymous = function() { 521 | console.log('anonymous function expression'); 522 | }; 523 | } 524 | ``` 525 | 526 | - Named function expressions hoist the variable name, not the function name or the function body. 527 | 528 | ```javascript 529 | function example() { 530 | console.log(named); // => undefined 531 | 532 | named(); // => TypeError named is not a function 533 | 534 | superPower(); // => ReferenceError superPower is not defined 535 | 536 | var named = function superPower() { 537 | console.log('Flying'); 538 | }; 539 | } 540 | 541 | // the same is true when the function name 542 | // is the same as the variable name. 543 | function example() { 544 | console.log(named); // => undefined 545 | 546 | named(); // => TypeError named is not a function 547 | 548 | var named = function named() { 549 | console.log('named'); 550 | } 551 | } 552 | ``` 553 | 554 | - Function declarations hoist their name and the function body. 555 | 556 | ```javascript 557 | function example() { 558 | superPower(); // => Flying 559 | 560 | function superPower() { 561 | console.log('Flying'); 562 | } 563 | } 564 | ``` 565 | 566 | - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). 567 | 568 | **[⬆ back to top](#table-of-contents)** 569 | 570 | 571 | 572 | ## Comparison Operators & Equality 573 | 574 | - Use `===` and `!==` over `==` and `!=`. 575 | - Conditional statements such as the `if` statement evaulate their expression using coercion with the `ToBoolean` abstract method and always follow these simple rules: 576 | 577 | + **Objects** evaluate to **true** 578 | + **Undefined** evaluates to **false** 579 | + **Null** evaluates to **false** 580 | + **Booleans** evaluate to **the value of the boolean** 581 | + **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true** 582 | + **Strings** evaluate to **false** if an empty string `''`, otherwise **true** 583 | 584 | ```javascript 585 | if ([0]) { 586 | // true 587 | // An array is an object, objects evaluate to true 588 | } 589 | ``` 590 | 591 | - Use shortcuts. 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 | - For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. 616 | 617 | **[⬆ back to top](#table-of-contents)** 618 | 619 | 620 | ## Blocks 621 | 622 | - Use braces with all multi-line blocks. 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 you're using multi-line blocks with `if` and `else`, put `else` on the same line as your 647 | `if` block's closing brace. 648 | 649 | ```javascript 650 | // bad 651 | if (test) { 652 | thing1(); 653 | thing2(); 654 | } 655 | else { 656 | thing3(); 657 | } 658 | 659 | // good 660 | if (test) { 661 | thing1(); 662 | thing2(); 663 | } else { 664 | thing3(); 665 | } 666 | ``` 667 | 668 | 669 | **[⬆ back to top](#table-of-contents)** 670 | 671 | 672 | ## Comments 673 | 674 | - Use `/** ... */` for multi-line comments. Include a description, specify types and values for all parameters and return values. 675 | 676 | ```javascript 677 | // bad 678 | // make() returns a new element 679 | // based on the passed in tag name 680 | // 681 | // @param {String} tag 682 | // @return {Element} element 683 | function make(tag) { 684 | 685 | // ...stuff... 686 | 687 | return element; 688 | } 689 | 690 | // good 691 | /** 692 | * make() returns a new element 693 | * based on the passed in tag name 694 | * 695 | * @param {String} tag 696 | * @return {Element} element 697 | */ 698 | function make(tag) { 699 | 700 | // ...stuff... 701 | 702 | return element; 703 | } 704 | ``` 705 | 706 | - Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment. 707 | 708 | ```javascript 709 | // bad 710 | var active = true; // is current tab 711 | 712 | // good 713 | // is current tab 714 | var active = true; 715 | 716 | // bad 717 | function getType() { 718 | console.log('fetching type...'); 719 | // set the default type to 'no type' 720 | var type = this._type || 'no type'; 721 | 722 | return type; 723 | } 724 | 725 | // good 726 | function getType() { 727 | console.log('fetching type...'); 728 | 729 | // set the default type to 'no type' 730 | var type = this._type || 'no type'; 731 | 732 | return type; 733 | } 734 | ``` 735 | 736 | - Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME -- need to figure this out` or `TODO -- need to implement`. 737 | 738 | - Use `// FIXME:` to annotate problems. 739 | 740 | ```javascript 741 | function Calculator() { 742 | 743 | // FIXME: shouldn't use a global here 744 | total = 0; 745 | 746 | return this; 747 | } 748 | ``` 749 | 750 | - Use `// TODO:` to annotate solutions to problems. 751 | 752 | ```javascript 753 | function Calculator() { 754 | 755 | // TODO: total should be configurable by an options param 756 | this.total = 0; 757 | 758 | return this; 759 | } 760 | ``` 761 | 762 | **[⬆ back to top](#table-of-contents)** 763 | 764 | 765 | ## Whitespace 766 | 767 | - Use soft tabs set to 2 spaces. 768 | 769 | ```javascript 770 | // bad 771 | function() { 772 | ∙∙∙∙var name; 773 | } 774 | 775 | // bad 776 | function() { 777 | ∙var name; 778 | } 779 | 780 | // good 781 | function() { 782 | ∙∙var name; 783 | } 784 | ``` 785 | 786 | - Place 1 space before the leading brace. 787 | 788 | ```javascript 789 | // bad 790 | function test(){ 791 | console.log('test'); 792 | } 793 | 794 | // good 795 | function test() { 796 | console.log('test'); 797 | } 798 | 799 | // bad 800 | dog.set('attr',{ 801 | age: '1 year', 802 | breed: 'Bernese Mountain Dog' 803 | }); 804 | 805 | // good 806 | dog.set('attr', { 807 | age: '1 year', 808 | breed: 'Bernese Mountain Dog' 809 | }); 810 | ``` 811 | 812 | - Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space before the argument list in function calls and declarations. 813 | 814 | ```javascript 815 | // bad 816 | if(isJedi) { 817 | fight (); 818 | } 819 | 820 | // good 821 | if (isJedi) { 822 | fight(); 823 | } 824 | 825 | // bad 826 | function fight () { 827 | console.log ('Swooosh!'); 828 | } 829 | 830 | // good 831 | function fight() { 832 | console.log('Swooosh!'); 833 | } 834 | ``` 835 | 836 | - Set off operators with spaces. 837 | 838 | ```javascript 839 | // bad 840 | var x=y+5; 841 | 842 | // good 843 | var x = y + 5; 844 | ``` 845 | 846 | - End files with a single newline character. 847 | 848 | ```javascript 849 | // bad 850 | (function(global) { 851 | // ...stuff... 852 | })(this); 853 | ``` 854 | 855 | ```javascript 856 | // bad 857 | (function(global) { 858 | // ...stuff... 859 | })(this);↵ 860 | ↵ 861 | ``` 862 | 863 | ```javascript 864 | // good 865 | (function(global) { 866 | // ...stuff... 867 | })(this);↵ 868 | ``` 869 | 870 | - Use indentation when making long method chains. Use a leading dot, which 871 | emphasizes that the line is a method call, not a new statement. 872 | 873 | ```javascript 874 | // bad 875 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 876 | 877 | // bad 878 | $('#items'). 879 | find('.selected'). 880 | highlight(). 881 | end(). 882 | find('.open'). 883 | updateCount(); 884 | 885 | // good 886 | $('#items') 887 | .find('.selected') 888 | .highlight() 889 | .end() 890 | .find('.open') 891 | .updateCount(); 892 | 893 | // bad 894 | var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) 895 | .attr('width', (radius + margin) * 2).append('svg:g') 896 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 897 | .call(tron.led); 898 | 899 | // good 900 | var leds = stage.selectAll('.led') 901 | .data(data) 902 | .enter().append('svg:svg') 903 | .classed('led', true) 904 | .attr('width', (radius + margin) * 2) 905 | .append('svg:g') 906 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 907 | .call(tron.led); 908 | ``` 909 | 910 | - Leave a blank line after blocks and before the next statement 911 | 912 | ```javascript 913 | // bad 914 | if (foo) { 915 | return bar; 916 | } 917 | return baz; 918 | 919 | // good 920 | if (foo) { 921 | return bar; 922 | } 923 | 924 | return baz; 925 | 926 | // bad 927 | var obj = { 928 | foo: function() { 929 | }, 930 | bar: function() { 931 | } 932 | }; 933 | return obj; 934 | 935 | // good 936 | var obj = { 937 | foo: function() { 938 | }, 939 | 940 | bar: function() { 941 | } 942 | }; 943 | 944 | return obj; 945 | ``` 946 | 947 | 948 | **[⬆ back to top](#table-of-contents)** 949 | 950 | ## Commas 951 | 952 | - Leading commas: **Nope.** 953 | 954 | ```javascript 955 | // bad 956 | var story = [ 957 | once 958 | , upon 959 | , aTime 960 | ]; 961 | 962 | // good 963 | var story = [ 964 | once, 965 | upon, 966 | aTime 967 | ]; 968 | 969 | // bad 970 | var hero = { 971 | firstName: 'Bob' 972 | , lastName: 'Parr' 973 | , heroName: 'Mr. Incredible' 974 | , superPower: 'strength' 975 | }; 976 | 977 | // good 978 | var hero = { 979 | firstName: 'Bob', 980 | lastName: 'Parr', 981 | heroName: 'Mr. Incredible', 982 | superPower: 'strength' 983 | }; 984 | ``` 985 | 986 | - Additional trailing comma: **Nope.** This can cause problems with IE6/7 and IE9 if it's in quirksmode. Also, in some implementations of ES3 would add length to an array if it had an additional trailing comma. This was clarified in ES5 ([source](http://es5.github.io/#D)): 987 | 988 | > 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. 989 | 990 | ```javascript 991 | // bad 992 | var hero = { 993 | firstName: 'Kevin', 994 | lastName: 'Flynn', 995 | }; 996 | 997 | var heroes = [ 998 | 'Batman', 999 | 'Superman', 1000 | ]; 1001 | 1002 | // good 1003 | var hero = { 1004 | firstName: 'Kevin', 1005 | lastName: 'Flynn' 1006 | }; 1007 | 1008 | var heroes = [ 1009 | 'Batman', 1010 | 'Superman' 1011 | ]; 1012 | ``` 1013 | 1014 | **[⬆ back to top](#table-of-contents)** 1015 | 1016 | 1017 | ## Semicolons 1018 | 1019 | - **Yup.** 1020 | 1021 | ```javascript 1022 | // bad 1023 | (function() { 1024 | var name = 'Skywalker' 1025 | return name 1026 | })() 1027 | 1028 | // good 1029 | (function() { 1030 | var name = 'Skywalker'; 1031 | return name; 1032 | })(); 1033 | 1034 | // good (guards against the function becoming an argument when two files with IIFEs are concatenated) 1035 | ;(function() { 1036 | var name = 'Skywalker'; 1037 | return name; 1038 | })(); 1039 | ``` 1040 | 1041 | [Read more](http://stackoverflow.com/a/7365214/1712802). 1042 | 1043 | **[⬆ back to top](#table-of-contents)** 1044 | 1045 | 1046 | ## Type Casting & Coercion 1047 | 1048 | - Perform type coercion at the beginning of the statement. 1049 | - Strings: 1050 | 1051 | ```javascript 1052 | // => this.reviewScore = 9; 1053 | 1054 | // bad 1055 | var totalScore = this.reviewScore + ''; 1056 | 1057 | // good 1058 | var totalScore = '' + this.reviewScore; 1059 | 1060 | // bad 1061 | var totalScore = '' + this.reviewScore + ' total score'; 1062 | 1063 | // good 1064 | var totalScore = this.reviewScore + ' total score'; 1065 | ``` 1066 | 1067 | - Use `parseInt` for Numbers and always with a radix for type casting. 1068 | 1069 | ```javascript 1070 | var inputValue = '4'; 1071 | 1072 | // bad 1073 | var val = new Number(inputValue); 1074 | 1075 | // bad 1076 | var val = +inputValue; 1077 | 1078 | // bad 1079 | var val = inputValue >> 0; 1080 | 1081 | // bad 1082 | var val = parseInt(inputValue); 1083 | 1084 | // good 1085 | var val = Number(inputValue); 1086 | 1087 | // good 1088 | var val = parseInt(inputValue, 10); 1089 | ``` 1090 | 1091 | - If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](http://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing. 1092 | 1093 | ```javascript 1094 | // good 1095 | /** 1096 | * parseInt was the reason my code was slow. 1097 | * Bitshifting the String to coerce it to a 1098 | * Number made it a lot faster. 1099 | */ 1100 | var val = inputValue >> 0; 1101 | ``` 1102 | 1103 | - **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](http://es5.github.io/#x4.3.19), but Bitshift operations always return a 32-bit integer ([source](http://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647: 1104 | 1105 | ```javascript 1106 | 2147483647 >> 0 //=> 2147483647 1107 | 2147483648 >> 0 //=> -2147483648 1108 | 2147483649 >> 0 //=> -2147483647 1109 | ``` 1110 | 1111 | - Booleans: 1112 | 1113 | ```javascript 1114 | var age = 0; 1115 | 1116 | // bad 1117 | var hasAge = new Boolean(age); 1118 | 1119 | // good 1120 | var hasAge = Boolean(age); 1121 | 1122 | // good 1123 | var hasAge = !!age; 1124 | ``` 1125 | 1126 | **[⬆ back to top](#table-of-contents)** 1127 | 1128 | 1129 | ## Naming Conventions 1130 | 1131 | - Avoid single letter names. Be descriptive with your naming. 1132 | 1133 | ```javascript 1134 | // bad 1135 | function q() { 1136 | // ...stuff... 1137 | } 1138 | 1139 | // good 1140 | function query() { 1141 | // ..stuff.. 1142 | } 1143 | ``` 1144 | 1145 | - Use camelCase when naming objects, functions, and instances. 1146 | 1147 | ```javascript 1148 | // bad 1149 | var OBJEcttsssss = {}; 1150 | var this_is_my_object = {}; 1151 | var o = {}; 1152 | function c() {} 1153 | 1154 | // good 1155 | var thisIsMyObject = {}; 1156 | function thisIsMyFunction() {} 1157 | ``` 1158 | 1159 | - Use PascalCase when naming constructors or classes. 1160 | 1161 | ```javascript 1162 | // bad 1163 | function user(options) { 1164 | this.name = options.name; 1165 | } 1166 | 1167 | var bad = new user({ 1168 | name: 'nope' 1169 | }); 1170 | 1171 | // good 1172 | function User(options) { 1173 | this.name = options.name; 1174 | } 1175 | 1176 | var good = new User({ 1177 | name: 'yup' 1178 | }); 1179 | ``` 1180 | 1181 | - Use a leading underscore `_` when naming private properties. 1182 | 1183 | ```javascript 1184 | // bad 1185 | this.__firstName__ = 'Panda'; 1186 | this.firstName_ = 'Panda'; 1187 | 1188 | // good 1189 | this._firstName = 'Panda'; 1190 | ``` 1191 | 1192 | - When saving a reference to `this` use `_this`. 1193 | 1194 | ```javascript 1195 | // bad 1196 | function() { 1197 | var self = this; 1198 | return function() { 1199 | console.log(self); 1200 | }; 1201 | } 1202 | 1203 | // bad 1204 | function() { 1205 | var that = this; 1206 | return function() { 1207 | console.log(that); 1208 | }; 1209 | } 1210 | 1211 | // good 1212 | function() { 1213 | var _this = this; 1214 | return function() { 1215 | console.log(_this); 1216 | }; 1217 | } 1218 | ``` 1219 | 1220 | - Name your functions. This is helpful for stack traces. 1221 | 1222 | ```javascript 1223 | // bad 1224 | var log = function(msg) { 1225 | console.log(msg); 1226 | }; 1227 | 1228 | // good 1229 | var log = function log(msg) { 1230 | console.log(msg); 1231 | }; 1232 | ``` 1233 | 1234 | - **Note:** IE8 and below exhibit some quirks with named function expressions. See [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/) for more info. 1235 | 1236 | - If your file exports a single class, your filename should be exactly the name of the class. 1237 | ```javascript 1238 | // file contents 1239 | class CheckBox { 1240 | // ... 1241 | } 1242 | module.exports = CheckBox; 1243 | 1244 | // in some other file 1245 | // bad 1246 | var CheckBox = require('./checkBox'); 1247 | 1248 | // bad 1249 | var CheckBox = require('./check_box'); 1250 | 1251 | // good 1252 | var CheckBox = require('./CheckBox'); 1253 | ``` 1254 | 1255 | **[⬆ back to top](#table-of-contents)** 1256 | 1257 | 1258 | ## Accessors 1259 | 1260 | - Accessor functions for properties are not required. 1261 | - If you do make accessor functions use getVal() and setVal('hello'). 1262 | 1263 | ```javascript 1264 | // bad 1265 | dragon.age(); 1266 | 1267 | // good 1268 | dragon.getAge(); 1269 | 1270 | // bad 1271 | dragon.age(25); 1272 | 1273 | // good 1274 | dragon.setAge(25); 1275 | ``` 1276 | 1277 | - If the property is a boolean, use isVal() or hasVal(). 1278 | 1279 | ```javascript 1280 | // bad 1281 | if (!dragon.age()) { 1282 | return false; 1283 | } 1284 | 1285 | // good 1286 | if (!dragon.hasAge()) { 1287 | return false; 1288 | } 1289 | ``` 1290 | 1291 | - It's okay to create get() and set() functions, but be consistent. 1292 | 1293 | ```javascript 1294 | function Jedi(options) { 1295 | options || (options = {}); 1296 | var lightsaber = options.lightsaber || 'blue'; 1297 | this.set('lightsaber', lightsaber); 1298 | } 1299 | 1300 | Jedi.prototype.set = function(key, val) { 1301 | this[key] = val; 1302 | }; 1303 | 1304 | Jedi.prototype.get = function(key) { 1305 | return this[key]; 1306 | }; 1307 | ``` 1308 | 1309 | **[⬆ back to top](#table-of-contents)** 1310 | 1311 | 1312 | ## Constructors 1313 | 1314 | - Assign methods to the prototype object, instead of overwriting the prototype with a new object. Overwriting the prototype makes inheritance impossible: by resetting the prototype you'll overwrite the base! 1315 | 1316 | ```javascript 1317 | function Jedi() { 1318 | console.log('new jedi'); 1319 | } 1320 | 1321 | // bad 1322 | Jedi.prototype = { 1323 | fight: function fight() { 1324 | console.log('fighting'); 1325 | }, 1326 | 1327 | block: function block() { 1328 | console.log('blocking'); 1329 | } 1330 | }; 1331 | 1332 | // good 1333 | Jedi.prototype.fight = function fight() { 1334 | console.log('fighting'); 1335 | }; 1336 | 1337 | Jedi.prototype.block = function block() { 1338 | console.log('blocking'); 1339 | }; 1340 | ``` 1341 | 1342 | - Methods can return `this` to help with method chaining. 1343 | 1344 | ```javascript 1345 | // bad 1346 | Jedi.prototype.jump = function() { 1347 | this.jumping = true; 1348 | return true; 1349 | }; 1350 | 1351 | Jedi.prototype.setHeight = function(height) { 1352 | this.height = height; 1353 | }; 1354 | 1355 | var luke = new Jedi(); 1356 | luke.jump(); // => true 1357 | luke.setHeight(20); // => undefined 1358 | 1359 | // good 1360 | Jedi.prototype.jump = function() { 1361 | this.jumping = true; 1362 | return this; 1363 | }; 1364 | 1365 | Jedi.prototype.setHeight = function(height) { 1366 | this.height = height; 1367 | return this; 1368 | }; 1369 | 1370 | var luke = new Jedi(); 1371 | 1372 | luke.jump() 1373 | .setHeight(20); 1374 | ``` 1375 | 1376 | 1377 | - It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects. 1378 | 1379 | ```javascript 1380 | function Jedi(options) { 1381 | options || (options = {}); 1382 | this.name = options.name || 'no name'; 1383 | } 1384 | 1385 | Jedi.prototype.getName = function getName() { 1386 | return this.name; 1387 | }; 1388 | 1389 | Jedi.prototype.toString = function toString() { 1390 | return 'Jedi - ' + this.getName(); 1391 | }; 1392 | ``` 1393 | 1394 | **[⬆ back to top](#table-of-contents)** 1395 | 1396 | 1397 | ## Events 1398 | 1399 | - When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of: 1400 | 1401 | ```js 1402 | // bad 1403 | $(this).trigger('listingUpdated', listing.id); 1404 | 1405 | ... 1406 | 1407 | $(this).on('listingUpdated', function(e, listingId) { 1408 | // do something with listingId 1409 | }); 1410 | ``` 1411 | 1412 | prefer: 1413 | 1414 | ```js 1415 | // good 1416 | $(this).trigger('listingUpdated', { listingId : listing.id }); 1417 | 1418 | ... 1419 | 1420 | $(this).on('listingUpdated', function(e, data) { 1421 | // do something with data.listingId 1422 | }); 1423 | ``` 1424 | 1425 | **[⬆ back to top](#table-of-contents)** 1426 | 1427 | 1428 | ## Modules 1429 | 1430 | - The module should start with a `!`. This ensures that if a malformed module forgets to include a final semicolon there aren't errors in production when the scripts get concatenated. [Explanation](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933) 1431 | - The file should be named with camelCase, live in a folder with the same name, and match the name of the single export. 1432 | - Add a method called `noConflict()` that sets the exported module to the previous version and returns this one. 1433 | - Always declare `'use strict';` at the top of the module. 1434 | 1435 | ```javascript 1436 | // fancyInput/fancyInput.js 1437 | 1438 | !function(global) { 1439 | 'use strict'; 1440 | 1441 | var previousFancyInput = global.FancyInput; 1442 | 1443 | function FancyInput(options) { 1444 | this.options = options || {}; 1445 | } 1446 | 1447 | FancyInput.noConflict = function noConflict() { 1448 | global.FancyInput = previousFancyInput; 1449 | return FancyInput; 1450 | }; 1451 | 1452 | global.FancyInput = FancyInput; 1453 | }(this); 1454 | ``` 1455 | 1456 | **[⬆ back to top](#table-of-contents)** 1457 | 1458 | 1459 | ## jQuery 1460 | 1461 | - Prefix jQuery object variables with a `$`. 1462 | 1463 | ```javascript 1464 | // bad 1465 | var sidebar = $('.sidebar'); 1466 | 1467 | // good 1468 | var $sidebar = $('.sidebar'); 1469 | ``` 1470 | 1471 | - Cache jQuery lookups. 1472 | 1473 | ```javascript 1474 | // bad 1475 | function setSidebar() { 1476 | $('.sidebar').hide(); 1477 | 1478 | // ...stuff... 1479 | 1480 | $('.sidebar').css({ 1481 | 'background-color': 'pink' 1482 | }); 1483 | } 1484 | 1485 | // good 1486 | function setSidebar() { 1487 | var $sidebar = $('.sidebar'); 1488 | $sidebar.hide(); 1489 | 1490 | // ...stuff... 1491 | 1492 | $sidebar.css({ 1493 | 'background-color': 'pink' 1494 | }); 1495 | } 1496 | ``` 1497 | 1498 | - For DOM queries use Cascading `$('.sidebar ul')` or parent > child `$('.sidebar > ul')`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) 1499 | - Use `find` with scoped jQuery object queries. 1500 | 1501 | ```javascript 1502 | // bad 1503 | $('ul', '.sidebar').hide(); 1504 | 1505 | // bad 1506 | $('.sidebar').find('ul').hide(); 1507 | 1508 | // good 1509 | $('.sidebar ul').hide(); 1510 | 1511 | // good 1512 | $('.sidebar > ul').hide(); 1513 | 1514 | // good 1515 | $sidebar.find('ul').hide(); 1516 | ``` 1517 | 1518 | **[⬆ back to top](#table-of-contents)** 1519 | 1520 | 1521 | ## ECMAScript 5 Compatibility 1522 | 1523 | - Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/). 1524 | 1525 | **[⬆ back to top](#table-of-contents)** 1526 | 1527 | 1528 | ## Testing 1529 | 1530 | - **Yup.** 1531 | 1532 | ```javascript 1533 | function() { 1534 | return true; 1535 | } 1536 | ``` 1537 | 1538 | **[⬆ back to top](#table-of-contents)** 1539 | 1540 | 1541 | ## Performance 1542 | 1543 | - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) 1544 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) 1545 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) 1546 | - [Bang Function](http://jsperf.com/bang-function) 1547 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) 1548 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) 1549 | - [Long String Concatenation](http://jsperf.com/ya-string-concat) 1550 | - Loading... 1551 | 1552 | **[⬆ back to top](#table-of-contents)** 1553 | 1554 | 1555 | ## Resources 1556 | 1557 | 1558 | **Read This** 1559 | 1560 | - [Annotated ECMAScript 5.1](http://es5.github.com/) 1561 | 1562 | **Tools** 1563 | 1564 | - Code Style Linters 1565 | + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) 1566 | + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) 1567 | 1568 | **Other Styleguides** 1569 | 1570 | - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) 1571 | - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) 1572 | - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) 1573 | - [JavaScript Standard Style](https://github.com/feross/standard) 1574 | 1575 | **Other Styles** 1576 | 1577 | - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen 1578 | - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen 1579 | - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun 1580 | - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman 1581 | 1582 | **Further Reading** 1583 | 1584 | - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll 1585 | - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer 1586 | - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz 1587 | - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban 1588 | - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock 1589 | 1590 | **Books** 1591 | 1592 | - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford 1593 | - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov 1594 | - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz 1595 | - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders 1596 | - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas 1597 | - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw 1598 | - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig 1599 | - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch 1600 | - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault 1601 | - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg 1602 | - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy 1603 | - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon 1604 | - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov 1605 | - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman 1606 | - [Eloquent JavaScript](http://eloquentjavascript.net) - Marijn Haverbeke 1607 | - [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS) - Kyle Simpson 1608 | 1609 | **Blogs** 1610 | 1611 | - [DailyJS](http://dailyjs.com/) 1612 | - [JavaScript Weekly](http://javascriptweekly.com/) 1613 | - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) 1614 | - [Bocoup Weblog](http://weblog.bocoup.com/) 1615 | - [Adequately Good](http://www.adequatelygood.com/) 1616 | - [NCZOnline](http://www.nczonline.net/) 1617 | - [Perfection Kills](http://perfectionkills.com/) 1618 | - [Ben Alman](http://benalman.com/) 1619 | - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) 1620 | - [Dustin Diaz](http://dustindiaz.com/) 1621 | - [nettuts](http://net.tutsplus.com/?s=javascript) 1622 | 1623 | **Podcasts** 1624 | 1625 | - [JavaScript Jabber](http://devchat.tv/js-jabber/) 1626 | 1627 | 1628 | **[⬆ back to top](#table-of-contents)** 1629 | 1630 | ## In the Wild 1631 | 1632 | This is a list of organizations that are using this style guide. Send us a pull request or open an issue and we'll add you to the list. 1633 | 1634 | - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) 1635 | - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) 1636 | - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) 1637 | - **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript) 1638 | - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) 1639 | - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) 1640 | - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) 1641 | - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) 1642 | - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) 1643 | - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) 1644 | - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) 1645 | - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) 1646 | - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) 1647 | - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) 1648 | - **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) 1649 | - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) 1650 | - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) 1651 | - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) 1652 | - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) 1653 | - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) 1654 | - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) 1655 | - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) 1656 | - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) 1657 | - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) 1658 | - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) 1659 | - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) 1660 | - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) 1661 | - **Muber**: [muber/javascript](https://github.com/muber/javascript) 1662 | - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) 1663 | - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) 1664 | - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) 1665 | - **Nordic Venture Family**: [CodeDistillery/javascript](https://github.com/CodeDistillery/javascript) 1666 | - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) 1667 | - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) 1668 | - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) 1669 | - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) 1670 | - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) 1671 | - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) 1672 | - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) 1673 | - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) 1674 | - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) 1675 | - **Target**: [target/javascript](https://github.com/target/javascript) 1676 | - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) 1677 | - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) 1678 | - **Userify**: [userify/javascript](https://github.com/userify/javascript) 1679 | - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) 1680 | - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) 1681 | - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) 1682 | - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) 1683 | 1684 | ## Translation 1685 | 1686 | This style guide is also available in other languages: 1687 | 1688 | - ![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) 1689 | - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) 1690 | - ![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) 1691 | - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) 1692 | - ![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) 1693 | - ![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) 1694 | - ![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) 1695 | - ![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) 1696 | - ![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) 1697 | - ![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) 1698 | - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) 1699 | - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [uprock/javascript](https://github.com/uprock/javascript) 1700 | - ![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) 1701 | - ![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) 1702 | 1703 | ## The JavaScript Style Guide Guide 1704 | 1705 | - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) 1706 | 1707 | ## Chat With Us About JavaScript 1708 | 1709 | - Find us on [gitter](https://gitter.im/airbnb/javascript). 1710 | 1711 | ## Contributors 1712 | 1713 | - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) 1714 | 1715 | 1716 | ## License 1717 | 1718 | (The MIT License) 1719 | 1720 | Copyright (c) 2014 Airbnb 1721 | 1722 | Permission is hereby granted, free of charge, to any person obtaining 1723 | a copy of this software and associated documentation files (the 1724 | 'Software'), to deal in the Software without restriction, including 1725 | without limitation the rights to use, copy, modify, merge, publish, 1726 | distribute, sublicense, and/or sell copies of the Software, and to 1727 | permit persons to whom the Software is furnished to do so, subject to 1728 | the following conditions: 1729 | 1730 | The above copyright notice and this permission notice shall be 1731 | included in all copies or substantial portions of the Software. 1732 | 1733 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 1734 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1735 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1736 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 1737 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 1738 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 1739 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1740 | 1741 | **[⬆ back to top](#table-of-contents)** 1742 | 1743 | # }; 1744 | -------------------------------------------------------------------------------- /linters/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "plugins": [ 4 | "react" 5 | ], 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "ecmaFeatures": { 11 | "arrowFunctions": true, 12 | "blockBindings": true, 13 | "classes": true, 14 | "defaultParams": true, 15 | "destructuring": true, 16 | "forOf": true, 17 | "generators": false, 18 | "modules": true, 19 | "objectLiteralComputedProperties": true, 20 | "objectLiteralDuplicateProperties": false, 21 | "objectLiteralShorthandMethods": true, 22 | "objectLiteralShorthandProperties": true, 23 | "spread": true, 24 | "superInFunctions": true, 25 | "templateStrings": true, 26 | "jsx": true 27 | }, 28 | "rules": { 29 | /** 30 | * Strict mode 31 | */ 32 | // babel inserts "use strict"; for us 33 | // http://eslint.org/docs/rules/strict 34 | "strict": [2, "never"], 35 | 36 | /** 37 | * ES6 38 | */ 39 | "no-var": 2, // http://eslint.org/docs/rules/no-var 40 | 41 | /** 42 | * Variables 43 | */ 44 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow 45 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 46 | "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars 47 | "vars": "local", 48 | "args": "after-used" 49 | }], 50 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 51 | 52 | /** 53 | * Possible errors 54 | */ 55 | "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle 56 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign 57 | "no-console": 1, // http://eslint.org/docs/rules/no-console 58 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger 59 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert 60 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition 61 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 62 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 63 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 64 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 65 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 66 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 67 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 68 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 69 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 70 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 71 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 72 | "no-reserved-keys": 2, // http://eslint.org/docs/rules/no-reserved-keys 73 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 74 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 75 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 76 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 77 | 78 | /** 79 | * Best practices 80 | */ 81 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 82 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 83 | "default-case": 2, // http://eslint.org/docs/rules/default-case 84 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 85 | "allowKeywords": true 86 | }], 87 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 88 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 89 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 90 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 91 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 92 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 93 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 94 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 95 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 96 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 97 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 98 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 99 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 100 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 101 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 102 | "no-new": 2, // http://eslint.org/docs/rules/no-new 103 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 104 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 105 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 106 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 107 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign 108 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 109 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 110 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 111 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 112 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 113 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 114 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 115 | "no-with": 2, // http://eslint.org/docs/rules/no-with 116 | "radix": 2, // http://eslint.org/docs/rules/radix 117 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top 118 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 119 | "yoda": 2, // http://eslint.org/docs/rules/yoda 120 | 121 | /** 122 | * Style 123 | */ 124 | "indent": [2, 2], // http://eslint.org/docs/rules/ 125 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 126 | "1tbs", { 127 | "allowSingleLine": true 128 | }], 129 | "quotes": [ 130 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 131 | ], 132 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 133 | "properties": "never" 134 | }], 135 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 136 | "before": false, 137 | "after": true 138 | }], 139 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 140 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 141 | "func-names": 1, // http://eslint.org/docs/rules/func-names 142 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 143 | "beforeColon": false, 144 | "afterColon": true 145 | }], 146 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 147 | "newIsCap": true 148 | }], 149 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines 150 | "max": 2 151 | }], 152 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary 153 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 154 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 155 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 156 | "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func 157 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 158 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 159 | "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks 160 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi 161 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 162 | "before": false, 163 | "after": true 164 | }], 165 | "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords 166 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 167 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 168 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 169 | "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case 170 | "spaced-line-comment": 2, // http://eslint.org/docs/rules/spaced-line-comment 171 | 172 | /** 173 | * JSX style 174 | */ 175 | "react/display-name": 0, 176 | "react/jsx-boolean-value": 2, 177 | "react/jsx-quotes": [2, "double"], 178 | "react/jsx-no-undef": 2, 179 | "react/jsx-sort-props": 0, 180 | "react/jsx-sort-prop-types": 0, 181 | "react/jsx-uses-react": 2, 182 | "react/jsx-uses-vars": 2, 183 | "react/no-did-mount-set-state": [2, "allow-in-func"], 184 | "react/no-did-update-set-state": 2, 185 | "react/no-multi-comp": 2, 186 | "react/no-unknown-property": 2, 187 | "react/prop-types": 2, 188 | "react/react-in-jsx-scope": 2, 189 | "react/self-closing-comp": 2, 190 | "react/wrap-multilines": 2, 191 | "react/sort-comp": [2, { 192 | "order": [ 193 | "displayName", 194 | "propTypes", 195 | "contextTypes", 196 | "childContextTypes", 197 | "mixins", 198 | "statics", 199 | "defaultProps", 200 | "getDefaultProps", 201 | "getInitialState", 202 | "getChildContext", 203 | "componentWillMount", 204 | "componentDidMount", 205 | "componentWillReceiveProps", 206 | "shouldComponentUpdate", 207 | "componentWillUpdate", 208 | "componentDidUpdate", 209 | "componentWillUnmount", 210 | "/^on.+$/", 211 | "/^get.+$/", 212 | "/^render.+$/", 213 | "render" 214 | ] 215 | }] 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /linters/README.md: -------------------------------------------------------------------------------- 1 | ## `.eslintrc` 2 | 3 | Our `.eslintrc` requires the following NPM packages packages: 4 | 5 | - `eslint` 6 | - `babel-eslint` 7 | - `eslint-plugin-react` 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 80 characters 38 | "maxlen": 80, 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 | -------------------------------------------------------------------------------- /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/index.js: -------------------------------------------------------------------------------- 1 | var resolve = require('resolve'); 2 | var stripComments = require('strip-json-comments'); 3 | var fs = require('fs'); 4 | 5 | // you could do this all at once if you wanted to look cool 6 | var filename = resolve.sync('airbnb-style/linters/.eslintrc'); 7 | var data = fs.readFileSync(filename, {encoding: 'utf-8'}); 8 | var dataWithoutComments = stripComments(data); 9 | var parsed = JSON.parse(dataWithoutComments); 10 | 11 | module.exports = parsed; 12 | -------------------------------------------------------------------------------- /packages/eslint-config-airbnb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-airbnb", 3 | "version": "0.0.6", 4 | "description": "Airbnb's ESLint config, following our styleguide", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/airbnb/javascript" 12 | }, 13 | "keywords": [ 14 | "eslint", 15 | "eslintconfig", 16 | "config", 17 | "airbnb", 18 | "javascript", 19 | "styleguide" 20 | ], 21 | "author": "Jake Teton-Landis (https://twitter.com/@jitl)", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/airbnb/javascript/issues" 25 | }, 26 | "homepage": "https://github.com/airbnb/javascript", 27 | "dependencies": { 28 | "airbnb-style": "2.0.0", 29 | "babel-eslint": "3.1.7", 30 | "eslint": "0.21.2", 31 | "eslint-plugin-react": "2.3.0", 32 | "resolve": "1.1.6", 33 | "strip-json-comments": "1.0.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /react/README.md: -------------------------------------------------------------------------------- 1 | # Airbnb React/JSX Style Guide 2 | 3 | *A mostly reasonable approach to React and JSX* 4 | 5 | ## Table of Contents 6 | 7 | 1. [Basic Rules](#basic-rules) 8 | 1. [Naming](#naming) 9 | 1. [Declaration](#declaration) 10 | 1. [Alignment](#alignment) 11 | 1. [Quotes](#quotes) 12 | 1. [Spacing](#spacing) 13 | 1. [Props](#props) 14 | 1. [Parentheses](#parentheses) 15 | 1. [Tags](#tags) 16 | 1. [Methods](#methods) 17 | 1. [Ordering](#ordering) 18 | 19 | ## Basic Rules 20 | 21 | - Only include one React component per file. 22 | - Always use JSX syntax. 23 | - Do not use `React.createElement` unless you're initializing the app from a file that does not transform JSX. 24 | 25 | ## Naming 26 | 27 | - **Extensions**: Use `.js` extension for React components. 28 | - **Filename**: Use PascalCase for filenames. E.g., `ReservationCard.js`. 29 | - **Reference Naming**: Use PascalCase for React components and camelCase for their instances: 30 | ```javascript 31 | // bad 32 | const reservationCard = require('./ReservationCard'); 33 | 34 | // good 35 | const ReservationCard = require('./ReservationCard'); 36 | 37 | // bad 38 | const ReservationItem = ; 39 | 40 | // good 41 | const reservationItem = ; 42 | ``` 43 | 44 | **Component Naming**: Use the filename as the component name. So `ReservationCard.js` should have a reference name of ReservationCard. However, for root components of a directory, use index.js as the filename and use the directory name as the component name: 45 | ```javascript 46 | // bad 47 | const Footer = require('./Footer/Footer.js') 48 | 49 | // bad 50 | const Footer = require('./Footer/index.js') 51 | 52 | // good 53 | const Footer = require('./Footer') 54 | ``` 55 | 56 | 57 | ## Declaration 58 | - Do not use displayName for naming components, instead name the component by reference. 59 | 60 | ```javascript 61 | // bad 62 | export default React.createClass({ 63 | displayName: 'ReservationCard', 64 | // stuff goes here 65 | }); 66 | 67 | // good 68 | const ReservationCard = React.createClass({ 69 | // stuff goes here 70 | }); 71 | export default ReservationCard; 72 | ``` 73 | 74 | ## Alignment 75 | - Follow these alignment styles for js syntax 76 | 77 | ```javascript 78 | // bad 79 | 81 | 82 | // good 83 | 87 | 88 | // if props fit in one line then keep it on the same line 89 | 90 | 91 | // children get indented normally 92 | 96 | 97 | 98 | ``` 99 | 100 | ## Quotes 101 | - Always use double quotes (`"`) for JSX attributes, but single quotes for all other JavaScript. 102 | ```javascript 103 | // bad 104 | 105 | 106 | // good 107 | 108 | 109 | // bad 110 | 111 | 112 | // good 113 | 114 | ``` 115 | 116 | ## Spacing 117 | - Always include a single space in your self-closing tag. 118 | ```javascript 119 | // bad 120 | 121 | 122 | // very bad 123 | 124 | 125 | // bad 126 | 128 | 129 | // good 130 | 131 | ``` 132 | 133 | ## Props 134 | - Always use camelCase for prop names. 135 | ```javascript 136 | // bad 137 | 141 | 142 | // good 143 | 147 | ``` 148 | 149 | ## Parentheses 150 | - Wrap JSX tags in parentheses when they span more than one line: 151 | ```javascript 152 | /// bad 153 | render() { 154 | return 155 | 156 | ; 157 | } 158 | 159 | // good 160 | render() { 161 | return ( 162 | 163 | 164 | 165 | ); 166 | } 167 | 168 | // good, when single line 169 | render() { 170 | const body =
    hello
    ; 171 | return {body}; 172 | } 173 | ``` 174 | 175 | ## Tags 176 | - Always self-close tags that have no children. 177 | ```javascript 178 | // bad 179 | 180 | 181 | // good 182 | 183 | ``` 184 | 185 | - If your component has multi-line properties, close its tag on a new line. 186 | ```javascript 187 | // bad 188 | 191 | 192 | // good 193 | 197 | ``` 198 | 199 | ## Methods 200 | - Do not use underscore prefix for internal methods of a react component. 201 | ```javascript 202 | // bad 203 | React.createClass({ 204 | _onClickSubmit() { 205 | // do stuff 206 | } 207 | 208 | // other stuff 209 | }); 210 | 211 | // good 212 | React.createClass({ 213 | onClickSubmit() { 214 | // do stuff 215 | } 216 | 217 | // other stuff 218 | }); 219 | ``` 220 | 221 | ## Ordering 222 | - Always follow the following order for methods in a react component: 223 | 224 | 1. displayName 225 | 2. mixins (as of React v0.13 mixins are deprecated) 226 | 3. statics 227 | 4. propTypes 228 | 5. getDefaultProps 229 | 6. getInitialState 230 | 7. componentWillMount 231 | 8. componentDidMount 232 | 9. componentWillReceiveProps 233 | 10. shouldComponentUpdate 234 | 11. componentWillUpdate 235 | 12. componentDidUpdate 236 | 13. componentWillUnmount 237 | 14. *clickHandlers or eventHandlers* like onClickSubmit() or onChangeDescription() 238 | 15. *getter methods for render* like getSelectReason() or getFooterContent() 239 | 16. *Optional render methods* like renderNavigation() or renderProfilePicture() 240 | 17. render 241 | 242 | **[⬆ back to top](#table-of-contents)** 243 | --------------------------------------------------------------------------------