更合理的 JavaScript 编写方式。 via Airbnb
For the ES5-only guide click here.
- 类型
- 引用
- Objects
- Arrays
- Destructuring
- Strings
- Functions
- Arrow Functions
- Constructors
- Modules
- Iterators and Generators
- Properties
- Variables
- Hoisting
- Comparison Operators & Equality
- Blocks
- Comments
- Whitespace
- Commas
- Semicolons
- Type Casting & Coercion
- Naming Conventions
- Accessors
- Events
- jQuery
- ECMAScript 5 Compatibility
- ECMAScript 6 Styles
- Testing
- Performance
- Resources
- In the Wild
- Translation
- The JavaScript Style Guide Guide
- Chat With Us About Javascript
- Contributors
- License
1.1基本类型:基本类型数据直接存取。
stringnumberbooleannullundefined
constfoo=1;letbar=foo;bar=9;console.log(foo,bar);// => 1, 9
1.2复杂类型:复杂数据类型通过引用方式存取。
objectarrayfunction
constfoo=[1,2];constbar=foo;bar[0]=9;console.log(foo[0],bar[0]);// => 9, 9
- 2.1 对所用引用使用
const;避免使用var。
为什么?这能确保无法对引用重新赋值,以避免可能导致的 bug 和难以理解的代码。
```javascript
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
```
- 2.2 必须使用可变引用时,使用
let而不是var。
为什么?使用块级作用域(
let),而不是函数作用域(var)。
```javascript
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
```
2.3 注意:
let和const都是块级作用域。// const 和 let 只存在于它们被定义的区块中{leta=1;constb=1;}console.log(a);// ReferenceErrorconsole.log(b);// ReferenceError
3.1 使用字面量语法创建对象。
// badconstitem=newObject();// goodconstitem={};
3.2 如果代码在浏览器中执行,不要使用保留字作为键值,不然的话在 IE8 中不会执行更多信息(but who care IE8?)。在 ES6 模块和服务器端代码中使用则没有问题。
// badconstsuperman={default: {clark: 'kent'},private: true,};// goodconstsuperman={defaults: {clark: 'kent'},hidden: true,};
3.3 使用可读的同义词替换需要使用的保留字。
// badconstsuperman={class: 'alien',};// badconstsuperman={klass: 'alien',};// goodconstsuperman={type: 'alien',};
- 3.4 创建包含动态属性名的对象时,使用经过计算的属性名称。
为什么?这样可以一次定义所有属性。
```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 使用对象方法简写语法(省略
function关键字)。// badconstatom={value: 1,addValue: function(value){returnatom.value+value;},};// goodconstatom={value: 1,addValue(value){returnatom.value+value;},};
- 3.6 使用属性值简写语法。
为什么?这样更简洁、更具描述性。
```javascript
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
```
- 3.7 把简写的属性分组排列在对象声明顶部。
为什么?这样更容易区分哪些属性采用了简写。
```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,
};
```
4.1 使用字面量语法创建数组。
// badconstitems=newArray();// goodconstitems=[];
4.2 使用
push添加元素,而不是直接赋值。constsomeStack=[];// badsomeStack[someStack.length]='abracadabra';// goodsomeStack.push('abracadabra');
4.3 使用数组扩展运算符
...拷贝数组。// badconstlen=items.length;constitemsCopy=[];leti;for(i=0;i<len;i++){itemsCopy[i]=items[i];}// goodconstitemsCopy=[...items];
4.4 使用
Array.from转换类数组对象。constfoo=document.querySelectorAll('.foo');constnodes=Array.from(foo);
- 5.1 调用对象的多个属性时,使用解构语法。
为什么?解构可以减少临时引用属性。
```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 使用数组解构。
constarr=[1,2,3,4];// badconstfirst=arr[0];constsecond=arr[1];// goodconst[first,second]=arr;
5.3 多个返回值时使用对象解构,而不是数组。
为什么?对象顺序不敏感,方便添加新属性。
```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);
```
6.1 使用单引号
''。// badconstname="Capt. Janeway";// goodconstname='Capt. Janeway';
6.2 长度超过 80 个字符的字符串应采用多行拼接。
6.3 注意:过度使用长字符串拼接会影响性能。jsPerf 测试 & 讨论。
// badconsterrorMessage='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.';// badconsterrorMessage='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.';// goodconsterrorMessage='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 编程创建字符串时使用模板语法替代拼接。
为什么?模板字符串提供换行、变量插入功能,让代码更简洁易读。
```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}?`;
}
```
- 7.1 使用函数声明而不是函数表达式。
为什么?函数声明是命名的,易于跟踪调试;函数声明把真个函数提升,而函数表达式只提升引用的变量名。此规则使得 箭头函数 可以替代函数表达式。
```javascript
// bad
const foo = function () {
};
// good
function foo() {
}
```
7.2 函数表达式:
// immediately-invoked function expression (IIFE)(()=>{console.log('Welcome to the Internet. Please follow me.');})();
7.3 绝不要在非函数代码块中声明函数(
if、while等等),可以把函数赋值给一个变量。浏览器允许这么做,但解析结果会不一致。7.4注意: ECMA-262 为一系列语句定义了「块」(
block),函数什么并不包含在其中。 阅读 ECMA-262 关于这个问题问题的说明.// badif(currentUser){functiontest(){console.log('Nope.');}}// goodlettest;if(currentUser){test=()=>{console.log('Yup.');};}
7.5 绝不要把参数命名为
arguments,这会覆盖每个函数作用域里的arguments对象。// badfunctionnope(name,options,arguments){// ...stuff...}// goodfunctionyup(name,options,args){// ...stuff...}
- 7.6 绝不要使用
arguments,使用...语法代替。
为什么?
...明确地反应所处理的参数;...语法返回一个真正的数组,而不是像arguments一样的类数组。
```javascript
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
```
7.7 使用默认参数语法,而不是去改变函数参数。
// really badfunctionhandleThings(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 badfunctionhandleThings(opts){if(opts===void0){opts={};}// ...}// goodfunctionhandleThings(opts={}){// ...}
7.8 设置默认参数时应避免产生其他影响。
为什么?这会产生令人迷惑的结果。
varb=1;// badfunctioncount(a=b++){console.log(a);}count();// 1count();// 2count(3);// 3count();// 3- 8.1 必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数语法。
为什么?箭头函数自动绑定执行上下文文为创建时的
this;更简洁。
为什么不?当遇到复杂的函数时,或许应该把逻辑放到独立的函数声明中去。
```javascript
// bad
[1, 2, 3].map(function (x) {
return x * x;
});
// good
[1, 2, 3].map((x) => {
return x * x;
});
```
- 8.2 函数体只有一行、只有一个参数而且不需要返回值时,可以省略花括号和圆括号。否则,保留括号,并使用
return语句。
为什么?语法糖。多个函数连缀时可读性很好。 为什么不?需要返回值的时候。
```javascript
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].reduce((total, n) => {
return total + n;
}, 0);
```
- 9.1 Always use
class. Avoid manipulatingprototypedirectly.
Why?
classsyntax 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 Use
extendsfor 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 Methods can return
thisto help with method chaining.// badJedi.prototype.jump=function(){this.jumping=true;returntrue;};Jedi.prototype.setHeight=function(height){this.height=height;};constluke=newJedi();luke.jump();// => trueluke.setHeight(20);// => undefined// goodclassJedi{jump(){this.jumping=true;returnthis;}setHeight(height){this.height=height;returnthis;}}constluke=newJedi();luke.jump().setHeight(20);
9.4 It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects.
classJedi{constructor(options={}){this.name=options.name||'no name';}getName(){returnthis.name;}toString(){return`Jedi - ${this.getName()}`;}}
- 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 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.3And 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;
```
- 11.1 Don't use iterators. Prefer JavaScript's higher-order functions like
map()andreduce()instead of loops likefor-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 Don't use generators for now.
Why? They don't transpile well to ES5.
12.1 Use dot notation when accessing properties.
constluke={jedi: true,age: 28,};// badconstisJedi=luke['jedi'];// goodconstisJedi=luke.jedi;
12.2 Use subscript notation
[]when accessing properties with a variable.constluke={jedi: true,age: 28,};functiongetProp(prop){returnluke[prop];}constisJedi=getProp('jedi');
13.1 Always use
constto declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that.// badsuperPower=newSuperPower();// goodconstsuperPower=newSuperPower();
13.2 Use one
constdeclaration 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.// badconstitems=getItems(),goSportsTeam=true,dragonball='z';// bad// (compare to above, and try to spot the mistake)constitems=getItems(),goSportsTeam=true;dragonball='z';// goodconstitems=getItems();constgoSportsTeam=true;constdragonball='z';
13.3 Group all your
consts and then group all yourlets.
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;
```
- 13.4 Assign variables where you need them, but place them in a reasonable place.
Why?
letandconstare 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 - unnecessary 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;
}
```
14.1
vardeclarations get hoisted to the top of their scope, their assignment does not.constandletdeclarations are blessed with a new concept called Temporal Dead Zones (TDZ). It's important to know why typeof is no longer safe.// we know this wouldn't work (assuming there// is no notDefined global variable)functionexample(){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.functionexample(){console.log(declaredButNotAssigned);// => undefinedvardeclaredButNotAssigned=true;}// The interpreter is hoisting the variable// declaration to the top of the scope,// which means our example could be rewritten as:functionexample(){letdeclaredButNotAssigned;console.log(declaredButNotAssigned);// => undefineddeclaredButNotAssigned=true;}// using const and letfunctionexample(){console.log(declaredButNotAssigned);// => throws a ReferenceErrorconsole.log(typeofdeclaredButNotAssigned);// => throws a ReferenceErrorconstdeclaredButNotAssigned=true;}
14.2 Anonymous function expressions hoist their variable name, but not the function assignment.
functionexample(){console.log(anonymous);// => undefinedanonymous();// => TypeError anonymous is not a functionvaranonymous=function(){console.log('anonymous function expression');};}
14.3 Named function expressions hoist the variable name, not the function name or the function body.
functionexample(){console.log(named);// => undefinednamed();// => TypeError named is not a functionsuperPower();// => ReferenceError superPower is not definedvarnamed=functionsuperPower(){console.log('Flying');};}// the same is true when the function name// is the same as the variable name.functionexample(){console.log(named);// => undefinednamed();// => TypeError named is not a functionvarnamed=functionnamed(){console.log('named');}}
14.4 Function declarations hoist their name and the function body.
functionexample(){superPower();// => FlyingfunctionsuperPower(){console.log('Flying');}}
For more information refer to JavaScript Scoping & Hoisting by Ben Cherry.
15.1 Use
===and!==over==and!=.15.2 Conditional statements such as the
ifstatement evaluate their expression using coercion with theToBooleanabstract 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
if([0]){// true// An array is an object, objects evaluate to true}
15.3 Use shortcuts.
// badif(name!==''){// ...stuff...}// goodif(name){// ...stuff...}// badif(collection.length>0){// ...stuff...}// goodif(collection.length){// ...stuff...}
15.4 For more information see Truth Equality and JavaScript by Angus Croll.
16.1 Use braces with all multi-line blocks.
// badif(test)returnfalse;// goodif(test)returnfalse;// goodif(test){returnfalse;}// badfunction(){returnfalse;}// goodfunction(){returnfalse;}
16.2 If you're using multi-line blocks with
ifandelse, putelseon the same line as yourifblock's closing brace.// badif(test){thing1();thing2();}else{thing3();}// goodif(test){thing1();thing2();}else{thing3();}
17.1 Use
/** ... */for multi-line comments. Include a description, specify types and values for all parameters and return values.// bad// make() returns a new element// based on the passed in tag name//// @param {String} tag// @return {Element} elementfunctionmake(tag){// ...stuff...returnelement;}// good/** * make() returns a new element * based on the passed in tag name * * @param {String} tag * @return {Element} element */functionmake(tag){// ...stuff...returnelement;}
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.// badconstactive=true;// is current tab// good// is current tabconstactive=true;// badfunctiongetType(){console.log('fetching type...');// set the default type to 'no type'consttype=this._type||'no type';returntype;}// goodfunctiongetType(){console.log('fetching type...');// set the default type to 'no type'consttype=this._type||'no type';returntype;}
17.3 Prefixing your comments with
FIXMEorTODOhelps 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 areFIXME -- need to figure this outorTODO -- need to implement.17.4 Use
// FIXME:to annotate problems.classCalculator{constructor(){// FIXME: shouldn't use a global heretotal=0;}}
17.5 Use
// TODO:to annotate solutions to problems.classCalculator{constructor(){// TODO: total should be configurable by an options paramthis.total=0;}}
18.1 Use soft tabs set to 2 spaces.
// badfunction(){∙∙∙∙constname;}// badfunction(){∙constname;}// goodfunction(){∙∙constname;}
18.2 Place 1 space before the leading brace.
// badfunctiontest(){console.log('test');}// goodfunctiontest(){console.log('test');}// baddog.set('attr',{age: '1 year',breed: 'Bernese Mountain Dog',});// gooddog.set('attr',{age: '1 year',breed: 'Bernese Mountain Dog',});
18.3 Place 1 space before the opening parenthesis in control statements (
if,whileetc.). Place no space before the argument list in function calls and declarations.// badif(isJedi){fight();}// goodif(isJedi){fight();}// badfunctionfight(){console.log('Swooosh!');}// goodfunctionfight(){console.log('Swooosh!');}
18.4 Set off operators with spaces.
// badconstx=y+5;// goodconstx=y+5;
18.5 End files with a single newline character.
// bad(function(global){// ...stuff...})(this);
// bad(function(global){// ...stuff...})(this);↵↵
// good(function(global){// ...stuff...})(this);↵
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.
// 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();// badconstleds=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);// goodconstleds=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 Leave a blank line after blocks and before the next statement.
// badif(foo){returnbar;}returnbaz;// goodif(foo){returnbar;}returnbaz;// badconstobj={foo(){},bar(){},};returnobj;// goodconstobj={foo(){},bar(){},};returnobj;
19.1 Leading commas: Nope.
// badconststory=[once,upon,aTime];// goodconststory=[once,upon,aTime,];// badconsthero={firstName: 'Ada',lastName: 'Lovelace',birthYear: 1815,superPower: 'computers'};// goodconsthero={firstName: 'Ada',lastName: 'Lovelace',birthYear: 1815,superPower: 'computers',};
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 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',
];
```
20.1Yup.
// bad(function(){constname='Skywalker'returnname})()// good(()=>{constname='Skywalker';returnname;})();// good (guards against the function becoming an argument when two files with IIFEs are concatenated);(()=>{constname='Skywalker';returnname;})();
21.1 Perform type coercion at the beginning of the statement.
21.2 Strings:
// => this.reviewScore = 9;// badconsttotalScore=this.reviewScore+'';// goodconsttotalScore=String(this.reviewScore);
21.3 Use
parseIntfor Numbers and always with a radix for type casting.constinputValue='4';// badconstval=newNumber(inputValue);// badconstval=+inputValue;// badconstval=inputValue>>0;// badconstval=parseInt(inputValue);// goodconstval=Number(inputValue);// goodconstval=parseInt(inputValue,10);
21.4 If for whatever reason you are doing something wild and
parseIntis your bottleneck and need to use Bitshift for performance reasons, leave a comment explaining why and what you're doing.// good/** * parseInt was the reason my code was slow. * Bitshifting the String to coerce it to a * Number made it a lot faster. */constval=inputValue>>0;
21.5Note: Be careful when using bitshift operations. Numbers are represented as 64-bit values, but Bitshift operations always return a 32-bit integer (source). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. Discussion. Largest signed 32-bit Int is 2,147,483,647:
2147483647>>0//=> 21474836472147483648>>0//=> -21474836482147483649>>0//=> -2147483647
21.6 Booleans:
constage=0;// badconsthasAge=newBoolean(age);// goodconsthasAge=Boolean(age);// goodconsthasAge=!!age;
22.1 Avoid single letter names. Be descriptive with your naming.
// badfunctionq(){// ...stuff...}// goodfunctionquery(){// ..stuff..}
22.2 Use camelCase when naming objects, functions, and instances.
// badconstOBJEcttsssss={};constthis_is_my_object={};functionc(){}// goodconstthisIsMyObject={};functionthisIsMyFunction(){}
22.3 Use PascalCase when naming constructors or classes.
// badfunctionuser(options){this.name=options.name;}constbad=newuser({name: 'nope',});// goodclassUser{constructor(options){this.name=options.name;}}constgood=newUser({name: 'yup',});
22.4 Use a leading underscore
_when naming private properties.// badthis.__firstName__='Panda';this.firstName_='Panda';// goodthis._firstName='Panda';
22.5 Don't save references to
this. Use arrow functions or Function#bind.// badfunctionfoo(){constself=this;returnfunction(){console.log(self);};}// badfunctionfoo(){constthat=this;returnfunction(){console.log(that);};}// goodfunctionfoo(){return()=>{console.log(this);};}
22.6 If your file exports a single class, your filename should be exactly the name of the class.
// file contentsclassCheckBox{// ...}exportdefaultCheckBox;// in some other file// badimportCheckBoxfrom'./checkBox';// badimportCheckBoxfrom'./check_box';// goodimportCheckBoxfrom'./CheckBox';
22.7 Use camelCase when you export-default a function. Your filename should be identical to your function's name.
functionmakeStyleGuide(){}exportdefaultmakeStyleGuide;
22.8 Use PascalCase when you export a singleton / function library / bare object.
constAirbnbStyleGuide={es6: {}};exportdefaultAirbnbStyleGuide;
23.1 Accessor functions for properties are not required.
23.2 If you do make accessor functions use getVal() and setVal('hello').
// baddragon.age();// gooddragon.getAge();// baddragon.age(25);// gooddragon.setAge(25);
23.3 If the property is a
boolean, useisVal()orhasVal().// badif(!dragon.age()){returnfalse;}// goodif(!dragon.hasAge()){returnfalse;}
23.4 It's okay to create get() and set() functions, but be consistent.
classJedi{constructor(options={}){constlightsaber=options.lightsaber||'blue';this.set('lightsaber',lightsaber);}set(key,val){this[key]=val;}get(key){returnthis[key];}}
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:
// bad$(this).trigger('listingUpdated',listing.id); ... $(this).on('listingUpdated',function(e,listingId){// do something with listingId});
prefer:
// good$(this).trigger('listingUpdated',{listingId : listing.id}); ... $(this).on('listingUpdated',function(e,data){// do something with data.listingId});
25.1 Prefix jQuery object variables with a
$.// badconstsidebar=$('.sidebar');// goodconst$sidebar=$('.sidebar');
25.2 Cache jQuery lookups.
// badfunctionsetSidebar(){$('.sidebar').hide();// ...stuff...$('.sidebar').css({'background-color': 'pink'});}// goodfunctionsetSidebar(){const$sidebar=$('.sidebar');$sidebar.hide();// ...stuff...$sidebar.css({'background-color': 'pink'});}
25.3 For DOM queries use Cascading
$('.sidebar ul')or parent > child$('.sidebar > ul'). jsPerf25.4 Use
findwith scoped jQuery object queries.// bad$('ul','.sidebar').hide();// bad$('.sidebar').find('ul').hide();// good$('.sidebar ul').hide();// good$('.sidebar > ul').hide();// good$sidebar.find('ul').hide();
- 26.1 Refer to Kangax's ES5 compatibility table.
- 27.1 This is a collection of links to the various es6 features.
- Arrow Functions
- Classes
- Object Shorthand
- Object Concise
- Object Computed Properties
- Template Strings
- Destructuring
- Default Parameters
- Rest
- Array Spreads
- Let and Const
- Iterators and Generators
- Modules
28.1Yup.
function(){returntrue;}
- On Layout & Web Performance
- String vs Array Concat
- Try/Catch Cost In a Loop
- Bang Function
- jQuery Find vs Context, Selector
- innerHTML vs textContent for script text
- Long String Concatenation
- Loading...
Learning ES6
Read This
Tools
- Code Style Linters
Other Style Guides
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
Other Styles
- Naming this in nested functions - Christian Johansen
- Conditional Callbacks - Ross Allen
- Popular JavaScript Coding Conventions on Github - JeongHoon Byun
- Multiple var statements in JavaScript, not superfluous - Ben Alman
Further Reading
- Understanding JavaScript Closures - Angus Croll
- Basic JavaScript for the impatient programmer - Dr. Axel Rauschmayer
- You Might Not Need jQuery - Zack Bloom & Adam Schwartz
- ES6 Features - Luke Hoban
- Frontend Guidelines - Benjamin De Cock
Books
- JavaScript: The Good Parts - Douglas Crockford
- JavaScript Patterns - Stoyan Stefanov
- Pro JavaScript Design Patterns - Ross Harmes and Dustin Diaz
- High Performance Web Sites: Essential Knowledge for Front-End Engineers - Steve Souders
- Maintainable JavaScript - Nicholas C. Zakas
- JavaScript Web Applications - Alex MacCaw
- Pro JavaScript Techniques - John Resig
- Smashing Node.js: JavaScript Everywhere - Guillermo Rauch
- Secrets of the JavaScript Ninja - John Resig and Bear Bibeault
- Human JavaScript - Henrik Joreteg
- Superhero.js - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
- JSBooks - Julien Bouquillon
- Third Party JavaScript - Ben Vinegar and Anton Kovalyov
- Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript - David Herman
- Eloquent JavaScript - Marijn Haverbeke
Blogs
- DailyJS
- JavaScript Weekly
- JavaScript, JavaScript...
- Bocoup Weblog
- Adequately Good
- NCZOnline
- Perfection Kills
- Ben Alman
- Dmitry Baranovskiy
- Dustin Diaz
- nettuts
Podcasts
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
- Adult Swim: adult-swim/javascript
- Airbnb: airbnb/javascript
- Apartmint: apartmint/javascript
- Avalara: avalara/javascript
- Billabong: billabong/javascript
- Blendle: blendle/javascript
- Compass Learning: compasslearning/javascript-style-guide
- DailyMotion: dailymotion/javascript
- Digitpaintdigitpaint/javascript
- Evernote: evernote/javascript-style-guide
- ExactTarget: ExactTarget/javascript
- ExpensifyExpensify/Style-Guide
- Flexberry: Flexberry/javascript-style-guide
- Gawker Media: gawkermedia/javascript
- General Electric: GeneralElectric/javascript
- GoodData: gooddata/gdc-js-style
- Grooveshark: grooveshark/javascript
- How About We: howaboutwe/javascript
- InfoJobs: InfoJobs/JavaScript-Style-Guide
- Intent Media: intentmedia/javascript
- Jam3: Jam3/Javascript-Code-Conventions
- JSSolutions: JSSolutions/javascript
- Kinetica Solutions: kinetica/javascript
- Mighty Spring: mightyspring/javascript
- MinnPost: MinnPost/javascript
- ModCloth: modcloth/javascript
- Money Advice Service: moneyadviceservice/javascript
- Muber: muber/javascript
- National Geographic: natgeo/javascript
- National Park Service: nationalparkservice/javascript
- Nimbl3: nimbl3/javascript
- Orion Health: orionhealth/javascript
- Peerby: Peerby/javascript
- Razorfish: razorfish/javascript-style-guide
- reddit: reddit/styleguide/javascript
- REI: reidev/js-style-guide
- Ripple: ripple/javascript-style-guide
- SeekingAlpha: seekingalpha/javascript-style-guide
- Shutterfly: shutterfly/javascript
- StudentSphere: studentsphere/javascript
- Target: target/javascript
- TheLadders: TheLadders/javascript
- T4R Technology: T4R-Technology/javascript
- VoxFeed: VoxFeed/javascript-style-guide
- Weggo: Weggo/javascript
- Zillow: zillow/javascript
- ZocDoc: ZocDoc/javascript
This style guide is also available in other languages:
Brazilian Portuguese: armoucar/javascript-style-guide
Bulgarian: borislavvv/javascript
Catalan: fpmweb/javascript-style-guide
Chinese(Traditional): jigsawye/javascript
Chinese(Simplified): sivan/javascript-style-guide
French: nmussy/javascript-style-guide
German: timofurrer/javascript-style-guide
Italian: sinkswim/javascript-style-guide
Japanese: mitsuruog/javacript-style-guide
Korean: tipjs/javascript-style-guide
Polish: mjurczyk/javascript
Russian: uprock/javascript
Spanish: paolocarrasco/javascript-style-guide
Thai: lvarayut/javascript-style-guide
- Find us on gitter.
(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.