diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..eb96531fe5 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + extends: [ + './configs/base', + ].map(require.resolve) +}; diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..99ade442e7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,25 @@ +#### What does this PR do? +#### Where should the reviewer start? +#### How has this been tested? +#### Any background context you want to provide? +#### What are the relevant tickets? +#### How will this be deployed? +#### What picture best describes this PR (optional but encouraged)? +#### Is there a link to personal staging environment? + +#### Developer Done List +- [ ] Tests Added/Updated +- [ ] Updated README.md/RUNBOOK.md +- [ ] Verified backward compatible +- [ ] Verified database migrations will not be catastrophic +- [ ] Considered Security, Availability and Confidentiality + +#### For the Reviewer: +#### By approving this PR, the reviewer acknowledges that they have checked all items in this done list. + +#### Reviewer/Approval Done List +- [ ] Tests Pass Locally +- [ ] CI Build Passes +- [ ] Verified README.md/RUNBOOK.md is updated +- [ ] Verified changes are backward compatible +- [ ] Reviewed impact to Security, Availability and Confidentiality (if issue found, add comments and request changes) \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9cff3a7873..fe46900ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ -.idea* +# gitignore + +node_modules + +# Only apps should have lockfiles +yarn.lock +package-lock.json + node_modules/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..0cc8a61437 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Cloudability + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0216ee8f05..5ddff87092 100644 --- a/README.md +++ b/README.md @@ -1,2232 +1,46 @@ -# DataHero (Airbnb) JavaScript Style Guide() { +# Cloudability JavaScript Style Guide() { -*A Hero's guide to heroic JavaScript* +Based off the [airbnb javascript style guide](https://github.com/airbnb/javascript). -[](https://www.npmjs.com/package/eslint-config-airbnb) -[](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +## Installation -[For the ES5-only guide click here](es5/). +First install dependent packages and this package with the correct naming. -## Table of Contents +``` +yarn add -D -E \ + eslint-plugin-cloudability@git://github.com/cloudability/javascript-style-guide.git#v1.0.0 \ + eslint@6 +``` - 1. [Types](#types) - 1. [References](#references) - 1. [Objects](#objects) - 1. [Arrays](#arrays) - 1. [Destructuring](#destructuring) - 1. [Strings](#strings) - 1. [Functions](#functions) - 1. [Arrow Functions](#arrow-functions) - 1. [Constructors](#constructors) - 1. [Modules](#modules) - 1. [Iterators and Generators](#iterators-and-generators) - 1. [Properties](#properties) - 1. [Variables](#variables) - 1. [Hoisting](#hoisting) - 1. [Comparison Operators & Equality](#comparison-operators--equality) - 1. [Blocks](#blocks) - 1. [Comments](#comments) - 1. [Whitespace](#whitespace) - 1. [Commas](#commas) - 1. [Semicolons](#semicolons) - 1. [Type Casting & Coercion](#type-casting--coercion) - 1. [Naming Conventions](#naming-conventions) - 1. [Accessors](#accessors) - 1. [Events](#events) - 1. [jQuery](#jquery) - 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility) - 1. [ECMAScript 6 Styles](#ecmascript-6-styles) - 1. [Testing](#testing) - 1. [Performance](#performance) - 1. [Resources](#resources) - 1. [In the Wild](#in-the-wild) - 1. [Translation](#translation) - 1. [The JavaScript Style Guide Guide](#the-javascript-style-guide-guide) - 1. [Chat With Us About Javascript](#chat-with-us-about-javascript) - 1. [Contributors](#contributors) - 1. [License](#license) +In a `.eslintrc.js` at root or any other relevant location, add a `plugin:cloudability` config to the `extends` array. For example: -## Types +``` +module.exports = { + extends: [ + 'plugin:cloudability/base', + ], +} +``` - - [1.1](#1.1) **Primitives**: When you access a primitive type you work directly on its value. +Besides `base`, the other configs are `gui|guitTest|service|typescript`. - + `string` - + `number` - + `boolean` - + `null` - + `undefined` +#### Use with Typescript - ```javascript - const foo = 1; - let bar = foo; +`yarn add -D -E typescript` - bar = 9; +Make sure the config being used is the `cloudability/typescript` one. - console.log(foo, bar); // => 1, 9 - ``` - - [1.2](#1.2) **Complex**: When you access a complex type you work on a reference to its value. +#### Use in VSCode - + `object` - + `array` - + `function` +In VSCode's settings.json, add - ```javascript - const foo = [1, 2]; - const bar = foo; - - bar[0] = 9; - - console.log(foo[0], bar[0]); // => 9, 9 - ``` - -**[⬆ back to top](#table-of-contents)** - -## References - - - [2.1](#2.1) Use `const` for all of your references; avoid using `var`. - - > Why? This ensures that you can't reassign your references (mutation), which can lead to bugs and difficult to comprehend code. - - ```javascript - // bad - var a = 1; - var b = 2; - - // good - const a = 1; - const b = 2; - ``` - - - [2.2](#2.2) If you must mutate references, use `let` instead of `var`. - - > Why? `let` is block-scoped rather than function-scoped like `var`. - - ```javascript - // bad - var count = 1; - if (true) { - count += 1; - } - - // good, use the let. - let count = 1; - if (true) { - count += 1; - } - ``` - - - [2.3](#2.3) Note that both `let` and `const` are block-scoped. - - ```javascript - // const and let only exist in the blocks they are defined in. - { - let a = 1; - const b = 1; - } - console.log(a); // ReferenceError - console.log(b); // ReferenceError - ``` - -**[⬆ back to top](#table-of-contents)** - -## Objects - - - [3.1](#3.1) Use the literal syntax for object creation. - - ```javascript - // bad - const item = new Object(); - - // good - const item = {}; - ``` - - - [3.2](#3.2) If your code will be executed in browsers in script context, 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). It’s OK to use them in ES6 modules and server-side code. - - ```javascript - // bad - const superman = { - default: { clark: 'kent' }, - private: true, - }; - - // good - const superman = { - defaults: { clark: 'kent' }, - hidden: true, - }; - ``` - - - [3.3](#3.3) Use readable synonyms in place of reserved words. - - ```javascript - // bad - const superman = { - class: 'alien', - }; - - // bad - const superman = { - klass: 'alien', - }; - - // good - const superman = { - type: 'alien', - }; - ``` - - - - [3.4](#3.4) Use computed property names when creating objects with dynamic property names. - - > Why? They allow you to define all the properties of an object in one place. - - ```javascript - - function getKey(k) { - return `a key named ${k}`; - } - - // bad - const obj = { - id: 5, - name: 'San Francisco', - }; - obj[getKey('enabled')] = true; - - // good - const obj = { - id: 5, - name: 'San Francisco', - [getKey('enabled')]: true, - }; - ``` - - - - [3.5](#3.5) Use object method shorthand. - - ```javascript - // bad - const atom = { - value: 1, - - addValue: function (value) { - return atom.value + value; - }, - }; - - // good - const atom = { - value: 1, - - addValue(value) { - return atom.value + value; - }, - }; - ``` - - - - [3.6](#3.6) Use property value shorthand. - - > Why? It is shorter to write and descriptive. - - ```javascript - const lukeSkywalker = 'Luke Skywalker'; - - // bad - const obj = { - lukeSkywalker: lukeSkywalker, - }; - - // good - const obj = { - lukeSkywalker, - }; - ``` - - - [3.7](#3.7) Group your shorthand properties at the beginning of your object declaration. - - > Why? It's easier to tell which properties are using the shorthand. - - ```javascript - const anakinSkywalker = 'Anakin Skywalker'; - const lukeSkywalker = 'Luke Skywalker'; - - // bad - const obj = { - episodeOne: 1, - twoJediWalkIntoACantina: 2, - lukeSkywalker, - episodeThree: 3, - mayTheFourth: 4, - anakinSkywalker, - }; - - // good - const obj = { - lukeSkywalker, - anakinSkywalker, - episodeOne: 1, - twoJediWalkIntoACantina: 2, - episodeThree: 3, - mayTheFourth: 4, - }; - ``` - -**[⬆ back to top](#table-of-contents)** - -## Arrays - - - [4.1](#4.1) Use the literal syntax for array creation. - - ```javascript - // bad - const items = new Array(); - - // good - const items = []; - ``` - - - [4.2](#4.2) Use Array#push instead of direct assignment to add items to an array. - - ```javascript - const someStack = []; - - // bad - someStack[someStack.length] = 'abracadabra'; - - // good - someStack.push('abracadabra'); - ``` - - - - [4.3](#4.3) Use array spreads `...` to copy arrays. - - ```javascript - // bad - const len = items.length; - const itemsCopy = []; - let i; - - for (i = 0; i < len; i++) { - itemsCopy[i] = items[i]; - } - - // performance trick: use slice http://jsperf.com/converting-arguments-to-an-array/7 - itemsCopy = items.slice(); - - // best - itemsCopy = _.clone(items); // or _.cloneDeep(), as needed - - // good - const itemsCopy = [...items]; - - ``` - - [4.4](#4.4) To convert an array-like object to an array, use Array#from. - - ```javascript - const foo = document.querySelectorAll('.foo'); - const nodes = Array.from(foo); - ``` - -**[⬆ back to top](#table-of-contents)** - -## Destructuring - - - [5.1](#5.1) Use object destructuring when accessing and using multiple properties of an object. - - > Why? Destructuring saves you from creating temporary references for those properties. - - ```javascript - // bad - function getFullName(user) { - const firstName = user.firstName; - const lastName = user.lastName; - - return `${firstName} ${lastName}`; - } - - // good - function getFullName(obj) { - const { firstName, lastName } = obj; - return `${firstName} ${lastName}`; - } - - // best - function getFullName({ firstName, lastName }) { - return `${firstName} ${lastName}`; - } - ``` - - - [5.2](#5.2) Use array destructuring. - - ```javascript - const arr = [1, 2, 3, 4]; - - // bad - const first = arr[0]; - const second = arr[1]; - - // good - const [first, second] = arr; - ``` - - - [5.3](#5.3) Use object destructuring for multiple return values, not array destructuring. - - > Why? You can add new properties over time or change the order of things without breaking call sites. - - ```javascript - // bad - function processInput(input) { - // then a miracle occurs - return [left, right, top, bottom]; - } - - // the caller needs to think about the order of return data - const [left, __, top] = processInput(input); - - // good - function processInput(input) { - // then a miracle occurs - return { left, right, top, bottom }; - } - - // the caller selects only the data they need - const { left, right } = processInput(input); - ``` - - -**[⬆ back to top](#table-of-contents)** - -## Strings - - - [6.1](#6.1) Use single quotes `''` for strings. - - ```javascript - // bad - const name = "Capt. Janeway"; - - // good - const name = 'Capt. Janeway'; - ``` - - - [6.2](#6.2) Strings longer than 120 characters should be written across multiple lines using ES6 multi-line strings or regular string concatenation. - - [6.3](#6.3) 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). - - - ```javascript - // bad - 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.'; - - // bad - 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.'; - - // good - 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.'; - - // better, if ES6 environment or transpiler available (ES6 multiline strings) - 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.`; - ``` - - - - - [6.4](#6.4) When programmatically building up strings, use template strings instead of concatenation. - - > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features. - - ```javascript - // bad - function sayHi(name) { - return 'How are you, ' + name + '?'; - } - - // bad - function sayHi(name) { - return ['How are you, ', name, '?'].join(); - } - - // good - - function sayHi(name) { - return `How are you, ${name}?`; - } - ``` - - [6.5](#6.5) Never use eval() on a string, it opens too many vulnerabilities. - -**[⬆ back to top](#table-of-contents)** - - -## Functions - - - [7.1](#7.1) Use function declarations instead of function expressions. - - > 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. - - ```javascript - // anonymous function expression - // (fine for quick inline functions, but naming your function makes better stack traces) - var anonymous = function() { - return true; - }; - - // named function expression - // ('named' shows up in your stack traces) - var named = function named() { - return true; - }; - - // bad - const foo = function () { - }; - - // good - function foo() { - } - ``` - - - [7.2](#7.2) Function expressions: - - ```javascript - // immediately-invoked function expression (IIFE) - (() => { - console.log('Welcome to the Internet. Please follow me.'); - })(); - ``` - - - - [7.3](#7.3) 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). - - - ```javascript - // bad - if (currentUser) { - function test() { - console.log('Nope.'); - } - } - - // good - let test; - if (currentUser) { - test = () => { - console.log('Yup.'); - }; - } - ``` - - - [7.5](#7.5) Never name a parameter `arguments`. This will take precedence over the `arguments` object that is given to every function scope. - - ```javascript - // bad - function nope(name, options, arguments) { - // ...stuff... - } - - // good - function yup(name, options, args) { - // ...stuff... - } - ``` - - - - [7.6](#7.6) Never use `arguments`, opt to use rest syntax `...` instead. - - > Why? `...` is explicit about which arguments you want pulled. Plus rest arguments are a real Array and not Array-like like `arguments`. - - ```javascript - // bad - function concatenateAll() { - const args = Array.prototype.slice.call(arguments); - return args.join(''); - } - - // good - function concatenateAll(...args) { - return args.join(''); - } - ``` - - - - [7.7](#7.7) Use default parameter syntax rather than mutating function arguments. - - ```javascript - // really bad - function handleThings(opts) { - // No! We shouldn't mutate function arguments. - // Double bad: if opts is falsy it'll be set to an object which may - // be what you want but it can introduce subtle bugs. - opts = opts || {}; - // ... - } - - // still bad - function handleThings(opts) { - if (opts === void 0) { - opts = {}; - } - // ... - } - - // good - function handleThings(opts = {}) { - // ... - } - ``` - - - [7.8](#7.8) Avoid side effects with default parameters - - > Why? They are confusing to reason about. - - ```javascript - var b = 1; - // bad - function count(a = b++) { - console.log(a); - } - count(); // 1 - count(); // 2 - count(3); // 3 - count(); // 3 - ``` - -- [7.9](#7.9) Never use the Function constructor to create a new function. - - > Why? Creating a function in this way evaluates a string similarly to eval(), which opens vulnerabilities. - - ```javascript - // bad - var add = new Function('a', 'b', 'return a + b'); - - // still bad - var subtract = Function('a', 'b', 'return a - b'); - ``` - -**[⬆ back to top](#table-of-contents)** - -## Arrow Functions - - - [8.1](#8.1) When you must use function expressions (as when passing an anonymous function), use arrow function notation. - - > 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. - - > Why not? If you have a fairly complicated function, you might move that logic out into its own function declaration. - - ```javascript - // bad - [1, 2, 3].map(function (x) { - return x * x; - }); - - // good - [1, 2, 3].map((x) => { - return x * x; - }); - ``` - - - [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. - - > Why? Syntactic sugar. It reads well when multiple functions are chained together. - - > Why not? If you plan on returning an object. - - ```javascript - // good - [1, 2, 3].map(x => x * x); - - // good - [1, 2, 3].reduce((total, n) => { - return total + n; - }, 0); - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Constructors - - - [9.1](#9.1) Always use `class`. Avoid manipulating `prototype` directly. - - > Why? `class` syntax is more concise and easier to reason about. - - ```javascript - // bad - function Queue(contents = []) { - this._queue = [...contents]; - } - Queue.prototype.pop = function() { - const value = this._queue[0]; - this._queue.splice(0, 1); - return value; - } - - - // good - class Queue { - constructor(contents = []) { - this._queue = [...contents]; - } - pop() { - const value = this._queue[0]; - this._queue.splice(0, 1); - return value; - } - } - ``` - - - [9.2](#9.2) Use `extends` for inheritance. - - > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`. - - ```javascript - // bad - const inherits = require('inherits'); - function PeekableQueue(contents) { - Queue.apply(this, contents); - } - inherits(PeekableQueue, Queue); - PeekableQueue.prototype.peek = function() { - return this._queue[0]; - } - - // good - class PeekableQueue extends Queue { - peek() { - return this._queue[0]; - } - } - ``` - - - [9.3](#9.3) Methods can return `this` to help with method chaining. - - ```javascript - // bad - Jedi.prototype.jump = function() { - this.jumping = true; - return true; - }; - - Jedi.prototype.setHeight = function(height) { - this.height = height; - }; - - const luke = new Jedi(); - luke.jump(); // => true - luke.setHeight(20); // => undefined - - // good - class Jedi { - jump() { - this.jumping = true; - return this; - } - - setHeight(height) { - this.height = height; - return this; - } - } - - const luke = new Jedi(); - - luke.jump() - .setHeight(20); - ``` - - - - [9.4](#9.4) It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects. - - ```javascript - class Jedi { - constructor(options = {}) { - this.name = options.name || 'no name'; - } - - getName() { - return this.name; - } - - toString() { - return `Jedi - ${this.getName()}`; - } - } - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Modules - - - [10.1](#10.1) Always use modules (`import`/`export`) over a non-standard module system. You can always transpile to your preferred module system. - - > Why? Modules are the future, let's start using the future now. - - ```javascript - // bad - const AirbnbStyleGuide = require('./AirbnbStyleGuide'); - module.exports = AirbnbStyleGuide.es6; - - // ok - import AirbnbStyleGuide from './AirbnbStyleGuide'; - export default AirbnbStyleGuide.es6; - - // best - import { es6 } from './AirbnbStyleGuide'; - export default es6; - ``` - - - [10.2](#10.2) Do not use wildcard imports. - - > Why? This makes sure you have a single default export. - - ```javascript - // bad - import * as AirbnbStyleGuide from './AirbnbStyleGuide'; - - // good - import AirbnbStyleGuide from './AirbnbStyleGuide'; - ``` - - - [10.3](#10.3) And do not export directly from an import. - - > Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent. - - ```javascript - // bad - // filename es6.js - export { es6 as default } from './airbnbStyleGuide'; - - // good - // filename es6.js - import { es6 } from './AirbnbStyleGuide'; - export default es6; - ``` - -**[⬆ back to top](#table-of-contents)** - -## Iterators and Generators - - - [11.1](#11.1) Don't use iterators. Prefer JavaScript's higher-order functions like `map()` and `reduce()` instead of loops like `for-of`. - - > Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side-effects. - - ```javascript - const numbers = [1, 2, 3, 4, 5]; - - // bad - let sum = 0; - for (let num of numbers) { - sum += num; - } - - sum === 15; - - // good - let sum = 0; - numbers.forEach((num) => sum += num); - sum === 15; - - // best (use the functional force) - const sum = numbers.reduce((total, num) => total + num, 0); - sum === 15; - ``` - - - [11.2](#11.2) Don't use generators for now. - - > Why? They don't transpile well to ES5. - -**[⬆ back to top](#table-of-contents)** - - -## Properties - - - [12.1](#12.1) Use dot notation when accessing properties. - - ```javascript - const luke = { - jedi: true, - age: 28, - }; - - // bad - const isJedi = luke['jedi']; - - // good - var isJedi = luke.jedi; - - // exception: sometimes attributes contain illegal characters like '-'. - // In those cases, the subscript notation is a necessary evil. - var fontSize = awkwardObject['font-size']; - ``` - - - [12.2](#12.2) Use subscript notation `[]` when accessing properties with a variable. - - ```javascript - const luke = { - jedi: true, - age: 28, - }; - - function getProp(prop) { - return luke[prop]; - } - - const isJedi = getProp('jedi'); - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Variables - - - [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. - - ```javascript - // bad - superPower = new SuperPower(); - - // good - const superPower = new SuperPower(); - ``` - - - [13.2](#13.2) Use one `const` declaration per variable. - - > 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. - - ```javascript - // bad - const items = getItems(), - goSportsTeam = true, - dragonball = 'z'; - - // bad - // (compare to above, and try to spot the mistake) - const items = getItems(), - goSportsTeam = true; - dragonball = 'z'; - - // good - var dragonball = 'z'; - var goSportsTeam = true; - var items = getItems(); - ``` - - - [13.3](#13.3) Group all your `const`s and then group all your `let`s. - - > Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. - - ```javascript - // bad - let i, len, dragonball, - items = getItems(), - goSportsTeam = true; - - // bad - let i; - const items = getItems(); - let dragonball; - const goSportsTeam = true; - let len; - - // good - const goSportsTeam = true; - const items = getItems(); - let dragonball; - let i; - let length; - ``` - - - Module dependencies go at the top of the file and ordered as follows: - - Depedencies as sections ordered by type: - - Libraries (lodash, react, etc.) - - Module type (datastores then components, or business logic then models) - - Templates last - - Each section is alphabetical - - - Assign variables where they are used, not at the top like in the old days of C. This makes it easier to read, and the linter enforces block-scope, therefore avoiding common gotchas caused by variable hoisting. - - - [13.4](#13.4) Assign variables where you need them, but place them in a reasonable place. - - > Why? `let` and `const` are block scoped and not function scoped. - - ```javascript - // good - function() { - var name; - var age; - var classes; - - //..other stuff.. - - const name = getName(); - - if (name === 'test') { - return false; - } - - age = getAge(); - - if (age < 3) { - return false; - } - - classes = getClasses(); - ... - - return name; - } - - - // bad - // normally this would be valid-but-problematic JS because of hoisting, but the linter - // enforces block scope, preventing you from running this gotcha. - function(name) { - if (isNickname(name)) { - var fullName = getFull(name); // this part is fine - console.log('valid reference:', fullName); - } - - // valid JS and common hoisting gotcha, but linter blocks and says fullName is being used out of scope: - console.log('valid JS but a common hoisting gotcha:', fullName); -// LINTER ERROR: ^ 'fullName' used out of scope. - // good - function(hasName) { - if (!hasName) { - return false; - } - - const name = getName(); - this.setFirstName(name); - - return true; - } - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Hoisting - - - [14.1](#14.1) `var` declarations get hoisted to the top of their scope, their assignment does not. `const` and `let` declarations are blessed with a new concept called [Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let). It's important to know why [typeof is no longer safe](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15). - - ```javascript - // we know this wouldn't work (assuming there - // is no notDefined global variable) - function example() { - console.log(notDefined); // => throws a ReferenceError - } - - // creating a variable declaration after you reference the variable will work due to - // variable hoisting. - // Note: the assignment value of `true` is not hoisted. - function example() { - console.log(declaredButNotAssigned); // => undefined - var declaredButNotAssigned = true; - // LINTER ERROR: ^ 'declaredButNotAssigned' was used before it was defined. - } - - // The interpreter is hoisting the variable declaration to the top of the scope, - // which means our example could be rewritten as: - function example() { - let declaredButNotAssigned; - console.log(declaredButNotAssigned); // => undefined - declaredButNotAssigned = true; - } - - // using const and let - function example() { - console.log(declaredButNotAssigned); // => throws a ReferenceError - console.log(typeof declaredButNotAssigned); // => throws a ReferenceError - const declaredButNotAssigned = true; - } - ``` - - - [14.2](#14.2) Anonymous function expressions hoist their variable name, but not the function assignment. - - ```javascript - function example() { - console.log(anonymous); // => undefined - - anonymous(); // => TypeError anonymous is not a function - - var anonymous = function() { - //LINTER ERROR: ^ 'anonymous' was used before it was defined. - console.log('anonymous function expression'); - }; - } - ``` - - - [14.3](#14.3) Named function expressions hoist the variable name, not the function name or the function body. - - ```javascript - function example() { - console.log(named); // => undefined - - named(); // => TypeError named is not a function - - superPower(); // => ReferenceError superPower is not defined - - var named = function superPower() { - console.log('Flying'); - }; - } - - // the same is true when the function name - // is the same as the variable name. - function example() { - console.log(named); // => undefined - - named(); // => TypeError named is not a function - - var named = function named() { - console.log('named'); - } - } - ``` - - - [14.4](#14.4) Function declarations hoist their name and the function body. - - ```javascript - function example() { - superPower(); // => Flying - - function superPower() { - console.log('Flying'); - } - } - ``` - - - 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/). - -**[⬆ back to top](#table-of-contents)** - - -## Comparison Operators & Equality - - - [15.1](#15.1) Use `===` and `!==` over `==` and `!=`. - - [15.2](#15.2) Conditional statements such as the `if` statement evaluate their expression using coercion with the `ToBoolean` abstract method and always follow these simple rules: - - + **Objects** evaluate to **true** - + **Undefined** evaluates to **false** - + **Null** evaluates to **false** - + **Booleans** evaluate to **the value of the boolean** - + **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true** - + **Strings** evaluate to **false** if an empty string `''`, otherwise **true** - - ```javascript - if ([0]) { - // true - // An array is an object, objects evaluate to true - } - ``` - - - [15.3](#15.3) Use shortcuts. - - ```javascript - // bad - if (name !== '') { - // ...stuff... - } - - // good - if (name) { - // ...stuff... - } - - // bad - if (collection.length > 0) { - // ...stuff... - } - - // good - if (collection.length) { - // ...stuff... - } - ``` - - - [15.4](#15.4) For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. - -**[⬆ back to top](#table-of-contents)** - - -## Blocks - - - [16.1](#16.1) Use braces with all multi-line blocks. - - ```javascript - // bad - if (test) - return false; - - // good - if (test) return false; - - // good - if (test) { - return false; - } - - // bad - function() { return false; } - - // good - function() { - return false; - } - ``` - - - [16.2](#16.2) If you're using multi-line blocks with `if` and `else`, put `else` on the same line as your - `if` block's closing brace. - - ```javascript - // bad - if (test) { - thing1(); - thing2(); - } - else { - thing3(); - } - - // good - if (test) { - thing1(); - thing2(); - } else { - thing3(); - } - ``` - - -**[⬆ back to top](#table-of-contents)** - - -## Comments - - - [17.1](#17.1) Use `/** ... */` for multi-line comments. Include a description, specify types and values for all parameters and return values. - - ```javascript - // bad - // make() returns a new element - // based on the passed in tag name - // - // @param {String} tag - // @return {Element} element - function make(tag) { - - // ...stuff... - - return element; - } - - // good - /** - * make() returns a new element - * based on the passed in tag name - * - * @param {String} tag - * @return {Element} element - */ - function make(tag) { - - // ...stuff... - - return element; - } - ``` - - - [17.2](#17.2) 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. - - ```javascript - // bad - const active = true; // is current tab - - // good - // is current tab - const active = true; - - // bad - function getType() { - console.log('fetching type...'); - // set the default type to 'no type' - const type = this._type || 'no type'; - - return type; - } - - // good - function getType() { - console.log('fetching type...'); - - // set the default type to 'no type' - const type = this._type || 'no type'; - - return type; - } - ``` - - - [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`. - - - [17.4](#17.4) Use `// FIXME:` to annotate problems. - - ```javascript - class Calculator extends Abacus { - constructor() { - super(); - - // FIXME: shouldn't use a global here - total = 0; - } - } - ``` - - - [17.5](#17.5) Use `// TODO:` to annotate solutions to problems. - - ```javascript - class Calculator extends Abacus { - constructor() { - super(); - - // TODO: total should be configurable by an options param - this.total = 0; - } - } - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Whitespace - - - [18.1](#18.1) Use soft tabs set to 2 spaces. - - ```javascript - // bad - function() { - ∙∙∙∙const name; - } - - // bad - function() { - ∙const name; - } - - // good - function() { - ∙∙const name; - } - ``` - - - [18.2](#18.2) Place 1 space before the leading brace. - - ```javascript - // bad - function test(){ - console.log('test'); - } - - // good - function test() { - console.log('test'); - } - - // bad - dog.set('attr',{ - age: '1 year', - breed: 'Bernese Mountain Dog', - }); - - // good - dog.set('attr', { - age: '1 year', - breed: 'Bernese Mountain Dog', - }); - ``` - - - [18.3](#18.3) 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. - - ```javascript - // bad - if(isJedi) { - fight (); - } - - // good - if (isJedi) { - fight(); - } - - // bad - function fight () { - console.log ('Swooosh!'); - } - - // good - function fight() { - console.log('Swooosh!'); - } - ``` - - - [18.4](#18.4) Set off operators with spaces. - - ```javascript - // bad - const x=y+5; - - // good - const x = y + 5; - ``` - - - [18.5](#18.5) End files with a single newline character. - - ```javascript - // bad - (function(global) { - // ...stuff... - })(this); - ``` - - ```javascript - // bad - (function(global) { - // ...stuff... - })(this);↵ - ↵ - ``` - - ```javascript - // good - (function(global) { - // ...stuff... - })(this);↵ - ``` - - - [18.5](#18.5) Use indentation when making long method chains. Use a leading dot, which - emphasizes that the line is a method call, not a new statement. - - ```javascript - // bad - $('#items').find('.selected').highlight().end().find('.open').updateCount(); - - // bad - $('#items'). - find('.selected'). - highlight(). - end(). - find('.open'). - updateCount(); - - // good - $('#items') - .find('.selected') - .highlight() - .end() - .find('.open') - .updateCount(); - - // bad - const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) - .attr('width', (radius + margin) * 2).append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - - // good - const leds = stage.selectAll('.led') - .data(data) - .enter().append('svg:svg') - .classed('led', true) - .attr('width', (radius + margin) * 2) - .append('svg:g') - .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') - .call(tron.led); - ``` - - - [18.6](#18.6) Leave a blank line after blocks and before the next statement. - - ```javascript - // bad - if (foo) { - return bar; - } - return baz; - - // good - if (foo) { - return bar; - } - - return baz; - - // bad - const obj = { - foo() { - }, - bar() { - }, - }; - return obj; - - // good - const obj = { - foo() { - }, - - bar() { - }, - }; - - return obj; - - // bad - const arr = [ - function foo() { - }, - function bar() { - }, - ]; - return arr; - - // good - const arr = [ - function foo() { - }, - - function bar() { - }, - ]; - - return arr; - ``` - - -**[⬆ back to top](#table-of-contents)** - -## Commas - - - [19.1](#19.1) Leading commas: **Nope.** - - ```javascript - // bad - const story = [ - once - , upon - , aTime - ]; - - // good - const story = [ - once, - upon, - aTime, - ]; - - // bad - const hero = { - firstName: 'Ada' - , lastName: 'Lovelace' - , birthYear: 1815 - , superPower: 'computers' - }; - - // good - const hero = { - firstName: 'Ada', - lastName: 'Lovelace', - birthYear: 1815, - superPower: 'computers', - }; - ``` - - - [19.2](#19.2) Additional trailing comma: **Yup.** - - > 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. - - ```javascript - // bad - git diff without trailing comma - const hero = { - firstName: 'Florence', - - lastName: 'Nightingale' - + lastName: 'Nightingale', - + inventorOf: ['coxcomb graph', 'modern nursing'] - } - - // good - git diff with trailing comma - const hero = { - firstName: 'Florence', - lastName: 'Nightingale', - + inventorOf: ['coxcomb chart', 'modern nursing'], - } - - // bad - const hero = { - firstName: 'Dana', - lastName: 'Scully' - }; - - const heroes = [ - 'Batman', - 'Superman' - ]; - - // good - const hero = { - firstName: 'Dana', - lastName: 'Scully', - }; - - const heroes = [ - 'Batman', - 'Superman', - ]; - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Semicolons - - - [20.1](#20.1) **Yup.** - - ```javascript - // bad - (function() { - const name = 'Skywalker' - return name - })() - - // good - (() => { - const name = 'Skywalker'; - return name; - })(); - - // good (guards against the function becoming an argument when two files with IIFEs are concatenated) - ;(() => { - const name = 'Skywalker'; - return name; - })(); - ``` - - [Read more](http://stackoverflow.com/a/7365214/1712802). - -**[⬆ back to top](#table-of-contents)** - - -## Type Casting & Coercion - - - [21.1](#21.1) Perform type coercion at the beginning of the statement. - - [21.2](#21.2) Strings: - - ```javascript - // => this.reviewScore = 9; - - // bad - const totalScore = this.reviewScore + ''; - - // good - const totalScore = String(this.reviewScore); - ``` - - - [21.3](#21.3) Use `parseInt` for Numbers and always with a radix for type casting. - - ```javascript - const inputValue = '4'; - - // bad - const val = new Number(inputValue); - - // bad - const val = +inputValue; - - // bad - const val = inputValue >> 0; - - // bad - const val = parseInt(inputValue); - - // good - const val = Number(inputValue); - - // good - const val = parseInt(inputValue, 10); - ``` - - - [21.4](#21.4) 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. - - ```javascript - // good - /** - * parseInt was the reason my code was slow. - * Bitshifting the String to coerce it to a - * Number made it a lot faster. - */ - const val = inputValue >> 0; - ``` - - - [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: - - ```javascript - 2147483647 >> 0 //=> 2147483647 - 2147483648 >> 0 //=> -2147483648 - 2147483649 >> 0 //=> -2147483647 - ``` - - - [21.6](#21.6) Booleans: - - ```javascript - const age = 0; - - // bad - const hasAge = new Boolean(age); - - // good - const hasAge = Boolean(age); - - // good - const hasAge = !!age; - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Naming Conventions - - - [22.1](#22.1) Avoid single letter names. Be descriptive with your naming. - - ```javascript - // bad - function q() { - // ...stuff... - } - - // good - function query() { - // ..stuff.. - } - ``` - - - [22.2](#22.2) Use camelCase when naming objects, functions, and instances. - - ```javascript - // bad - const OBJEcttsssss = {}; - const this_is_my_object = {}; - function c() {} - - // good - const thisIsMyObject = {}; - function thisIsMyFunction() {} - ``` - - - [22.3](#22.3) Use PascalCase when naming constructors or classes. - - ```javascript - // bad - function user(options) { - this.name = options.name; - } - - const bad = new user({ - name: 'nope', - }); - - // good - class User { - constructor(options) { - this.name = options.name; - } - } - - const good = new User({ - name: 'yup', - }); - ``` - - - [22.4](#22.4) Use a leading underscore `_` when naming private properties. - - ```javascript - // bad - this.__firstName__ = 'Panda'; - this.firstName_ = 'Panda'; - - // good - this._firstName = 'Panda'; - ``` - - - [22.5](#22.5) Don't save references to `this`. Use arrow functions or Function#bind. - - ```javascript - // bad - function foo() { - const self = this; - return function() { - console.log(self); - }; - } - - // bad - function foo() { - const that = this; - return function() { - console.log(that); - }; - } - - // good - function foo() { - return () => { - console.log(this); - }; - } - ``` - - - [22.6](#22.6) If your file exports a single class, your filename should be exactly the name of the class. - ```javascript - // file contents - class CheckBox { - // ... - } - export default CheckBox; - - // in some other file - // bad - import CheckBox from './checkBox'; - - // bad - import CheckBox from './check_box'; - - // good - import CheckBox from './CheckBox'; - ``` - - - [22.7](#22.7) Use camelCase when you export-default a function. Your filename should be identical to your function's name. - - ```javascript - function makeStyleGuide() { - } - - export default makeStyleGuide; - ``` - - - [22.8](#22.8) Use PascalCase when you export a singleton / function library / bare object. - - ```javascript - const AirbnbStyleGuide = { - es6: { - } - }; - - export default AirbnbStyleGuide; - ``` - - -**[⬆ back to top](#table-of-contents)** - - -## Accessors - - - [23.1](#23.1) Accessor functions for properties are not required. - - [23.2](#23.2) If you do make accessor functions use getVal() and setVal('hello'). - - ```javascript - // bad - dragon.age(); - - // good - dragon.getAge(); - - // bad - dragon.age(25); - - // good - dragon.setAge(25); - ``` - - - [23.3](#23.3) If the property is a `boolean`, use `isVal()` or `hasVal()`. - - ```javascript - // bad - if (!dragon.age()) { - return false; - } - - // good - if (!dragon.hasAge()) { - return false; - } - ``` - - - [23.4](#23.4) It's okay to create get() and set() functions, but be consistent. - - ```javascript - class Jedi { - constructor(options = {}) { - const lightsaber = options.lightsaber || 'blue'; - this.set('lightsaber', lightsaber); - } - - set(key, val) { - this[key] = val; - } - - get(key) { - return this[key]; - } - } - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Events - - - [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: - - ```javascript - // bad - $(this).trigger('listingUpdated', listing.id); - - ... - - $(this).on('listingUpdated', function(e, listingId) { - // do something with listingId - }); - ``` - - prefer: - - ```javascript - // good - $(this).trigger('listingUpdated', { listingId : listing.id }); - - ... - - $(this).on('listingUpdated', function(e, data) { - // do something with data.listingId - }); - ``` - - **[⬆ back to top](#table-of-contents)** - - -## jQuery - - - [25.1](#25.1) Prefix jQuery object variables with a `$`. - - ```javascript - // bad - const sidebar = $('.sidebar'); - - // good - const $sidebar = $('.sidebar'); - ``` - - - [25.2](#25.2) Cache jQuery lookups. - - ```javascript - // bad - function setSidebar() { - $('.sidebar').hide(); - - // ...stuff... - - $('.sidebar').css({ - 'background-color': 'pink' - }); - } - - // good - function setSidebar() { - const $sidebar = $('.sidebar'); - $sidebar.hide(); - - // ...stuff... - - $sidebar.css({ - 'background-color': 'pink' - }); - } - ``` - - - [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) - - [25.4](#25.4) Use `find` with scoped jQuery object queries. - - ```javascript - // bad - $('ul', '.sidebar').hide(); - - // bad - $('.sidebar').find('ul').hide(); - - // good - $('.sidebar ul').hide(); - - // good - $('.sidebar > ul').hide(); - - // good - $sidebar.find('ul').hide(); - ``` - -**[⬆ back to top](#table-of-contents)** - - -## ECMAScript 5 Compatibility - - - [26.1](#26.1) Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/). - -**[⬆ back to top](#table-of-contents)** - -## ECMAScript 6 Styles - - - [27.1](#27.1) This is a collection of links to the various es6 features. - -1. [Arrow Functions](#arrow-functions) -1. [Classes](#constructors) -1. [Object Shorthand](#es6-object-shorthand) -1. [Object Concise](#es6-object-concise) -1. [Object Computed Properties](#es6-computed-properties) -1. [Template Strings](#es6-template-literals) -1. [Destructuring](#destructuring) -1. [Default Parameters](#es6-default-parameters) -1. [Rest](#es6-rest) -1. [Array Spreads](#es6-array-spreads) -1. [Let and Const](#references) -1. [Iterators and Generators](#iterators-and-generators) -1. [Modules](#modules) - -**[⬆ back to top](#table-of-contents)** - -## Testing - - - [28.1](#28.1) **Yup.** - - ```javascript - function() { - return true; - } - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Performance - - - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) - - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) - - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) - - [Bang Function](http://jsperf.com/bang-function) - - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) - - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) - - [Long String Concatenation](http://jsperf.com/ya-string-concat) - - Loading... - -**[⬆ back to top](#table-of-contents)** - - -## Resources - -**Learning ES6** - - - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) - - [ExploringJS](http://exploringjs.com/) - - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) - - [Comprehensive Overview of ES6 Features](http://es6-features.org/) - -**Read This** - - - [Standard ECMA-262](http://www.ecma-international.org/ecma-262/6.0/index.html) - -**Tools** - - - Code Style Linters - + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) - + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) - + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) - -**Other Style Guides** - - - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) - - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) - - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) - -**Other Styles** - - - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen - - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen - - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun - - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman - -**Further Reading** - - - [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll - - [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer - - [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Zack Bloom & Adam Schwartz - - [ES6 Features](https://github.com/lukehoban/es6features) - Luke Hoban - - [Frontend Guidelines](https://github.com/bendc/frontend-guidelines) - Benjamin De Cock - -**Books** - - - [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford - - [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov - - [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz - - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders - - [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas - - [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw - - [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig - - [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch - - [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault - - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg - - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy - - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon - - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov - - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman - - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke - - [You Don't Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson - -**Blogs** - - - [DailyJS](http://dailyjs.com/) - - [JavaScript Weekly](http://javascriptweekly.com/) - - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) - - [Bocoup Weblog](http://weblog.bocoup.com/) - - [Adequately Good](http://www.adequatelygood.com/) - - [NCZOnline](http://www.nczonline.net/) - - [Perfection Kills](http://perfectionkills.com/) - - [Ben Alman](http://benalman.com/) - - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - - [Dustin Diaz](http://dustindiaz.com/) - - [nettuts](http://net.tutsplus.com/?s=javascript) - -**Podcasts** - - - [JavaScript Jabber](http://devchat.tv/js-jabber/) - - -**[⬆ back to top](#table-of-contents)** - -## In the Wild - - 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. - - - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) - - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) - - **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript) - - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) - - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) - - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) - - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript) - - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) - - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) - - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) - - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) - - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) - - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) - - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) - - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) - - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) - - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) - - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) - - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) - - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript) - - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) - - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) - - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) - - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) - - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) - - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) - - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) - - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript) - - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) - - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) - - **Muber**: [muber/javascript](https://github.com/muber/javascript) - - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) - - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) - - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) - - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) - - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) - - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) - - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) - - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) - - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) - - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) - - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) - - **Springload**: [springload/javascript](https://github.com/springload/javascript) - - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) - - **Target**: [target/javascript](https://github.com/target/javascript) - - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) - - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) - - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) - - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) - - **Zillow**: [zillow/javascript](https://github.com/zillow/javascript) - - **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript) - -**[⬆ back to top](#table-of-contents)** - -## Translation - - This style guide is also available in other languages: - - -  **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) - -  **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) - -  **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) - -  **Chinese(Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) - -  **Chinese(Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) - -  **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) - -  **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) - -  **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) - -  **Japanese**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide) - -  **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) - -  **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) - -  **Russian**: [uprock/javascript](https://github.com/uprock/javascript) - -  **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) - -  **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) - -## The JavaScript Style Guide Guide - - - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) - -## Chat With Us About JavaScript - - - Find us on [gitter](https://gitter.im/airbnb/javascript). - -## Contributors - - - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) - - -## License - -(The MIT License) - -Copyright (c) 2014 Airbnb - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[⬆ back to top](#table-of-contents)** - -# }; +``` +{ + "eslint.validate": [ + "javascript", + "javascriptreact", + { "language": "typescript", "autoFix": true }, + { "language": "typescriptreact", "autoFix": true } + ] +} +``` diff --git a/configs/base.js b/configs/base.js new file mode 100644 index 0000000000..374623d036 --- /dev/null +++ b/configs/base.js @@ -0,0 +1,201 @@ +module.exports = { + parser : 'babel-eslint', + extends: [ + 'airbnb', + 'airbnb/hooks', + ], + parserOptions: { + ecmaVersion: 2018, + }, + + settings: { + react: { + version: 'detect', + }, + }, + + rules: { + + /* + * + * Rules related to react and other modules + * + */ + + // Not worth the effort of refactoring all `this.props` usage + 'react/destructuring-assignment': 'off', + + // We want to explicitly define boolean values within jsx + 'react/jsx-boolean-value': ['error', 'always'], + + // Forcing spacing with braces in jsx + 'react/jsx-curly-spacing': ['error', 'always'], + + // The rule require changes to large chunk of code and does not aid readability much + 'react/jsx-one-expression-per-line': 'off', + + // It makes writing wrapper components more frustrating + 'react/jsx-props-no-spreading': 'off', + + // AirBnB defaults to useless warnings. Devs should just disable this rule line-by-line as needed + 'react/no-danger': 'error', + + // We heavily use array keys as index throught our code. Benefits are too theoretical to be worth the effort + 'react/no-array-index-key': 'error', + + // AirBnB disabled this for server-rendering scenario, which does not apply to us + 'react/no-did-mount-set-state': 'error', + + // Too many false errors when passing around `props` as fn arg + 'react/no-unused-prop-types': 'off', + + // Check stateless functional components for 'this' in case it was refactored incorrectly + 'react/no-this-in-sfc': 'error', + + // TODO revisit. Does not work well with our custom `propsValidators` usage + 'react/no-typos': 'off', + + // Allow devs to prefer PureComponents when appropriate + 'react/prefer-stateless-function': ['error', { ignorePureComponents: true }], + + // AirBnB overrides default set of options, but those overrides are outdated. Default good enough for us. + 'react/sort-comp': ['error', {}], + + // Let dev decide whether to include state in constructor + 'react/state-in-constructor': 'off', + + // AirBnB indicated they will move to this rule in future so use it now + 'react/static-property-placement': ['error', 'static public field'], + + // Theoretically, we should turn this on so we remind ourselves not to depend on webpack too much, but oh well + 'import/no-webpack-loader-syntax': 'off', + + // Ideally should be active to further cement dev-intention in Components props, but not currently worth effort + 'react/require-default-props': 'off', + + + /* + * + * Extra ES6-related restrictions and misc AirBnB overrides + * + */ + + // Allow simple arrow fns in format of F = foo => bar + 'arrow-parens': ['error', 'as-needed', + { requireForBlockBody: true }, + ], + + // The consistency checks do not match our early-returns + normal callback coding style + 'consistent-return': 'off', + + // Relaxed but consistent usage of braces + curly: ['error', 'multi-line', 'consistent'], + + // Ensures that debug traces will give readable function names + 'func-names': ['error', 'as-needed'], + + // Devs can choose most readable style as long as consistent for every arg + 'function-paren-newline': ['error', 'consistent'], + + // Rule should be identical to AirBnB except for VariableDeclarator & MemberExpression + indent: ['error', 2, { + SwitchCase : 1, + VariableDeclarator : { var: 2, let: 2, const: 3 }, + outerIIFEBody : 1, + MemberExpression : 'off', + FunctionDeclaration : { parameters: 1, body: 1 }, + FunctionExpression : { parameters: 1, body: 1 }, + CallExpression : { arguments: 1 }, + ArrayExpression : 1, + ObjectExpression : 1, + ImportDeclaration : 1, + flatTernaryExpressions: false, + ignoredNodes : ['JSXElement', 'JSXElement > *', 'JSXAttribute', 'JSXIdentifier', 'JSXNamespacedName', 'JSXMemberExpression', 'JSXSpreadAttribute', 'JSXExpressionContainer', 'JSXOpeningElement', 'JSXClosingElement', 'JSXText', 'JSXEmptyExpression', 'JSXSpreadChild'], + ignoreComments : false, + }], + + // Align on colon to use auto-fix to prettify large objects + 'key-spacing': [ + 2, { + singleLine: { beforeColon: false, afterColon: true }, + multiLine : { beforeColon: false, afterColon: true, align: 'colon' }, + }, + ], + + // Should not leak stuff to clientside + 'no-console': 'error', + + // Allow multiple spaces only in certain situations involving aligned white spaces + 'no-multi-spaces': ['error', { + exceptions: { + ImportDeclaration : true, + Property : true, + LogicalExpression : true, + VariableDeclarator: true, + }, + }], + + // Do not allow shadowed functions except in cases of extremely common parameter names + 'no-shadow': ['error', { allow: ['callback', 'cb', 'done', 'err', 'error', 'next', 'req', 'res'] }], + + // Currently a standard usage in GUI js + 'no-underscore-dangle': 'off', + + // Only start enforcing rule when there are 5 or more props + 'object-curly-newline': [ + 'error', + { + ObjectExpression: { + minProperties: 5, + multiline : true, + consistent : true, + }, + ObjectPattern: { + minProperties: 5, + multiline : true, + consistent : true, + }, + ImportDeclaration: { minProperties: 5, multiline: true, consistent: true }, + ExportDeclaration: { minProperties: 5, multiline: true, consistent: true }, + }, + ], + + // Only enforce shorthand on properties because doing so on methods produces weird behavior with anonymous fn + 'object-shorthand': ['error', 'properties'], + + // Aggregate uninitialized declarations into one line. Otherwise, use multiple + 'one-var': ['error', { uninitialized: 'always', initialized: 'never' }], + + // We allow developers to add padded blocks if they aid readability + 'padded-blocks': 'off', + + // Override default by allowing named functions, which makes stack traces more readable + 'prefer-arrow-callback': ['error', { + allowNamedFunctions: true, + allowUnboundThis : true, + }], + + // Interferes with other rules when trying to destructure off of `this`. Also, too heavy handed a rule + 'prefer-destructuring': 'off', + + // Benefit of string templates is not worth all the white noise of current GUI infractions + 'prefer-template': 'off', + + // We do not like space before the first parenthesis in function decl + 'space-before-function-paren': ['error', 'never'], + + 'max-len': ['error', { + code : 120, + tabWidth: 2, + + ignoreUrls : true, + ignoreComments : true, + ignoreRegExpLiterals : true, + ignoreStrings : true, + ignoreTemplateLiterals: true, + + // require / async import statements can be as long as they need to be + ignorePattern: '.*(\\(|\\s)+(require|import)\\)', + }], + }, +}; diff --git a/configs/disable/react-a11y.js b/configs/disable/react-a11y.js new file mode 100644 index 0000000000..745a657390 --- /dev/null +++ b/configs/disable/react-a11y.js @@ -0,0 +1,11 @@ +const a11yRules = require('eslint-config-airbnb/rules/react-a11y'); + +module.exports = { + rules: Object.keys(a11yRules.rules).reduce((acc, key) => { + if (key.startsWith('jsx-a11y')) { + acc[key] = 'off'; + } + + return acc; + }, {}), +}; diff --git a/configs/disable/react.js b/configs/disable/react.js new file mode 100644 index 0000000000..5bcfbe0dd0 --- /dev/null +++ b/configs/disable/react.js @@ -0,0 +1,16 @@ +const reactRules = require('eslint-config-airbnb/rules/react'); + +module.exports = { + parserOptions: { + ecmaFeatures: { + jsx: false, + }, + }, + rules: Object.keys(reactRules.rules).reduce((acc, key) => { + if (key.startsWith('react')) { + acc[key] = 'off'; + } + + return acc; + }, {}), +}; diff --git a/configs/gui.js b/configs/gui.js new file mode 100644 index 0000000000..973ce5a238 --- /dev/null +++ b/configs/gui.js @@ -0,0 +1,66 @@ +module.exports = { + extends: [ + './base', + './disable/react-a11y', + ].map(require.resolve), + parserOptions: { + ecmaFeatures: { + jsx: true, + + // Temporarily allow "@dec() export {{default}} class A {}" + // TODO - take off band aid and change codebase to "export {{default}} @dec class A{}" as per JS spec + legacyDecorators: true, + }, + }, + settings: { + 'import/resolver': 'webpack', + }, + env: { + browser: true, + node : true, + }, + globals: { + $ : false, + _ : false, + Backbone: false, + jQuery : false, + + DEBUG : false, + cui : false, + cuiLodash: false, + }, + + rules: { + + /* + * + * Custom Cloudability created rules + * + */ + + // 'cloudability/foo': 'error', + + /* + * + * Extra ES6-related restrictions and misc AirBnB overrides + * + */ + + // GUI team has decided against dangling commas + 'comma-dangle': ['error', 'never'], + + // A very large chunk of our code base puts operator at end + 'operator-linebreak': ['error', 'after'], + + 'spaced-comment': ['error', 'always', { + line: { + markers: ['/', 'global'], + }, + block: { + exceptions: ['*'], + balanced : true, + }, + }], + + }, +}; diff --git a/configs/guiTests.js b/configs/guiTests.js new file mode 100644 index 0000000000..efc1d3e2a3 --- /dev/null +++ b/configs/guiTests.js @@ -0,0 +1,23 @@ +module.exports = { + globals: { + renderTools: false, + describe : false, + it : false, + }, + + extends: [ + './gui', + ].map(require.resolve), + + env: { + browser: false, + mocha : true, + jest : true, + }, + + rules: { + 'import/no-extraneous-dependencies': 'off', + 'react/jsx-filename-extension' : 'off', + 'global-require' : 'off', + }, +}; diff --git a/configs/service.js b/configs/service.js new file mode 100644 index 0000000000..5620866b16 --- /dev/null +++ b/configs/service.js @@ -0,0 +1,18 @@ +module.exports = { + extends: [ + './base', + ].map(require.resolve), + env: { + browser: false, + node : true, + }, + rules: { + + // Some libraries use callbacks with a common callback structure. Do not vary this structure + 'no-unused-vars': ['error', { + ignoreRestSiblings: true, + argsIgnorePattern : '^done$', + varsIgnorePattern : '^_|CloudyError|config|qlog$', + }], + }, +}; diff --git a/configs/typescript.js b/configs/typescript.js new file mode 100644 index 0000000000..466c39be10 --- /dev/null +++ b/configs/typescript.js @@ -0,0 +1,58 @@ +const typescriptEslint = require('@typescript-eslint/eslint-plugin'); + +module.exports = { + extends: [ + require.resolve('./base'), + 'plugin:import/typescript', + 'plugin:@typescript-eslint/eslint-recommended', + ], + parserOptions: { + project : './tsconfig.json', + ecmaFeatures: { + jsx: true, + }, + }, + env: { + browser : true, + commonjs: true, + es6 : true, + jest : true, + node : true, + }, + + overrides: [ + { + files : ['*.ts', '*.tsx'], + parser : '@typescript-eslint/parser', + parserOptions: { + warnOnUnsupportedTypeScriptVersion: true, + }, + plugins: ['@typescript-eslint'], + + rules: { + ...typescriptEslint.configs.recommended.rules, + ...typescriptEslint.configs['recommended-requiring-type-checking'].rules, + + // Also allow react in .tsx files + 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx'] }], + + // TypeScript's `noFallthroughCasesInSwitch` option is more robust + 'default-case': 'off', + + // Allow rest siblings to be ignored + '@typescript-eslint/no-unused-vars': [ + 'error', + { + vars : 'all', + args : 'after-used', + ignoreRestSiblings: true, + }, + ], + + // Match airBnB's stricter rule on the useless constructor + 'no-useless-constructor' : 'off', + '@typescript-eslint/no-useless-constructor': 'error', + }, + }, + ], +}; diff --git a/es5/README.md b/es5/README.md deleted file mode 100644 index f961d8e0d6..0000000000 --- a/es5/README.md +++ /dev/null @@ -1,1741 +0,0 @@ -[](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -# Airbnb JavaScript Style Guide() { - -*A mostly reasonable approach to JavaScript* - - -## Table of Contents - - 1. [Types](#types) - 1. [Objects](#objects) - 1. [Arrays](#arrays) - 1. [Strings](#strings) - 1. [Functions](#functions) - 1. [Properties](#properties) - 1. [Variables](#variables) - 1. [Hoisting](#hoisting) - 1. [Comparison Operators & Equality](#comparison-operators--equality) - 1. [Blocks](#blocks) - 1. [Comments](#comments) - 1. [Whitespace](#whitespace) - 1. [Commas](#commas) - 1. [Semicolons](#semicolons) - 1. [Type Casting & Coercion](#type-casting--coercion) - 1. [Naming Conventions](#naming-conventions) - 1. [Accessors](#accessors) - 1. [Constructors](#constructors) - 1. [Events](#events) - 1. [Modules](#modules) - 1. [jQuery](#jquery) - 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility) - 1. [Testing](#testing) - 1. [Performance](#performance) - 1. [Resources](#resources) - 1. [In the Wild](#in-the-wild) - 1. [Translation](#translation) - 1. [The JavaScript Style Guide Guide](#the-javascript-style-guide-guide) - 1. [Chat With Us About Javascript](#chat-with-us-about-javascript) - 1. [Contributors](#contributors) - 1. [License](#license) - -## Types - - - **Primitives**: When you access a primitive type you work directly on its value. - - + `string` - + `number` - + `boolean` - + `null` - + `undefined` - - ```javascript - var foo = 1; - var bar = foo; - - bar = 9; - - console.log(foo, bar); // => 1, 9 - ``` - - **Complex**: When you access a complex type you work on a reference to its value. - - + `object` - + `array` - + `function` - - ```javascript - var foo = [1, 2]; - var bar = foo; - - bar[0] = 9; - - console.log(foo[0], bar[0]); // => 9, 9 - ``` - -**[⬆ back to top](#table-of-contents)** - -## Objects - - - Use the literal syntax for object creation. - - ```javascript - // bad - var item = new Object(); - - // good - var item = {}; - ``` - - - 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). - - ```javascript - // bad - var superman = { - default: { clark: 'kent' }, - private: true - }; - - // good - var superman = { - defaults: { clark: 'kent' }, - hidden: true - }; - ``` - - - Use readable synonyms in place of reserved words. - - ```javascript - // bad - var superman = { - class: 'alien' - }; - - // bad - var superman = { - klass: 'alien' - }; - - // good - var superman = { - type: 'alien' - }; - ``` - -**[⬆ back to top](#table-of-contents)** - -## Arrays - - - Use the literal syntax for array creation. - - ```javascript - // bad - var items = new Array(); - - // good - var items = []; - ``` - - - Use Array#push instead of direct assignment to add items to an array. - - ```javascript - var someStack = []; - - - // bad - someStack[someStack.length] = 'abracadabra'; - - // good - someStack.push('abracadabra'); - ``` - - - When you need to copy an array use Array#slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) - - ```javascript - var len = items.length; - var itemsCopy = []; - var i; - - // bad - for (i = 0; i < len; i++) { - itemsCopy[i] = items[i]; - } - - // good - itemsCopy = items.slice(); - ``` - - - To convert an array-like object to an array, use Array#slice. - - ```javascript - function trigger() { - var args = Array.prototype.slice.call(arguments); - ... - } - ``` - -**[⬆ back to top](#table-of-contents)** - - -## Strings - - - Use single quotes `''` for strings. - - ```javascript - // bad - var name = "Bob Parr"; - - // good - var name = 'Bob Parr'; - - // bad - var fullName = "Bob " + this.lastName; - - // good - var fullName = 'Bob ' + this.lastName; - ``` - - - Strings longer than 100 characters should be written across multiple lines using string concatenation. - - 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). - - ```javascript - // bad - 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.'; - - // bad - 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.'; - - // good - 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.'; - ``` - - - 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). - - ```javascript - var items; - var messages; - var length; - var i; - - messages = [{ - state: 'success', - message: 'This one worked.' - }, { - state: 'success', - message: 'This one worked as well.' - }, { - state: 'error', - message: 'This one did not work.' - }]; - - length = messages.length; - - // bad - function inbox(messages) { - items = '