From 9bd6a5b4424ebf069c8323fe1323ec99919fe623 Mon Sep 17 00:00:00 2001 From: Kevin Lanni Date: Thu, 8 Nov 2012 14:54:22 -0700 Subject: [PATCH 1/9] some minor changes --- README.md | 2588 ++++++++++++++++++++++++++--------------------------- 1 file changed, 1280 insertions(+), 1308 deletions(-) diff --git a/README.md b/README.md index 839b35ef51..b54b3ee67a 100644 --- a/README.md +++ b/README.md @@ -1,1308 +1,1280 @@ -# Airbnb JavaScript Style Guide() { - -*A mostly reasonable approach to JavaScript* - - -## Table of Contents - - 1. [Types](#types) - 1. [Objects](#objects) - 1. [Arrays](#arrays) - 1. [Strings](#strings) - 1. [Functions](#functions) - 1. [Properties](#properties) - 1. [Variables](#variables) - 1. [Hoisting](#hoisting) - 1. [Conditional Expressions & Equality](#conditionals) - 1. [Blocks](#blocks) - 1. [Comments](#comments) - 1. [Whitespace](#whitespace) - 1. [Leading Commas](#leading-commas) - 1. [Semicolons](#semicolons) - 1. [Type Casting & Coercion](#type-coercion) - 1. [Naming Conventions](#naming-conventions) - 1. [Accessors](#accessors) - 1. [Constructors](#constructors) - 1. [Modules](#modules) - 1. [jQuery](#jquery) - 1. [ES5 Compatability](#es5) - 1. [Testing](#testing) - 1. [Performance](#performance) - 1. [Resources](#resources) - 1. [The JavaScript Style Guide Guide](#guide-guide) - 1. [Contributors](#contributors) - 1. [License](#license) - -## Types - - - **Primitives**: When you access a primitive type you work directly on its value - - + `String` - + `Number` - + `Boolean` - + `null` - + `undefined` - - ```javascript - var foo = 1, - bar = foo; - - bar = 9; - - console.log(foo, bar); // => 1, 9 - ``` - - **Complex**: When you access a complex type you work on a reference to its value - - + `Object` - + `Array` - + `Function` - - ```javascript - var foo = [1, 2], - bar = foo; - - bar[0] = 9; - - console.log(foo[0], bar[0]); // => 9, 9 - ``` - - **[[⬆]](#TOC)** - -## Objects - - - Use the literal syntax for object creation. - - ```javascript - // bad - var item = new Object(); - - // good - var item = {}; - ``` - - - Don't use [reserved words](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words) as keys. - - ```javascript - // bad - var superman = { - class: 'superhero', - default: { clark: kent }, - private: true - }; - - // good - var superman = { - klass: 'superhero', - defaults: { clark: kent }, - hidden: true - }; - ``` - **[[⬆]](#TOC)** - -## Arrays - - - Use the literal syntax for array creation - - ```javascript - // bad - var items = new Array(); - - // good - var items = []; - ``` - - - For [performance reasons](http://jsperf.com/array-direct-assignment-vs-push/5) use direct assignment over Array#push - - ```javascript - var len = items.length, - itemsCopy = [], - i; - - // bad - for (i = 0; i < len; i++) { - itemsCopy.push(items[i]) - } - - // good - for (i = 0; i < len; i++) { - itemsCopy[i] = items[i]; - } - ``` - - **[[⬆]](#TOC)** - - -## Strings - - - Use single quotes `''` for strings - - ```javascript - // bad - var name = "Bob Parr"; - - // good - var name = 'Bob Parr'; - - // bad - var fullName = "Bob" + this.lastName; - - // good - var fullName = 'Bob' + this.lastName; - ``` - - - Strings longer than 80 characters should be written across multiple lines using string concatenation. - - ```javascript - // bad - var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; - - // bad - var errorMessage = 'This is a super long error that \ - was thrown because of Batman. \ - When you stop to think about \ - how Batman had anything to do \ - with this, you would get nowhere \ - fast.'; - - - // good - var errorMessage = 'This is a super long error that ' + - 'was thrown because of Batman.' + - 'When you stop to think about ' + - 'how Batman had anything to do ' + - 'with this, you would get nowhere ' + - 'fast.'; - ``` - - - When programatically building up a string, use Array#join instead of string concatenation. Mostly for IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). - - ```javascript - var items, - messages, - length, i; - - messages = [{ - state: 'success', - message: 'This one worked.' - },{ - state: 'success', - message: 'This one worked as well.' - },{ - state: 'error', - message: 'This one did not work.' - }]; - - length = messages.length; - - // bad - function inbox(messages) { - items = ''; - } - - // good - function inbox(messages) { - items = []; - - for (i = 0; i < length; i++) { - items[i] = '
  • ' + messages[i].message + '
  • '; - } - - return ''; - } - ``` - - **[[⬆]](#TOC)** - - -## Functions - - - Function expressions: - - ```javascript - // anonymous function expression - var anonymous = function() { - return true; - }; - - // named function expression - var named = function named() { - return true; - }; - - // immediately-invoked function expression (IIFE) - (function() { - 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. - - ```javascript - // bad - if (currentUser) { - function test() { - console.log('Nope.'); - } - } - - // good - if (currentUser) { - var test = function test() { - console.log('Yup.'); - }; - } - ``` - - - Never name a parameter `arguments`, this will take precendence 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... - } - ``` - - **[[⬆]](#TOC)** - - - -## Properties - - - Use dot notation when accessing properties. - - ```javascript - var luke = { - jedi: true, - age: 28 - }; - - // bad - var isJedi = luke['jedi']; - - // good - var isJedi = luke.jedi; - ``` - - - Use subscript notation `[]` when accessing properties with a variable. - - ```javascript - var luke = { - jedi: true, - age: 28 - }; - - function getProp(prop) { - return luke[prop]; - } - - var isJedi = getProp('jedi'); - ``` - - **[[⬆]](#TOC)** - - -## Variables - - - Always use `var` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. - - ```javascript - // bad - superPower = new SuperPower(); - - // good - var superPower = new SuperPower(); - ``` - - - Use one `var` declaration for multiple variables and declare each variable on a newline. - - ```javascript - // bad - var items = getItems(); - var goSportsTeam = true; - var dragonball = 'z'; - - // good - var items = getItems(), - goSportsTeam = true, - dragonball = 'z'; - ``` - - - Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. - - ```javascript - // bad - var i, len, dragonball, - items = getItems(), - goSportsTeam = true; - - // bad - var i, items = getItems(), - dragonball, - goSportsTeam = true, - len; - - // good - var items = getItems(), - goSportsTeam = true, - dragonball, - i, length; - ``` - - - Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues. - - ```javascript - // bad - function() { - test(); - console.log('doing stuff..'); - - //..other stuff.. - - var name = getName(); - - if (name === 'test') { - return false; - } - - return name; - } - - // good - function() { - var name = getName(); - - test(); - console.log('doing stuff..'); - - //..other stuff.. - - if (name === 'test') { - return false; - } - - return name; - } - - // bad - function() { - var name = getName(); - - if (!arguments.length) { - return false; - } - - return true; - } - - // good - function() { - if (!arguments.length) { - return false; - } - - var name = getName(); - - return true; - } - ``` - - **[[⬆]](#TOC)** - - -## Hoisting - - - Variable declarations get hoisted to the top of their scope, their assignment does not. - - ```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 interpretor is hoisting the variable - // declaration to the top of the scope. - // Which means our example could be rewritten as: - function example() { - var declaredButNotAssigned; - console.log(declaredButNotAssigned); // => undefined - declaredButNotAssigned = true; - } - ``` - - - Anonymous function expression 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/) - - **[[⬆]](#TOC)** - - - -## Conditional Expressions & Equality - - - Use `===` and `!==` over `==` and `!=`. - - Conditional expressions 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** evalute 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 - - **[[⬆]](#TOC)** - - -## 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; - } - ``` - - **[[⬆]](#TOC)** - - -## Comments - - - Use `/** ... */` for multiline comments. Include a description, specify types and values for all parameters and return values. - - ```javascript - // bad - // make() returns a new element - // based on the pased in tag name - // - // @param tag - // @return element - function make(tag) { - - // ...stuff... - - return element; - } - - // good - /** - * make() returns a new element - * based on the pased in tag name - * - * @param tag - * @return 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 emptyline before the comment. - - ```javascript - // bad - var active = true; // is current tab - - // good - // is current tab - var active = true; - - // bad - function getType() { - console.log('fetching type...'); - // set the default type to 'no type' - var type = this._type || 'no type'; - - return type; - } - - // good - function getType() { - console.log('fetching type...'); - - // set the default type to 'no type' - var type = this._type || 'no type'; - - return type; - } - ``` - - **[[⬆]](#TOC)** - - -## Whitespace - - - Use soft tabs set to 2 spaces - - ```javascript - // bad - function() { - ∙∙∙∙var name; - } - - // bad - function() { - ∙ var name; - } - - // good - function() { - ∙∙var 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 an empty newline at the end of the file. - - ```javascript - // bad - (function(global) { - // ...stuff... - })(this); - ``` - - ```javascript - // good - (function(global) { - // ...stuff... - })(this); - - ``` - - **[[⬆]](#TOC)** - - - 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); - ``` - -## Leading Commas - - - **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)** - - -## Semicolons - - - **Yup.** - - ```javascript - // bad - (function() { - var name = 'Skywalker' - return name - })() - - // good - (function() { - var name = 'Skywalker'; - return name; - })(); - - // good - ;(function() { - var name = 'Skywalker'; - return name; - })(); - ``` - - **[[⬆]](#TOC)** - - -## Type Casting & Coercion - - - 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)** - - -## 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 - 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'; - ``` - - - 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); - }; - ``` - - **[[⬆]](#TOC)** - - -## 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 - 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)** - - -## Constructors - - - 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'); - }; - ``` - - - Constructor methods should try to return `this`. This helps with method chaining which is often useful. - - ```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)** - - -## Modules - - - 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. - - 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) { - options || (options = {}); - } - - FancyInput.noConflict = function noConflict() { - global.FancyInput = previousFancyInput; - }; - - global.FancyInput = FancyInput; - }(this); - ``` - - **[[⬆]](#TOC)** - - -## jQuery - - - 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' - }); - } - ``` - - - Scope jQuery object queries with find. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/13) - - ```javascript - // bad - $('.sidebar ul').hide(); - - // bad - $('.sidebar > ul').hide(); - - // bad - $('.sidebar', 'ul').hide(); - - // good - $('.sidebar').find('ul').hide(); - ``` - - **[[⬆]](#TOC)** - - -## ECMAScript 5 Compatability - - - Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) - - **[[⬆]](#TOC)** - - -## Testing - - - **Yup.** - - ```javascript - function() { - return true; - } - ``` - - **[[⬆]](#TOC)** - - -## 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) - - Loading... - - **[[⬆]](#TOC)** - - -## Resources - - -**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/) - -**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](//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/) - - [net.tutsplus](http://net.tutsplus.com/?s=javascript) - - **[[⬆]](#TOC)** - -## The JavaScript Style Guide Guide - - - [Reference](//github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) - -## Contributors - - - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) - - -## License - -(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)** - -# }; - +# JavaScript Style Guide() { + +*A mostly reasonable approach to JavaScript* + +This version of the style guide includes my own personal style preferences. The main differences are style asthetics. + + +## Table of Contents + + 1. [Types](#types) + 1. [Objects](#objects) + 1. [Arrays](#arrays) + 1. [Strings](#strings) + 1. [Functions](#functions) + 1. [Properties](#properties) + 1. [Variables](#variables) + 1. [Hoisting](#hoisting) + 1. [Conditional Expressions & Equality](#conditionals) + 1. [Blocks](#blocks) + 1. [Comments](#comments) + 1. [Whitespace](#whitespace) + 1. [Leading Commas](#leading-commas) + 1. [Semicolons](#semicolons) + 1. [Type Casting & Coercion](#type-coercion) + 1. [Naming Conventions](#naming-conventions) + 1. [Accessors](#accessors) + 1. [Constructors](#constructors) + 1. [Modules](#modules) + 1. [jQuery](#jquery) + 1. [ES5 Compatability](#es5) + 1. [Testing](#testing) + 1. [Performance](#performance) + 1. [Resources](#resources) + 1. [The JavaScript Style Guide Guide](#guide-guide) + 1. [Contributors](#contributors) + 1. [License](#license) + +## Types + + - **Primitives**: When you access a primitive type you work directly on its value + + + `String` + + `Number` + + `Boolean` + + `null` + + `undefined` + + ```javascript + var foo = 1, + bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - **Complex**: When you access a complex type you work on a reference to its value + + + `Object` + + `Array` + + `Function` + + ```javascript + var foo = [1, 2], + bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + + **[[⬆]](#TOC)** + +## Objects + + - Use the literal syntax for object creation. + + ```javascript + // bad + var item = new Object(); + + // good + var item = {}; + ``` + + - Don't use [reserved words](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words) as keys. + + ```javascript + // bad + var superman = { + class: 'superhero', + default: { clark: kent }, + private: true + }; + + // good + var superman = { + klass: 'superhero', + defaults: { clark: kent }, + hidden: true + }; + ``` + **[[⬆]](#TOC)** + +## Arrays + + - Use the literal syntax for array creation + + ```javascript + // bad + var items = new Array(); + + // good + var items = []; + ``` + + - For [performance reasons](http://jsperf.com/array-direct-assignment-vs-push/5) use direct assignment over Array#push + + ```javascript + var len = items.length, + itemsCopy = [], + i; + + // bad + for (i = 0; i < len; i++) { + itemsCopy.push(items[i]) + } + + // good + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + ``` + + **[[⬆]](#TOC)** + + +## Strings + + - Use single quotes `''` for strings + + ```javascript + // bad + var name = "Bob Parr"; + + // good + var name = 'Bob Parr'; + + // bad + var fullName = "Bob" + this.lastName; + + // good + var fullName = 'Bob' + this.lastName; + ``` + + - Strings longer than 80 characters should be written across multiple lines using string concatenation. + + ```javascript + // bad + var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; + + // bad + var errorMessage = 'This is a super long error that \ + was thrown because of Batman. \ + When you stop to think about \ + how Batman had anything to do \ + with this, you would get nowhere \ + fast.'; + + + // good + var errorMessage = 'This is a super long error that ' + + 'was thrown because of Batman.' + + 'When you stop to think about ' + + 'how Batman had anything to do ' + + 'with this, you would get nowhere ' + + 'fast.'; + ``` + + - When programatically building up a string, use Array#join instead of string concatenation. Mostly for IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). + + ```javascript + var items, + messages, + length, i; + + messages = [{ + state: 'success', + message: 'This one worked.' + },{ + state: 'success', + message: 'This one worked as well.' + },{ + state: 'error', + message: 'This one did not work.' + }]; + + length = messages.length; + + // bad + function inbox(messages) { + items = '
      '; + + for (i = 0; i < length; i++) { + items += '
    • ' + messages[i].message + '
    • '; + } + + return items + '
    '; + } + + // good + function inbox(messages) { + items = []; + + for (i = 0; i < length; i++) { + items[i] = '
  • ' + messages[i].message + '
  • '; + } + + return '
      ' + items.join('') + '
    '; + } + ``` + + **[[⬆]](#TOC)** + + +## Functions + + - Function expressions: + + ```javascript + // anonymous function expression + var anonymous = function() { + return true; + }; + + // named function expression + var named = function named() { + return true; + }; + + // immediately-invoked function expression (IIFE) + (function() { + 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. + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + if (currentUser) { + var test = function test() { + console.log('Yup.'); + }; + } + ``` + + - Never name a parameter `arguments`, this will take precendence 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... + } + ``` + + **[[⬆]](#TOC)** + + + +## Properties + + - Use dot notation when accessing properties. + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + // bad + var isJedi = luke['jedi']; + + // good + var isJedi = luke.jedi; + ``` + + - Use subscript notation `[]` when accessing properties with a variable. + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + function getProp(prop) { + return luke[prop]; + } + + var isJedi = getProp('jedi'); + ``` + + **[[⬆]](#TOC)** + + +## Variables + + - Always use `var` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. + + ```javascript + // bad + superPower = new SuperPower(); + + // good + var superPower = new SuperPower(); + ``` + + - Use one `var` declaration for multiple variables and declare each variable on a newline. + + ```javascript + // bad + var items = getItems(); + var goSportsTeam = true; + var dragonball = 'z'; + + // good + var items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + ``` + + - Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. + + ```javascript + // bad + var i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + var i, items = getItems(), + dragonball, + goSportsTeam = true, + len; + + // good + var items = getItems(), + goSportsTeam = true, + dragonball, + i, length; + ``` + + - Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues. + + ```javascript + // bad + function() { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + var name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // good + function() { + var name = getName(); + + test(); + console.log('doing stuff..'); + + //..other stuff.. + + if (name === 'test') { + return false; + } + + return name; + } + + // bad + function() { + var name = getName(); + + if (!arguments.length) { + return false; + } + + return true; + } + + // good + function() { + if (!arguments.length) { + return false; + } + + var name = getName(); + + return true; + } + ``` + + **[[⬆]](#TOC)** + + +## Hoisting + + - Variable declarations get hoisted to the top of their scope, their assignment does not. + + ```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 interpretor is hoisting the variable + // declaration to the top of the scope. + // Which means our example could be rewritten as: + function example() { + var declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + ``` + + - Anonymous function expression 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/) + + **[[⬆]](#TOC)** + + + +## Conditional Expressions & Equality + + - Use `===` and `!==` over `==` and `!=`. + - Conditional expressions 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** evalute 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 + + **[[⬆]](#TOC)** + + +## 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; + } + ``` + + **[[⬆]](#TOC)** + + +## Comments + + - Use `/** ... */` for multiline comments. Include a description, specify types and values for all parameters and return values. + + ```javascript + // bad + // make() returns a new element + // based on the pased in tag name + // + // @param tag + // @return element + function make(tag) { + + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the pased in tag name + * + * @param tag + * @return 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 emptyline before the comment. + + ```javascript + // bad + var active = true; // is current tab + + // good + // is current tab + var active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + var type = this._type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + var type = this._type || 'no type'; + + return type; + } + ``` + + **[[⬆]](#TOC)** + + +## Whitespace + + - Use hard tabs set to 4 + + ```javascript + // bad + function() { + ∙∙∙∙var name; + } + + // bad + function() { + ∙ var name; + } + + // good + function() { + var name; // that's 1 tab + } + ``` + - 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); + + ``` + + **[[⬆]](#TOC)** + + - 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); + ``` + +## Leading Commas + + - **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)** + + +## Semicolons + + - **Yup.** + + ```javascript + // bad + (function() { + var name = 'Skywalker' + return name + })() + + // good + (function() { + var name = 'Skywalker'; + return name; + })(); + + // good + ;(function() { + var name = 'Skywalker'; + return name; + })(); + ``` + + **[[⬆]](#TOC)** + + +## Type Casting & Coercion + + - 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); + + // preferred + 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); + + // preferred + var hasAge = !!age; + ``` + + **[[⬆]](#TOC)** + + +## Naming Conventions + + - Avoid single letter names. Be descriptive with your naming. + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - Use camelCase (also known as headlessCamelCase) when naming objects, functions, and instances + + ```javascript + // very 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'; + ``` + + - Name your functions. This is very helpful for stack traces. + + ```javascript + // bad + var log = function(msg) { + console.log(msg); + }; + + // good + var log = function log(msg) { + console.log(msg); + }; + ``` + + **[[⬆]](#TOC)** + + +## 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 + 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)** + + +## Constructors + + - 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'); + }; + ``` + + - Constructor methods should try to return `this`. This helps with method chaining which is often useful. + + ```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)** + + +## jQuery + + - 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' + }); + } + ``` + + - Scope jQuery object queries with find. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/13) + + ```javascript + // bad + $('.sidebar ul').hide(); + + // bad + $('.sidebar > ul').hide(); + + // bad + $('.sidebar', 'ul').hide(); + + // good + $('.sidebar').find('ul').hide(); + ``` + + **[[⬆]](#TOC)** + + +## ECMAScript 5 Compatability + + - Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) + + **[[⬆]](#TOC)** + + +## Testing + + - **Yup.** + + ```javascript + function() { + return true; + } + ``` + + **[[⬆]](#TOC)** + + +## 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) + - Loading... + + **[[⬆]](#TOC)** + + +## Resources + + +**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/) + +**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](//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/) + - [net.tutsplus](http://net.tutsplus.com/?s=javascript) + + **[[⬆]](#TOC)** + +## The JavaScript Style Guide Guide + + - [Reference](//github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + +## Contributors + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## License + +(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)** + +# }; + From ac81cb6941e47aecf5570ced72cce76297c4b350 Mon Sep 17 00:00:00 2001 From: Kevin Lanni Date: Thu, 8 Nov 2012 14:55:59 -0700 Subject: [PATCH 2/9] forgot to remove Modules from TOC --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b54b3ee67a..bf5c1d3636 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ This version of the style guide includes my own personal style preferences. The 1. [Naming Conventions](#naming-conventions) 1. [Accessors](#accessors) 1. [Constructors](#constructors) - 1. [Modules](#modules) 1. [jQuery](#jquery) 1. [ES5 Compatability](#es5) 1. [Testing](#testing) From 48fc787489055ab82bb610b956283ea4c12715f7 Mon Sep 17 00:00:00 2001 From: Kevin Lanni Date: Mon, 12 Nov 2012 17:21:22 -0700 Subject: [PATCH 3/9] added pad guideline --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index bf5c1d3636..68d540bf4c 100644 --- a/README.md +++ b/README.md @@ -701,6 +701,31 @@ This version of the style guide includes my own personal style preferences. The breed: 'Bernese Mountain Dog' }); ``` + - Pad multi-line logic with empty newlines + + ```javascript + // bad + var myFunc = function myFunc() { + var myVar = [1, 2, 3], + i = myVar.length; + while (i--) { + // ... + } + doSomething(); + } + + // good + var myFunc = function myFunc() { + var myVar = [1, 2, 3], + i = myVar.length; + + while (i--) { + // ... + } + + doSomething(); + } + ``` - Place an empty newline at the end of the file. ```javascript From 93cad389ef9d7bee519272097ec930cfc91bb266 Mon Sep 17 00:00:00 2001 From: Kevin Lanni Date: Mon, 12 Nov 2012 18:01:50 -0700 Subject: [PATCH 4/9] return statements and commas also updated comment style --- README.md | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 68d540bf4c..f78c91dbfb 100644 --- a/README.md +++ b/README.md @@ -598,10 +598,9 @@ This version of the style guide includes my own personal style preferences. The // make() returns a new element // based on the pased in tag name // - // @param tag - // @return element + // @param {string} tag Tag to create + // @return {element} element New element function make(tag) { - // ...stuff... return element; @@ -612,11 +611,10 @@ This version of the style guide includes my own personal style preferences. The * make() returns a new element * based on the pased in tag name * - * @param tag - * @return element + * @param {string} tag Tag to create + * @return {element} element New element */ function make(tag) { - // ...stuff... return element; @@ -701,6 +699,14 @@ This version of the style guide includes my own personal style preferences. The breed: 'Bernese Mountain Dog' }); ``` + - Place 1 space after commas in a series. + ```javascript + // bad + var myArray = [1,2,3,4]; + + // good + var myArray = [1, 2, 3, 4]; + ``` - Pad multi-line logic with empty newlines ```javascript @@ -726,6 +732,26 @@ This version of the style guide includes my own personal style preferences. The doSomething(); } ``` + - Place an empty newline before `return` statements. + ```javascript + // bad + Jedi.prototype.slash = function(target) { + this.target = target; + return this; + } + + // good + Jedi.prototype.slash = function(target) { + this.target = target; + + return this; + } + + // acceptable + Jedi.prototype.getTarget = function(target) { + return this.target; + } + ``` - Place an empty newline at the end of the file. ```javascript @@ -819,18 +845,21 @@ This version of the style guide includes my own personal style preferences. The // bad (function() { var name = 'Skywalker' + return name })() // good (function() { var name = 'Skywalker'; + return name; })(); // good ;(function() { var name = 'Skywalker'; + return name; })(); ``` @@ -1086,6 +1115,7 @@ This version of the style guide includes my own personal style preferences. The // bad Jedi.prototype.jump = function() { this.jumping = true; + return true; }; @@ -1100,11 +1130,13 @@ This version of the style guide includes my own personal style preferences. The // good Jedi.prototype.jump = function() { this.jumping = true; + return this; }; Jedi.prototype.setHeight = function(height) { this.height = height; + return this; }; From d8f509c1a17db6a70ec31d7c899071d87f1a9d07 Mon Sep 17 00:00:00 2001 From: ewaibel Date: Fri, 16 Nov 2012 10:08:55 -0700 Subject: [PATCH 5/9] Update linters/SublimeLinter/SublimeLinter.sublime-settings Updated settings to match User settings for JSHint. --- .../SublimeLinter.sublime-settings | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/linters/SublimeLinter/SublimeLinter.sublime-settings b/linters/SublimeLinter/SublimeLinter.sublime-settings index 85df10144d..5ecb89e367 100644 --- a/linters/SublimeLinter/SublimeLinter.sublime-settings +++ b/linters/SublimeLinter/SublimeLinter.sublime-settings @@ -22,6 +22,9 @@ // Define globals exposed by modern browsers. "browser": true, + // Define globals exposed by Dojo Toolkit. + "dojo": true, + // Define globals exposed by jQuery. "jquery": true, @@ -37,21 +40,37 @@ // with underscores. "camelcase": true, + // Require curly braces around blocks in loops and conditionals. + "curly": true, + // Prohibit the use of == and != in favor of === and !==. "eqeqeq": true, // Suppress warnings about == null comparisons. "eqnull": true, + // Prohibit the use of immediate function invocations without wrapping + // them in parentheses. + "immed": true, + // Enforce a tab width of 2 spaces. - "indent": 2, + "indent": 4, // Prohibit the use of a variable before it was defined. "latedef": true, + // Suppress warnings about functions inside of loops. + "loopfunc": false, + // Require capitalized names for constructor functions. "newcap": true, + // This option suppresses warnings about mixed tabs and spaces when the latter are used for alignmnent only. + "smarttabs": true, + + // Disable 'use strict' message. + "strict": false, + // Prohibit trailing whitespace. "trailing": true, @@ -59,6 +78,13 @@ "undef": true, // Warn when variables are defined but never used. - "unused": true - } + "unused": true, + + /* + * RELAXING OPTIONS + * ================= + */ + + // Suppress warnings about the use of eval. + "evil": false } From 1d7badd518f65981e746b897a492bef20078fc56 Mon Sep 17 00:00:00 2001 From: Kevin Lanni Date: Mon, 19 Nov 2012 14:58:53 -0700 Subject: [PATCH 6/9] pad inside braces --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index f78c91dbfb..ccef8e590b 100644 --- a/README.md +++ b/README.md @@ -699,7 +699,17 @@ This version of the style guide includes my own personal style preferences. The breed: 'Bernese Mountain Dog' }); ``` + - Place 1 space inside leading and trailing braces. + + ```javascript + // bad + doSomething({key: 'value'}); + + // good + doSomething({ key: 'value' }); + ``` - Place 1 space after commas in a series. + ```javascript // bad var myArray = [1,2,3,4]; @@ -733,6 +743,7 @@ This version of the style guide includes my own personal style preferences. The } ``` - Place an empty newline before `return` statements. + ```javascript // bad Jedi.prototype.slash = function(target) { From 2a45e0cd0368c84aa62dac0feec88a04359bd7f8 Mon Sep 17 00:00:00 2001 From: Kevin Lanni Date: Fri, 14 Dec 2012 12:09:00 -0700 Subject: [PATCH 7/9] added hanging semicolons --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index ccef8e590b..508ea80f51 100644 --- a/README.md +++ b/README.md @@ -874,6 +874,24 @@ This version of the style guide includes my own personal style preferences. The return name; })(); ``` + - No hanging semicolons + + ```javascript + // bad + (function() { + var name = 'Skywalker'; + + return name; + })() + ; + + // good + (function() { + var name = 'Skywalker'; + + return name; + })(); + ``` **[[⬆]](#TOC)** From dc60852eb8c72c5330a10351261236caf5efbe41 Mon Sep 17 00:00:00 2001 From: Kevin Lanni Date: Tue, 22 Jan 2013 20:33:21 -0700 Subject: [PATCH 8/9] mostly typo fixes --- README.md | 2730 ++++++++++++++++++++++++++--------------------------- 1 file changed, 1365 insertions(+), 1365 deletions(-) diff --git a/README.md b/README.md index 508ea80f51..1d46a87604 100644 --- a/README.md +++ b/README.md @@ -1,1365 +1,1365 @@ -# JavaScript Style Guide() { - -*A mostly reasonable approach to JavaScript* - -This version of the style guide includes my own personal style preferences. The main differences are style asthetics. - - -## Table of Contents - - 1. [Types](#types) - 1. [Objects](#objects) - 1. [Arrays](#arrays) - 1. [Strings](#strings) - 1. [Functions](#functions) - 1. [Properties](#properties) - 1. [Variables](#variables) - 1. [Hoisting](#hoisting) - 1. [Conditional Expressions & Equality](#conditionals) - 1. [Blocks](#blocks) - 1. [Comments](#comments) - 1. [Whitespace](#whitespace) - 1. [Leading Commas](#leading-commas) - 1. [Semicolons](#semicolons) - 1. [Type Casting & Coercion](#type-coercion) - 1. [Naming Conventions](#naming-conventions) - 1. [Accessors](#accessors) - 1. [Constructors](#constructors) - 1. [jQuery](#jquery) - 1. [ES5 Compatability](#es5) - 1. [Testing](#testing) - 1. [Performance](#performance) - 1. [Resources](#resources) - 1. [The JavaScript Style Guide Guide](#guide-guide) - 1. [Contributors](#contributors) - 1. [License](#license) - -## Types - - - **Primitives**: When you access a primitive type you work directly on its value - - + `String` - + `Number` - + `Boolean` - + `null` - + `undefined` - - ```javascript - var foo = 1, - bar = foo; - - bar = 9; - - console.log(foo, bar); // => 1, 9 - ``` - - **Complex**: When you access a complex type you work on a reference to its value - - + `Object` - + `Array` - + `Function` - - ```javascript - var foo = [1, 2], - bar = foo; - - bar[0] = 9; - - console.log(foo[0], bar[0]); // => 9, 9 - ``` - - **[[⬆]](#TOC)** - -## Objects - - - Use the literal syntax for object creation. - - ```javascript - // bad - var item = new Object(); - - // good - var item = {}; - ``` - - - Don't use [reserved words](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words) as keys. - - ```javascript - // bad - var superman = { - class: 'superhero', - default: { clark: kent }, - private: true - }; - - // good - var superman = { - klass: 'superhero', - defaults: { clark: kent }, - hidden: true - }; - ``` - **[[⬆]](#TOC)** - -## Arrays - - - Use the literal syntax for array creation - - ```javascript - // bad - var items = new Array(); - - // good - var items = []; - ``` - - - For [performance reasons](http://jsperf.com/array-direct-assignment-vs-push/5) use direct assignment over Array#push - - ```javascript - var len = items.length, - itemsCopy = [], - i; - - // bad - for (i = 0; i < len; i++) { - itemsCopy.push(items[i]) - } - - // good - for (i = 0; i < len; i++) { - itemsCopy[i] = items[i]; - } - ``` - - **[[⬆]](#TOC)** - - -## Strings - - - Use single quotes `''` for strings - - ```javascript - // bad - var name = "Bob Parr"; - - // good - var name = 'Bob Parr'; - - // bad - var fullName = "Bob" + this.lastName; - - // good - var fullName = 'Bob' + this.lastName; - ``` - - - Strings longer than 80 characters should be written across multiple lines using string concatenation. - - ```javascript - // bad - var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; - - // bad - var errorMessage = 'This is a super long error that \ - was thrown because of Batman. \ - When you stop to think about \ - how Batman had anything to do \ - with this, you would get nowhere \ - fast.'; - - - // good - var errorMessage = 'This is a super long error that ' + - 'was thrown because of Batman.' + - 'When you stop to think about ' + - 'how Batman had anything to do ' + - 'with this, you would get nowhere ' + - 'fast.'; - ``` - - - When programatically building up a string, use Array#join instead of string concatenation. Mostly for IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). - - ```javascript - var items, - messages, - length, i; - - messages = [{ - state: 'success', - message: 'This one worked.' - },{ - state: 'success', - message: 'This one worked as well.' - },{ - state: 'error', - message: 'This one did not work.' - }]; - - length = messages.length; - - // bad - function inbox(messages) { - items = '
      '; - - for (i = 0; i < length; i++) { - items += '
    • ' + messages[i].message + '
    • '; - } - - return items + '
    '; - } - - // good - function inbox(messages) { - items = []; - - for (i = 0; i < length; i++) { - items[i] = '
  • ' + messages[i].message + '
  • '; - } - - return '
      ' + items.join('') + '
    '; - } - ``` - - **[[⬆]](#TOC)** - - -## Functions - - - Function expressions: - - ```javascript - // anonymous function expression - var anonymous = function() { - return true; - }; - - // named function expression - var named = function named() { - return true; - }; - - // immediately-invoked function expression (IIFE) - (function() { - 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. - - ```javascript - // bad - if (currentUser) { - function test() { - console.log('Nope.'); - } - } - - // good - if (currentUser) { - var test = function test() { - console.log('Yup.'); - }; - } - ``` - - - Never name a parameter `arguments`, this will take precendence 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... - } - ``` - - **[[⬆]](#TOC)** - - - -## Properties - - - Use dot notation when accessing properties. - - ```javascript - var luke = { - jedi: true, - age: 28 - }; - - // bad - var isJedi = luke['jedi']; - - // good - var isJedi = luke.jedi; - ``` - - - Use subscript notation `[]` when accessing properties with a variable. - - ```javascript - var luke = { - jedi: true, - age: 28 - }; - - function getProp(prop) { - return luke[prop]; - } - - var isJedi = getProp('jedi'); - ``` - - **[[⬆]](#TOC)** - - -## Variables - - - Always use `var` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. - - ```javascript - // bad - superPower = new SuperPower(); - - // good - var superPower = new SuperPower(); - ``` - - - Use one `var` declaration for multiple variables and declare each variable on a newline. - - ```javascript - // bad - var items = getItems(); - var goSportsTeam = true; - var dragonball = 'z'; - - // good - var items = getItems(), - goSportsTeam = true, - dragonball = 'z'; - ``` - - - Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. - - ```javascript - // bad - var i, len, dragonball, - items = getItems(), - goSportsTeam = true; - - // bad - var i, items = getItems(), - dragonball, - goSportsTeam = true, - len; - - // good - var items = getItems(), - goSportsTeam = true, - dragonball, - i, length; - ``` - - - Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues. - - ```javascript - // bad - function() { - test(); - console.log('doing stuff..'); - - //..other stuff.. - - var name = getName(); - - if (name === 'test') { - return false; - } - - return name; - } - - // good - function() { - var name = getName(); - - test(); - console.log('doing stuff..'); - - //..other stuff.. - - if (name === 'test') { - return false; - } - - return name; - } - - // bad - function() { - var name = getName(); - - if (!arguments.length) { - return false; - } - - return true; - } - - // good - function() { - if (!arguments.length) { - return false; - } - - var name = getName(); - - return true; - } - ``` - - **[[⬆]](#TOC)** - - -## Hoisting - - - Variable declarations get hoisted to the top of their scope, their assignment does not. - - ```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 interpretor is hoisting the variable - // declaration to the top of the scope. - // Which means our example could be rewritten as: - function example() { - var declaredButNotAssigned; - console.log(declaredButNotAssigned); // => undefined - declaredButNotAssigned = true; - } - ``` - - - Anonymous function expression 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/) - - **[[⬆]](#TOC)** - - - -## Conditional Expressions & Equality - - - Use `===` and `!==` over `==` and `!=`. - - Conditional expressions 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** evalute 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 - - **[[⬆]](#TOC)** - - -## 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; - } - ``` - - **[[⬆]](#TOC)** - - -## Comments - - - Use `/** ... */` for multiline comments. Include a description, specify types and values for all parameters and return values. - - ```javascript - // bad - // make() returns a new element - // based on the pased in tag name - // - // @param {string} tag Tag to create - // @return {element} element New element - function make(tag) { - // ...stuff... - - return element; - } - - // good - /** - * make() returns a new element - * based on the pased in tag name - * - * @param {string} tag Tag to create - * @return {element} element New 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 emptyline before the comment. - - ```javascript - // bad - var active = true; // is current tab - - // good - // is current tab - var active = true; - - // bad - function getType() { - console.log('fetching type...'); - // set the default type to 'no type' - var type = this._type || 'no type'; - - return type; - } - - // good - function getType() { - console.log('fetching type...'); - - // set the default type to 'no type' - var type = this._type || 'no type'; - - return type; - } - ``` - - **[[⬆]](#TOC)** - - -## Whitespace - - - Use hard tabs set to 4 - - ```javascript - // bad - function() { - ∙∙∙∙var name; - } - - // bad - function() { - ∙ var name; - } - - // good - function() { - var name; // that's 1 tab - } - ``` - - 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 inside leading and trailing braces. - - ```javascript - // bad - doSomething({key: 'value'}); - - // good - doSomething({ key: 'value' }); - ``` - - Place 1 space after commas in a series. - - ```javascript - // bad - var myArray = [1,2,3,4]; - - // good - var myArray = [1, 2, 3, 4]; - ``` - - Pad multi-line logic with empty newlines - - ```javascript - // bad - var myFunc = function myFunc() { - var myVar = [1, 2, 3], - i = myVar.length; - while (i--) { - // ... - } - doSomething(); - } - - // good - var myFunc = function myFunc() { - var myVar = [1, 2, 3], - i = myVar.length; - - while (i--) { - // ... - } - - doSomething(); - } - ``` - - Place an empty newline before `return` statements. - - ```javascript - // bad - Jedi.prototype.slash = function(target) { - this.target = target; - return this; - } - - // good - Jedi.prototype.slash = function(target) { - this.target = target; - - return this; - } - - // acceptable - Jedi.prototype.getTarget = function(target) { - return this.target; - } - ``` - - Place an empty newline at the end of the file. - - ```javascript - // bad - (function(global) { - // ...stuff... - })(this); - ``` - - ```javascript - // good - (function(global) { - // ...stuff... - })(this); - - ``` - - **[[⬆]](#TOC)** - - - 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); - ``` - -## Leading Commas - - - **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)** - - -## Semicolons - - - **Yup.** - - ```javascript - // bad - (function() { - var name = 'Skywalker' - - return name - })() - - // good - (function() { - var name = 'Skywalker'; - - return name; - })(); - - // good - ;(function() { - var name = 'Skywalker'; - - return name; - })(); - ``` - - No hanging semicolons - - ```javascript - // bad - (function() { - var name = 'Skywalker'; - - return name; - })() - ; - - // good - (function() { - var name = 'Skywalker'; - - return name; - })(); - ``` - - **[[⬆]](#TOC)** - - -## Type Casting & Coercion - - - 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); - - // preferred - 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); - - // preferred - var hasAge = !!age; - ``` - - **[[⬆]](#TOC)** - - -## Naming Conventions - - - Avoid single letter names. Be descriptive with your naming. - - ```javascript - // bad - function q() { - // ...stuff... - } - - // good - function query() { - // ..stuff.. - } - ``` - - - Use camelCase (also known as headlessCamelCase) when naming objects, functions, and instances - - ```javascript - // very 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'; - ``` - - - Name your functions. This is very helpful for stack traces. - - ```javascript - // bad - var log = function(msg) { - console.log(msg); - }; - - // good - var log = function log(msg) { - console.log(msg); - }; - ``` - - **[[⬆]](#TOC)** - - -## 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 - 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)** - - -## Constructors - - - 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'); - }; - ``` - - - Constructor methods should try to return `this`. This helps with method chaining which is often useful. - - ```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)** - - -## jQuery - - - 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' - }); - } - ``` - - - Scope jQuery object queries with find. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/13) - - ```javascript - // bad - $('.sidebar ul').hide(); - - // bad - $('.sidebar > ul').hide(); - - // bad - $('.sidebar', 'ul').hide(); - - // good - $('.sidebar').find('ul').hide(); - ``` - - **[[⬆]](#TOC)** - - -## ECMAScript 5 Compatability - - - Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) - - **[[⬆]](#TOC)** - - -## Testing - - - **Yup.** - - ```javascript - function() { - return true; - } - ``` - - **[[⬆]](#TOC)** - - -## 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) - - Loading... - - **[[⬆]](#TOC)** - - -## Resources - - -**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/) - -**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](//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/) - - [net.tutsplus](http://net.tutsplus.com/?s=javascript) - - **[[⬆]](#TOC)** - -## The JavaScript Style Guide Guide - - - [Reference](//github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) - -## Contributors - - - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) - - -## License - -(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)** - -# }; - +# JavaScript Style Guide() { + +*A mostly reasonable approach to JavaScript* + +This version of the style guide includes my own personal style preferences. The main differences are style asthetics. + + +## Table of Contents + + 1. [Types](#types) + 1. [Objects](#objects) + 1. [Arrays](#arrays) + 1. [Strings](#strings) + 1. [Functions](#functions) + 1. [Properties](#properties) + 1. [Variables](#variables) + 1. [Hoisting](#hoisting) + 1. [Conditional Expressions & Equality](#conditionals) + 1. [Blocks](#blocks) + 1. [Comments](#comments) + 1. [Whitespace](#whitespace) + 1. [Leading Commas](#leading-commas) + 1. [Semicolons](#semicolons) + 1. [Type Casting & Coercion](#type-coercion) + 1. [Naming Conventions](#naming-conventions) + 1. [Accessors](#accessors) + 1. [Constructors](#constructors) + 1. [jQuery](#jquery) + 1. [ES5 Compatibility](#es5) + 1. [Testing](#testing) + 1. [Performance](#performance) + 1. [Resources](#resources) + 1. [The JavaScript Style Guide Guide](#guide-guide) + 1. [Contributors](#contributors) + 1. [License](#license) + +## Types + + - **Primitives**: When you access a primitive type you work directly on its value + + + `String` + + `Number` + + `Boolean` + + `null` + + `undefined` + + ```javascript + var foo = 1, + bar = foo; + + bar = 9; + + console.log(foo, bar); // => 1, 9 + ``` + - **Complex**: When you access a complex type you work on a reference to its value + + + `Object` + + `Array` + + `Function` + + ```javascript + var foo = [1, 2], + bar = foo; + + bar[0] = 9; + + console.log(foo[0], bar[0]); // => 9, 9 + ``` + + **[[⬆]](#TOC)** + +## Objects + + - Use the literal syntax for object creation. + + ```javascript + // bad + var item = new Object(); + + // good + var item = {}; + ``` + + - Don't use [reserved words](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words) as keys. + + ```javascript + // bad + var superman = { + class: 'superhero', + default: { clark: kent }, + private: true + }; + + // good + var superman = { + klass: 'superhero', + defaults: { clark: kent }, + hidden: true + }; + ``` + **[[⬆]](#TOC)** + +## Arrays + + - Use the literal syntax for array creation + + ```javascript + // bad + var items = new Array(); + + // good + var items = []; + ``` + + - For [performance reasons](http://jsperf.com/array-direct-assignment-vs-push/5) use direct assignment over Array#push + + ```javascript + var len = items.length, + itemsCopy = [], + i; + + // bad + for (i = 0; i < len; i++) { + itemsCopy.push(items[i]) + } + + // good + for (i = 0; i < len; i++) { + itemsCopy[i] = items[i]; + } + ``` + + **[[⬆]](#TOC)** + + +## Strings + + - Use single quotes `''` for strings + + ```javascript + // bad + var name = "Bob Parr"; + + // good + var name = 'Bob Parr'; + + // bad + var fullName = "Bob" + this.lastName; + + // good + var fullName = 'Bob' + this.lastName; + ``` + + - Strings longer than 80 characters should be written across multiple lines using string concatenation. + + ```javascript + // bad + var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; + + // bad + var errorMessage = 'This is a super long error that \ + was thrown because of Batman. \ + When you stop to think about \ + how Batman had anything to do \ + with this, you would get nowhere \ + fast.'; + + + // good + var errorMessage = 'This is a super long error that ' + + 'was thrown because of Batman.' + + 'When you stop to think about ' + + 'how Batman had anything to do ' + + 'with this, you would get nowhere ' + + 'fast.'; + ``` + + - When programmatically building up a string, use Array#join instead of string concatenation. Mostly for IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). + + ```javascript + var items, + messages, + length, i; + + messages = [{ + state: 'success', + message: 'This one worked.' + },{ + state: 'success', + message: 'This one worked as well.' + },{ + state: 'error', + message: 'This one did not work.' + }]; + + length = messages.length; + + // bad + function inbox(messages) { + items = '
      '; + + for (i = 0; i < length; i++) { + items += '
    • ' + messages[i].message + '
    • '; + } + + return items + '
    '; + } + + // good + function inbox(messages) { + items = []; + + for (i = 0; i < length; i++) { + items[i] = '
  • ' + messages[i].message + '
  • '; + } + + return '
      ' + items.join('') + '
    '; + } + ``` + + **[[⬆]](#TOC)** + + +## Functions + + - Function expressions: + + ```javascript + // anonymous function expression + var anonymous = function() { + return true; + }; + + // named function expression + var named = function named() { + return true; + }; + + // immediately-invoked function expression (IIFE) + (function() { + 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. + + ```javascript + // bad + if (currentUser) { + function test() { + console.log('Nope.'); + } + } + + // good + if (currentUser) { + var test = function 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... + } + ``` + + **[[⬆]](#TOC)** + + + +## Properties + + - Use dot notation when accessing properties. + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + // bad + var isJedi = luke['jedi']; + + // good + var isJedi = luke.jedi; + ``` + + - Use subscript notation `[]` when accessing properties with a variable. + + ```javascript + var luke = { + jedi: true, + age: 28 + }; + + function getProp(prop) { + return luke[prop]; + } + + var isJedi = getProp('jedi'); + ``` + + **[[⬆]](#TOC)** + + +## Variables + + - Always use `var` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. + + ```javascript + // bad + superPower = new SuperPower(); + + // good + var superPower = new SuperPower(); + ``` + + - Use one `var` declaration for multiple variables and declare each variable on a newline. + + ```javascript + // bad + var items = getItems(); + var goSportsTeam = true; + var dragonball = 'z'; + + // good + var items = getItems(), + goSportsTeam = true, + dragonball = 'z'; + ``` + + - Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. + + ```javascript + // bad + var i, len, dragonball, + items = getItems(), + goSportsTeam = true; + + // bad + var i, items = getItems(), + dragonball, + goSportsTeam = true, + len; + + // good + var items = getItems(), + goSportsTeam = true, + dragonball, + i, length; + ``` + + - Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues. + + ```javascript + // bad + function() { + test(); + console.log('doing stuff..'); + + //..other stuff.. + + var name = getName(); + + if (name === 'test') { + return false; + } + + return name; + } + + // good + function() { + var name = getName(); + + test(); + console.log('doing stuff..'); + + //..other stuff.. + + if (name === 'test') { + return false; + } + + return name; + } + + // bad + function() { + var name = getName(); + + if (!arguments.length) { + return false; + } + + return true; + } + + // good + function() { + if (!arguments.length) { + return false; + } + + var name = getName(); + + return true; + } + ``` + + **[[⬆]](#TOC)** + + +## Hoisting + + - Variable declarations get hoisted to the top of their scope, their assignment does not. + + ```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 interpretor is hoisting the variable + // declaration to the top of the scope. + // Which means our example could be rewritten as: + function example() { + var declaredButNotAssigned; + console.log(declaredButNotAssigned); // => undefined + declaredButNotAssigned = true; + } + ``` + + - Anonymous function expression 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/) + + **[[⬆]](#TOC)** + + + +## Conditional Expressions & Equality + + - Use `===` and `!==` over `==` and `!=`. + - Conditional expressions 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 + + **[[⬆]](#TOC)** + + +## 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; + } + ``` + + **[[⬆]](#TOC)** + + +## Comments + + - Use `/** ... */` for multiline comments. Include a description, specify types and values for all parameters and return values. + + ```javascript + // bad + // make() returns a new element + // based on the pased in tag name + // + // @param {string} tag Tag to create + // @return {element} element New element + function make(tag) { + // ...stuff... + + return element; + } + + // good + /** + * make() returns a new element + * based on the pased in tag name + * + * @param {string} tag Tag to create + * @return {element} element New 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 emptyline before the comment. + + ```javascript + // bad + var active = true; // is current tab + + // good + // is current tab + var active = true; + + // bad + function getType() { + console.log('fetching type...'); + // set the default type to 'no type' + var type = this._type || 'no type'; + + return type; + } + + // good + function getType() { + console.log('fetching type...'); + + // set the default type to 'no type' + var type = this._type || 'no type'; + + return type; + } + ``` + + **[[⬆]](#TOC)** + + +## Whitespace + + - Use hard tabs set to 4 + + ```javascript + // bad + function() { + ∙∙∙∙var name; + } + + // bad + function() { + ∙ var name; + } + + // good + function() { + var name; // that's 1 tab + } + ``` + - 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 inside leading and trailing braces. + + ```javascript + // bad + doSomething({key: 'value'}); + + // good + doSomething({ key: 'value' }); + ``` + - Place 1 space after commas in a series. + + ```javascript + // bad + var myArray = [1,2,3,4]; + + // good + var myArray = [1, 2, 3, 4]; + ``` + - Pad multi-line logic with empty newlines + + ```javascript + // bad + var myFunc = function myFunc() { + var myVar = [1, 2, 3], + i = myVar.length; + while (i--) { + // ... + } + doSomething(); + } + + // good + var myFunc = function myFunc() { + var myVar = [1, 2, 3], + i = myVar.length; + + while (i--) { + // ... + } + + doSomething(); + } + ``` + - Place an empty newline before `return` statements. + + ```javascript + // bad + Jedi.prototype.slash = function(target) { + this.target = target; + return this; + } + + // good + Jedi.prototype.slash = function(target) { + this.target = target; + + return this; + } + + // acceptable + Jedi.prototype.getTarget = function(target) { + return this.target; + } + ``` + - Place an empty newline at the end of the file. + + ```javascript + // bad + (function(global) { + // ...stuff... + })(this); + ``` + + ```javascript + // good + (function(global) { + // ...stuff... + })(this); + + ``` + + **[[⬆]](#TOC)** + + - 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); + ``` + +## Leading Commas + + - **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)** + + +## Semicolons + + - **Yup.** + + ```javascript + // bad + (function() { + var name = 'Skywalker' + + return name + })() + + // good + (function() { + var name = 'Skywalker'; + + return name; + })(); + + // good + ;(function() { + var name = 'Skywalker'; + + return name; + })(); + ``` + - No hanging semicolons + + ```javascript + // bad + (function() { + var name = 'Skywalker'; + + return name; + })() + ; + + // good + (function() { + var name = 'Skywalker'; + + return name; + })(); + ``` + + **[[⬆]](#TOC)** + + +## Type Casting & Coercion + + - 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); + + // preferred + 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); + + // preferred + var hasAge = !!age; + ``` + + **[[⬆]](#TOC)** + + +## Naming Conventions + + - Avoid single letter names. Be descriptive with your naming. + + ```javascript + // bad + function q() { + // ...stuff... + } + + // good + function query() { + // ..stuff.. + } + ``` + + - Use camelCase (also known as headlessCamelCase) when naming objects, functions, and instances + + ```javascript + // very 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'; + ``` + + - Name your functions. This is very helpful for stack traces. + + ```javascript + // bad + var log = function(msg) { + console.log(msg); + }; + + // good + var log = function log(msg) { + console.log(msg); + }; + ``` + + **[[⬆]](#TOC)** + + +## 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 + 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)** + + +## Constructors + + - 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'); + }; + ``` + + - Constructor methods should try to return `this`. This helps with method chaining which is often useful. + + ```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)** + + +## jQuery + + - 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' + }); + } + ``` + + - Scope jQuery object queries with find. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/13) + + ```javascript + // bad + $('.sidebar ul').hide(); + + // bad + $('.sidebar > ul').hide(); + + // bad + $('.sidebar', 'ul').hide(); + + // good + $('.sidebar').find('ul').hide(); + ``` + + **[[⬆]](#TOC)** + + +## ECMAScript 5 Compatibility + + - Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) + + **[[⬆]](#TOC)** + + +## Testing + + - **Yup.** + + ```javascript + function() { + return true; + } + ``` + + **[[⬆]](#TOC)** + + +## 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) + - Loading... + + **[[⬆]](#TOC)** + + +## Resources + + +**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/) + +**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](//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/) + - [net.tutsplus](http://net.tutsplus.com/?s=javascript) + + **[[⬆]](#TOC)** + +## The JavaScript Style Guide Guide + + - [Reference](//github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) + +## Contributors + + - [View Contributors](https://github.com/airbnb/javascript/graphs/contributors) + + +## License + +(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)** + +# }; + From d61d1ca25f0fd729f61345d84dcd9b37d6c7b912 Mon Sep 17 00:00:00 2001 From: Kevin Lanni Date: Fri, 18 Oct 2013 12:56:08 -0700 Subject: [PATCH 9/9] convert arrow symbol to "TOC" --- README.md | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index ca02b2e4ff..3c4f6f4669 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ This version of the style guide includes my own personal style preferences. The console.log(foo[0], bar[0]); // => 9, 9 ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Objects @@ -120,7 +120,7 @@ This version of the style guide includes my own personal style preferences. The type: 'alien' }; ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Arrays @@ -172,7 +172,7 @@ This version of the style guide includes my own personal style preferences. The } ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Strings @@ -262,7 +262,7 @@ This version of the style guide includes my own personal style preferences. The } ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Functions @@ -319,7 +319,7 @@ This version of the style guide includes my own personal style preferences. The } ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** @@ -355,7 +355,7 @@ This version of the style guide includes my own personal style preferences. The var isJedi = getProp('jedi'); ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Variables @@ -464,7 +464,7 @@ This version of the style guide includes my own personal style preferences. The } ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Hoisting @@ -525,7 +525,7 @@ This version of the style guide includes my own personal style preferences. The console.log('Flying'); }; } - + // the same is true when the function name // is the same as the variable name. function example() { @@ -553,7 +553,7 @@ This version of the style guide includes my own personal style preferences. The - 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/) - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** @@ -602,7 +602,7 @@ This version of the style guide includes my own personal style preferences. The - For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Blocks @@ -631,7 +631,7 @@ This version of the style guide includes my own personal style preferences. The } ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Comments @@ -722,7 +722,7 @@ This version of the style guide includes my own personal style preferences. The } ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Whitespace @@ -888,7 +888,7 @@ This version of the style guide includes my own personal style preferences. The .call(tron.led); ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Commas @@ -950,7 +950,7 @@ This version of the style guide includes my own personal style preferences. The ]; ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Semicolons @@ -998,7 +998,7 @@ This version of the style guide includes my own personal style preferences. The })(); ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Type Casting & Coercion @@ -1073,7 +1073,7 @@ This version of the style guide includes my own personal style preferences. The var hasAge = !!age; ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Naming Conventions @@ -1187,7 +1187,7 @@ This version of the style guide includes my own personal style preferences. The }; ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Accessors @@ -1241,7 +1241,7 @@ This version of the style guide includes my own personal style preferences. The }; ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Constructors @@ -1329,7 +1329,7 @@ This version of the style guide includes my own personal style preferences. The }; ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Events @@ -1360,7 +1360,7 @@ This version of the style guide includes my own personal style preferences. The }); ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Modules @@ -1391,7 +1391,7 @@ This version of the style guide includes my own personal style preferences. The }(this); ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## jQuery @@ -1456,14 +1456,14 @@ This version of the style guide includes my own personal style preferences. The $($sidebar[0]).find('ul'); ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## ECMAScript 5 Compatibility - Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Testing @@ -1476,7 +1476,7 @@ This version of the style guide includes my own personal style preferences. The } ``` - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Performance @@ -1490,7 +1490,7 @@ This version of the style guide includes my own personal style preferences. The - [Long String Concatenation](http://jsperf.com/ya-string-concat) - Loading... - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## Resources @@ -1540,7 +1540,7 @@ This version of the style guide includes my own personal style preferences. The - [Dustin Diaz](http://dustindiaz.com/) - [nettuts](http://net.tutsplus.com/?s=javascript) - **[[⬆]](#TOC)** + **[[TOC]](#TOC)** ## In the Wild @@ -1607,7 +1607,7 @@ 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)** +**[[TOC]](#TOC)** # };