mirror of
https://github.com/airbnb/javascript.git
synced 2026-01-14 03:18:03 -05:00
2148 lines
55 KiB
Markdown
2148 lines
55 KiB
Markdown
[](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*
|
|
|
|
[For the ES5-only guide click here](es5/).
|
|
|
|
## Table of Contents
|
|
|
|
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)
|
|
|
|
## Types
|
|
|
|
- **Primitives**: When you access a primitive type you work directly on its value.
|
|
|
|
+ `string`
|
|
+ `number`
|
|
+ `boolean`
|
|
+ `null`
|
|
+ `undefined`
|
|
|
|
```javascript
|
|
const foo = 1;
|
|
let 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
|
|
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
|
|
|
|
- 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;
|
|
```
|
|
|
|
- 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;
|
|
}
|
|
```
|
|
|
|
- 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
|
|
|
|
- Use the literal syntax for object creation.
|
|
|
|
```javascript
|
|
// bad
|
|
const item = new Object();
|
|
|
|
// good
|
|
const 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
|
|
const superman = {
|
|
default: { clark: 'kent' },
|
|
private: true
|
|
};
|
|
|
|
// good
|
|
const superman = {
|
|
defaults: { clark: 'kent' },
|
|
hidden: true
|
|
};
|
|
```
|
|
|
|
- Use readable synonyms in place of reserved words.
|
|
|
|
```javascript
|
|
// bad
|
|
const superman = {
|
|
class: 'alien'
|
|
};
|
|
|
|
// bad
|
|
const superman = {
|
|
klass: 'alien'
|
|
};
|
|
|
|
// good
|
|
const superman = {
|
|
type: 'alien'
|
|
};
|
|
```
|
|
|
|
<a name="es6-computed-properties"></a>
|
|
- 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,
|
|
};
|
|
```
|
|
|
|
<a name="es6-object-shorthand"></a>
|
|
- 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;
|
|
},
|
|
};
|
|
```
|
|
|
|
<a name="es6-object-concise"></a>
|
|
- 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
|
|
};
|
|
```
|
|
|
|
- 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,
|
|
twoJedisWalkIntoACantina: 2,
|
|
lukeSkywalker,
|
|
episodeThree: 3,
|
|
mayTheFourth: 4,
|
|
anakinSkywalker,
|
|
};
|
|
|
|
// good
|
|
const obj = {
|
|
lukeSkywalker,
|
|
anakinSkywalker,
|
|
episodeOne: 1,
|
|
twoJedisWalkIntoACantina: 2,
|
|
episodeThree: 3,
|
|
mayTheFourth: 4,
|
|
};
|
|
```
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
## Arrays
|
|
|
|
- Use the literal syntax for array creation.
|
|
|
|
```javascript
|
|
// bad
|
|
const items = new Array();
|
|
|
|
// good
|
|
const items = [];
|
|
```
|
|
|
|
- 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');
|
|
```
|
|
|
|
<a name="es6-array-spreads"></a>
|
|
- 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];
|
|
}
|
|
|
|
// good
|
|
const itemsCopy = [...items];
|
|
```
|
|
- 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
|
|
|
|
- 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}`;
|
|
}
|
|
```
|
|
|
|
- Use array destructuring.
|
|
|
|
```javascript
|
|
const arr = [1, 2, 3, 4];
|
|
|
|
// bad
|
|
const first = arr[0];
|
|
const second = arr[1];
|
|
|
|
// good
|
|
const [first, second] = arr;
|
|
```
|
|
|
|
- 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
|
|
|
|
- Use single quotes `''` for strings.
|
|
|
|
```javascript
|
|
// bad
|
|
const name = "Capt. Janeway";
|
|
|
|
// good
|
|
const name = 'Capt. Janeway';
|
|
```
|
|
|
|
- Strings longer than 80 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
|
|
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.';
|
|
```
|
|
|
|
<a name="es6-template-literals"></a>
|
|
- 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}?`;
|
|
}
|
|
```
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
|
|
## Functions
|
|
|
|
- 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
|
|
// bad
|
|
const foo = function () {
|
|
};
|
|
|
|
// good
|
|
function foo() {
|
|
}
|
|
```
|
|
|
|
- Function expressions:
|
|
|
|
```javascript
|
|
// immediately-invoked function expression (IIFE)
|
|
(() => {
|
|
console.log('Welcome to the Internet. Please follow me.');
|
|
})();
|
|
```
|
|
|
|
- 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.
|
|
- **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.');
|
|
};
|
|
}
|
|
```
|
|
|
|
- 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...
|
|
}
|
|
```
|
|
|
|
<a name="es6-rest"></a>
|
|
- 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('');
|
|
}
|
|
```
|
|
|
|
<a name="es6-default-parameters"></a>
|
|
- 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 = {}) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
- 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
|
|
```
|
|
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
## Arrow Functions
|
|
|
|
- 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
|
|
});
|
|
```
|
|
|
|
- If the function body fits on one line, feel free to omit the braces and use implicit return. Otherwise, add the 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].map((x) => {
|
|
return { number: x };
|
|
});
|
|
```
|
|
|
|
- Always use parentheses around the arguments. Omitting the parentheses makes the functions less readable and only works for single arguments.
|
|
|
|
> Why? These declarations read better with parentheses. They are also required when you have multiple parameters so this enforces consistency.
|
|
|
|
```javascript
|
|
// bad
|
|
[1, 2, 3].map(x => x * x);
|
|
|
|
// good
|
|
[1, 2, 3].map((x) => x * x);
|
|
```
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
|
|
## Constructors
|
|
|
|
- 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;
|
|
}
|
|
}
|
|
```
|
|
|
|
- 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];
|
|
}
|
|
}
|
|
```
|
|
|
|
- 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);
|
|
```
|
|
|
|
|
|
- It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects.
|
|
|
|
```javascript
|
|
class Jedi {
|
|
contructor(options = {}) {
|
|
this.name = options.name || 'no name';
|
|
}
|
|
|
|
getName() {
|
|
return this.name;
|
|
}
|
|
|
|
toString() {
|
|
return `Jedi - ${this.getName()}`;
|
|
}
|
|
}
|
|
```
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
|
|
## Modules
|
|
|
|
- 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;
|
|
```
|
|
|
|
- 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';
|
|
```
|
|
|
|
- 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
|
|
|
|
- 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;
|
|
```
|
|
|
|
- Don't use generators for now.
|
|
|
|
> Why? They don't transpile well to ES5.
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
|
|
## Properties
|
|
|
|
- Use dot notation when accessing properties.
|
|
|
|
```javascript
|
|
const luke = {
|
|
jedi: true,
|
|
age: 28,
|
|
};
|
|
|
|
// bad
|
|
const isJedi = luke['jedi'];
|
|
|
|
// good
|
|
const isJedi = luke.jedi;
|
|
```
|
|
|
|
- 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
|
|
|
|
- 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();
|
|
```
|
|
|
|
- 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
|
|
const items = getItems();
|
|
const goSportsTeam = true;
|
|
const dragonball = 'z';
|
|
```
|
|
|
|
- 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;
|
|
```
|
|
|
|
- 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() {
|
|
test();
|
|
console.log('doing stuff..');
|
|
|
|
//..other stuff..
|
|
|
|
const name = getName();
|
|
|
|
if (name === 'test') {
|
|
return false;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
// bad - unnessary function call
|
|
function(hasName) {
|
|
const name = getName();
|
|
|
|
if (!hasName) {
|
|
return false;
|
|
}
|
|
|
|
this.setFirstName(name);
|
|
|
|
return true;
|
|
}
|
|
|
|
// good
|
|
function(hasName) {
|
|
if (!hasName) {
|
|
return false;
|
|
}
|
|
|
|
const name = getName();
|
|
this.setFirstName(name);
|
|
|
|
return true;
|
|
}
|
|
```
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
|
|
## Hoisting
|
|
|
|
- `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;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
```
|
|
|
|
- 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() {
|
|
console.log('anonymous function expression');
|
|
};
|
|
}
|
|
```
|
|
|
|
- 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');
|
|
}
|
|
}
|
|
```
|
|
|
|
- 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
|
|
|
|
- Use `===` and `!==` over `==` and `!=`.
|
|
- Comparison operators are evaluated using coercion with the `ToBoolean` 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
|
|
}
|
|
```
|
|
|
|
- Use shortcuts.
|
|
|
|
```javascript
|
|
// bad
|
|
if (name !== '') {
|
|
// ...stuff...
|
|
}
|
|
|
|
// good
|
|
if (name) {
|
|
// ...stuff...
|
|
}
|
|
|
|
// bad
|
|
if (collection.length > 0) {
|
|
// ...stuff...
|
|
}
|
|
|
|
// good
|
|
if (collection.length) {
|
|
// ...stuff...
|
|
}
|
|
```
|
|
|
|
- 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
|
|
|
|
- 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;
|
|
}
|
|
```
|
|
|
|
- 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
|
|
|
|
- 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;
|
|
}
|
|
```
|
|
|
|
- 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;
|
|
}
|
|
```
|
|
|
|
- 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`.
|
|
|
|
- Use `// FIXME:` to annotate problems.
|
|
|
|
```javascript
|
|
class Calculator {
|
|
constructor() {
|
|
// FIXME: shouldn't use a global here
|
|
total = 0;
|
|
}
|
|
}
|
|
```
|
|
|
|
- Use `// TODO:` to annotate solutions to problems.
|
|
|
|
```javascript
|
|
class Calculator {
|
|
constructor() {
|
|
// TODO: total should be configurable by an options param
|
|
this.total = 0;
|
|
}
|
|
}
|
|
```
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
|
|
## Whitespace
|
|
|
|
- Use soft tabs set to 2 spaces.
|
|
|
|
```javascript
|
|
// bad
|
|
function() {
|
|
∙∙∙∙const name;
|
|
}
|
|
|
|
// bad
|
|
function() {
|
|
∙const name;
|
|
}
|
|
|
|
// good
|
|
function() {
|
|
∙∙const name;
|
|
}
|
|
```
|
|
|
|
- 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'
|
|
});
|
|
```
|
|
|
|
- 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!');
|
|
}
|
|
```
|
|
|
|
- Set off operators with spaces.
|
|
|
|
```javascript
|
|
// bad
|
|
const x=y+5;
|
|
|
|
// good
|
|
const x = y + 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);↵
|
|
```
|
|
|
|
- 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);
|
|
```
|
|
|
|
- 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;
|
|
```
|
|
|
|
|
|
**[⬆ back to top](#table-of-contents)**
|
|
|
|
## Commas
|
|
|
|
- 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',
|
|
};
|
|
```
|
|
|
|
- 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', 'mordern nursing']
|
|
}
|
|
|
|
// good - git diff with trailing comma
|
|
const hero = {
|
|
firstName: 'Florence',
|
|
lastName: 'Nightingale',
|
|
+ inventorOf: ['coxcomb chart', 'mordern 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
|
|
|
|
- **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
|
|
|
|
- Perform type coercion at the beginning of the statement.
|
|
- Strings:
|
|
|
|
```javascript
|
|
// => this.reviewScore = 9;
|
|
|
|
// bad
|
|
const totalScore = this.reviewScore + '';
|
|
|
|
// good
|
|
const totalScore = String(this.reviewScore);
|
|
```
|
|
|
|
- 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);
|
|
```
|
|
|
|
- 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;
|
|
```
|
|
|
|
- **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
|
|
```
|
|
|
|
- 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
|
|
|
|
- Avoid single letter names. Be descriptive with your naming.
|
|
|
|
```javascript
|
|
// bad
|
|
function q() {
|
|
// ...stuff...
|
|
}
|
|
|
|
// good
|
|
function query() {
|
|
// ..stuff..
|
|
}
|
|
```
|
|
|
|
- Use camelCase when naming objects, functions, and instances.
|
|
|
|
```javascript
|
|
// bad
|
|
const OBJEcttsssss = {};
|
|
const this_is_my_object = {};
|
|
function c() {}
|
|
|
|
// good
|
|
const thisIsMyObject = {};
|
|
function thisIsMyFunction() {}
|
|
```
|
|
|
|
- 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',
|
|
});
|
|
```
|
|
|
|
- Use a leading underscore `_` when naming private properties.
|
|
|
|
```javascript
|
|
// bad
|
|
this.__firstName__ = 'Panda';
|
|
this.firstName_ = 'Panda';
|
|
|
|
// good
|
|
this._firstName = 'Panda';
|
|
```
|
|
|
|
- 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);
|
|
};
|
|
}
|
|
```
|
|
|
|
- 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';
|
|
```
|
|
|
|
- Use camelCase when you export-default a function. Your filename should be identical to your function's name.
|
|
|
|
```javascript
|
|
function makeStyleGuide() {
|
|
}
|
|
|
|
export default makeStyleGuide;
|
|
```
|
|
|
|
- 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
|
|
|
|
- Accessor functions for properties are not required.
|
|
- 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);
|
|
```
|
|
|
|
- If the property is a boolean, use isVal() or hasVal().
|
|
|
|
```javascript
|
|
// bad
|
|
if (!dragon.age()) {
|
|
return false;
|
|
}
|
|
|
|
// good
|
|
if (!dragon.hasAge()) {
|
|
return false;
|
|
}
|
|
```
|
|
|
|
- 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
|
|
|
|
- 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
|
|
|
|
- Prefix jQuery object variables with a `$`.
|
|
|
|
```javascript
|
|
// bad
|
|
const sidebar = $('.sidebar');
|
|
|
|
// good
|
|
const $sidebar = $('.sidebar');
|
|
```
|
|
|
|
- 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'
|
|
});
|
|
}
|
|
```
|
|
|
|
- For DOM queries use Cascading `$('.sidebar ul')` or parent > child `$('.sidebar > ul')`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16)
|
|
- 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
|
|
|
|
- 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
|
|
|
|
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
|
|
|
|
- **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**
|
|
|
|
- [Annotated ECMAScript 5.1](http://es5.github.com/)
|
|
|
|
**Tools**
|
|
|
|
- Code Style Linters
|
|
+ [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 Styleguides**
|
|
|
|
- [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
|
|
|
|
**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)
|
|
- **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript)
|
|
- **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript)
|
|
- **Avalara**: [avalara/javascript](https://github.com/avalara/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)
|
|
- **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide)
|
|
- **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript)
|
|
- **GeneralElectric**: [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)
|
|
- **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)
|
|
- **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)
|
|
- **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)
|
|
- **Userify**: [userify/javascript](https://github.com/userify/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)
|
|
|
|
## 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)**: [adamlu/javascript-style-guide](https://github.com/adamlu/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)**
|
|
|
|
# };
|