GitHub - dvasuta/javascript: JavaScript Style Guide
**[[⬆]](#TOC)**
## <a name='whitespace'>Whitespace</a>
- Use soft tabs set to 2 spaces
```javascript
// bad
function∙() {
∙∙∙∙var name;
}
// bad
function() {
∙var name;
}
// good
function∙() {
∙∙var name;
}
```
- No end of line whitespace.
- No blank line whitespace.
- Place one space before and after binary and ternary operators.
```javascript
var a∙=∙1∙+∙2,
b∙=∙a++,
c∙=∙a∙>∙0∙?∙'yes'∙:∙'no';
```
- 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 an empty newline at the end of the file.
```javascript
// bad
(function (global) {
// ...stuff...
})(this);
```
```javascript
// good
(function (global) {
// ...stuff...
})(this);
```
- Use indentation when making long method chains.
```javascript
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
// bad
var 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
var 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);
```
**[[⬆]](#TOC)**
## <a name='leading-commas'>Leading Commas</a>
- **Nope.**
```javascript
// bad
var once
, upon
, aTime;
// good
var once,
upon,
aTime;
// bad
var hero = {
firstName: 'Bob'
, lastName: 'Parr'
, heroName: 'Mr. Incredible'
, superPower: 'strength'
};
// good
var hero = {
firstName: 'Bob',
lastName: 'Parr',
heroName: 'Mr. Incredible',
superPower: 'strength'
};
```
**[[⬆]](#TOC)**
## <a name='semicolons'>Semicolons</a>
- **Yup.**
```javascript
// bad
(function () {
var name = 'Skywalker'
return name
})()
// good
(function () {
var name = 'Skywalker';
return name;
})();
// good
;(function () {
var name = 'Skywalker';
return name;
})();
```
**[[⬆]](#TOC)**
## <a name='type-coercion'>Type Casting & Coercion</a>
- Perform type coercion at the beginning of the statement.
- Strings:
```javascript
// => this.reviewScore = 9;
// bad
var totalScore = this.reviewScore + '';
// good
var totalScore = '' + this.reviewScore;
// bad
var totalScore = '' + this.reviewScore + ' total score';
// good
var totalScore = this.reviewScore + ' total score';
```
- Use `parseInt` for Numbers and always with a radix for type casting.
- 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
var inputValue = '4';
// bad
var val = new Number(inputValue);
// bad
var val = +inputValue;
// bad
var val = inputValue >> 0;
// bad
var val = parseInt(inputValue);
// good
var val = Number(inputValue);
// good
var val = parseInt(inputValue, 10);
// good
/**
* parseInt was the reason my code was slow.
* Bitshifting the String to coerce it to a
* Number made it a lot faster.
*/
var val = inputValue >> 0;
```
- Booleans:
```javascript
var age = 0;
// bad
var hasAge = new Boolean(age);
// good
var hasAge = Boolean(age);
// good
var hasAge = !!age;
```
**[[⬆]](#TOC)**
## <a name='naming-conventions'>Naming Conventions</a>
- Name variables and functions logically: For example: `popUpWindowForAd` rather than `myWindow`.
- All Boolean variables should start with "is".
```javascript
isValid = (test.value >= 4 && test.success);
```
- 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
var OBJEcttsssss = {};
var this_is_my_object = {};
var this-is-my-object = {};
function c() {};
var u = new user({
name: 'Bob Parr'
});
// good
var thisIsMyObject = {};
function thisIsMyFunction() {};
var user = new User({
name: 'Bob Parr'
});
```
- Use PascalCase when naming constructors or classes
```javascript
// bad
function user(options) {
this.name = options.name;
}
var bad = new user({
name: 'nope'
});
// good
function User(options) {
this.name = options.name;
}
var 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';
```
- When saving a reference to `this` use `_this`.
```javascript
// bad
function () {
var self = this;
return function() {
console.log(self);
};
}
// bad
function () {
var that = this;
return function() {
console.log(that);
};
}
// good
function () {
var _this = this;
return function() {
console.log(_this);
};
}
```
- Name your functions. This is helpful for stack traces.
```javascript
// bad
var log = function (msg) {
console.log(msg);
};
// good
var log = function log (msg) {
console.log(msg);
};
```
- Constant values
If a value is intended to be constant and immutable, it should be given a name in `CONSTANT_VALUE_CASE`. `ALL_CAPS` additionally implies `@const` (that the value is not overwritable).
Primitive types (number, string, boolean) are constant values.
Objects' immutabilty is more subjective — objects should be considered immutable only if they do not demonstrate obeserverable state change. This is not enforced by the compiler.
- Constant pointers (variables and properties)
The `@const` annotation on a variable or property implies that it is not overwritable. This is enforced by the compiler at build time. This behavior is consistent with the const keyword (which we do not use due to the lack of support in Internet Explorer).
A `@const` annotation on a method additionally implies that the method should not be overriden in subclasses.
**Examples**
Note that `@const` does not necessarily imply `CONSTANT_VALUES_CASE`. However, `CONSTANT_VALUES_CASE` does imply `@const`.
```javascript
/**
* Request timeout in milliseconds.
* @type {number}
*/
goog.example.TIMEOUT_IN_MILLISECONDS = 60;
```
The number of seconds in a minute never changes. It is a constant value. `ALL_CAPS` also implies `@const`, so the constant cannot be overwritten.
The open source compiler will allow the symbol it to be overwritten because the constant is not marked as `@const`.
```javascript
/**
* Map of URL to response string.
* @const
*/
MyClass._fetchedUrlCache = new goog.structs.Map();
```
In this case, the pointer can never be overwritten, but value is highly mutable and not constant (and thus in `camelCase`, not `ALL_CAPS`).
**[[⬆]](#TOC)**
## <a name='accessors'>Accessors</a>
- 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
function Jedi (options) {
options || (options = {});
var lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
Jedi.prototype.set = function (key, val) {
this[key] = val;
};
Jedi.prototype.get = function (key) {
return this[key];
};
```
**[[⬆]](#TOC)**
## <a name='constructors'>Constructors</a>
- Assign methods to the prototype object, instead of overwriting the prototype with a new object. Overwriting the prototype makes inheritance impossible: by resetting the prototype you'll overwrite the base!
```javascript
function Jedi () {
console.log('new jedi');
}
// bad
Jedi.prototype = {
fight: function fight () {
console.log('fighting');
},
block: function block () {
console.log('blocking');
}
};
// good
Jedi.prototype.fight = function fight () {
console.log('fighting');
};
Jedi.prototype.block = function block () {
console.log('blocking');
};
```
- 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;
};
var luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20) // => undefined
// good
Jedi.prototype.jump = function () {
this.jumping = true;
return this;
};
Jedi.prototype.setHeight = function (height) {
this.height = height;
return this;
};
var 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
function Jedi (options) {
options || (options = {});
this.name = options.name || 'no name';
}
Jedi.prototype.getName = function getName () {
return this.name;
};
Jedi.prototype.toString = function toString () {
return 'Jedi - ' + this.getName();
};
```
**[[⬆]](#TOC)**
## <a name='modules'>Modules</a>
- The module should start with a `!`. This ensures that if a malformed module forgets to include a final semicolon there aren't errors in production when the scripts get concatenated.
- The file should be named with camelCase, live in a folder with the same name, and match the name of the single export.
- Add a method called `noConflict()` that sets the exported module to the previous version and returns this one.
- Always declare `'use strict';` at the top of the module.
```javascript
// fancyInput/fancyInput.js
!function(global) {
'use strict';
var previousFancyInput = global.FancyInput;
function FancyInput (options) {
this.options = options || {};
}
FancyInput.noConflict = function noConflict () {
global.FancyInput = previousFancyInput;
return FancyInput;
};
global.FancyInput = FancyInput;
}(this);
```
**[[⬆]](#TOC)**
## <a name='jquery'>jQuery</a>
- Prefix jQuery object variables with a `$`.
```javascript
// bad
var sidebar = $('.sidebar');
// good
var $sidebar = $('.sidebar');
```
- Cache jQuery lookups.
```javascript
// bad
function setSidebar () {
$('.sidebar').hide();
// ...stuff...
$('.sidebar').css({
'background-color': 'pink'
});
}
// good
function setSidebar () {
var $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
$('.sidebar', 'ul').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good (slower)
$sidebar.find('ul');
// good (faster)
$($sidebar[0]).find('ul');
```
**[[⬆]](#TOC)**
## <a name='es5'>ECMAScript 5 Compatibility</a>
- Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/)
**[[⬆]](#TOC)**
## <a name='testing'>Testing</a>
- **Yup.**
```javascript
function () {
return true;
}
```
**[[⬆]](#TOC)**
## <a name='performance'>Performance</a>
- Prefer `this.foo = null`.
```javascript
Foo.prototype.dispose = function dispose () {
this.property_ = null;
};
```
Instead of:
```javascript
Foo.prototype.dispose = function dispose () {
delete this.property_;
};
```
In modern JavaScript engines, changing the number of properties on an object is much slower than reassigning the values. The delete keyword should be avoided except when it is necessary to remove a property from an object's iterated list of keys, or to change the result of `if (key in obj)`.
- Closures
Yes, but be careful.
The ability to create closures is perhaps the most useful and often overlooked feature of JS. Here is a good description of how closures work .
One thing to keep in mind, however, is that a closure keeps a pointer to its enclosing scope. As a result, attaching a closure to a DOM element can create a circular reference and thus, a memory leak. For example, in the following code:
```javascript
function foo (element, a, b) {
element.onclick = function() { /* uses a and b */ };
}
```
the function closure keeps a reference to `element`, `a`, and `b` even if it never uses element. Since element also keeps a reference to the closure, we have a cycle that won't be cleaned up by garbage collection. In these situations, the code can be structured as follows:
```javascript
function foo(element, a, b) {
element.onclick = bar(a, b);
}
function bar(a, b) {
return function() { /* uses a and b */ }
}
```
* * *
- [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...
**[[⬆]](#TOC)**
## <a name='resources'>Resources</a>
**Read This**
- [Annotated ECMAScript 5.1](http://es5.github.com/)
**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)
**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
**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)
**[[⬆]](#TOC)**
## <a name='license'>License</a>
(The MIT License)
Copyright (c) 2012 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.
**[[⬆]](#TOC)**
# };