JavaScript Style Guide

Table of Contents

  1. Types
  2. Objects
  3. Arrays
  4. Strings
  5. Functions
  6. Properties
  7. Variables
  8. Conditional Expressions and Equality
  9. block
  10. Comments
  11. Whitespace
  12. Commas
  13. Semicolons
  14. Type Casting & Coercion
  15. Naming Conventions
  16. Accessors
  17. Constructors
  18. Events
  19. Modules
  20. jQuery
  21. ES5 Compatibility
  22. Separation of HTML, CSS, and JavaScript
  23. Using jsHint
  24. Front-end Tools

Types

  • Primitive Values: Equivalent to pass-by-value (JavaScript objects all provide literals), use literals to create objects.
    • string
    • number
    • boolean
    • null
    • undefined
var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9

Complex Types: Equivalent to pass-by-reference

  • object
  • array
  • function
var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

Objects

  • Use literal values to create objects.
    // bad
    var item = new Object();
    
    // good
    var item = {};
    
  • Do not use reserved wordsreserved wordsas keys.
    // bad
    var superman = {
      class: 'superhero',
      default: { clark: 'kent' },
      private: true
    };
    
    // good
    var superman = {
      klass: 'superhero',
      defaults: { clark: 'kent' },
      hidden: true
    };
    

Arrays

  • Use literal values to create arrays.
    // bad
    var items = new Array();
    
    // good
    var items = [];
    
  • If you do not know the array length, use push.
    var someStack = [];
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
    
  • Use slice when you need to copy an array. jsPerf.
    var len = items.length,
        itemsCopy = [],
        i;
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();
    
  • Use slice to convert array-like objects to arrays.
    function trigger() {
      var args = [].slice.apply(arguments);
      ...
    }
    

Strings

  • Use single quotes for strings''(Because most of the time our strings, especially HTML, will contain")。
    // bad
    var name = "Bob Parr";
    
    // good
    var name = 'Bob Parr';
    
    // bad
    var fullName = "Bob " + this.lastName;
    
    // good
    var fullName = 'Bob ' + this.lastName;
    
  • Strings exceeding 80 characters (some specify 140, the project can define its own) should use string concatenation for line breaks.
  • Note: If overused, long string concatenation may affect performance.
    // 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 programming, use join instead of string concatenation to build strings.
    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 = '<ul>';
    
      for (i = 0; i < length; i++) {
        items += '<li>' + messages[i].message + '</li>';
      }
    
      return items + '</ul>';
    }
    
    // good
    function inbox(messages) {
      items = [];
    
      for (i = 0; i < length; i++) {
        items[i] = messages[i].message;
      }
    
      return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
    }
    

Functions

  • Function expressions:
    // 匿名函数表达式
    var anonymous = function() {
      return true;
    };
    
    // 有名函数表达式
    var named = function named() {
      return true;
    };
    
    // 立即调用函数表达式
    (function() {
      console.log('Welcome to the Internet. Please follow me.');
    })();
    
  • Never declare a function in a non-function block; assign that function to a variable instead. Browsers allow you to do this, but they parse it differently.
  • Note:ECMA-262 definesblockas a set of statements; a function declaration is not a statement.Read ECMA-262's explanation of this issue.。
    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    if (currentUser) {
      var test = function test() {
        console.log('Yup.');
      };
    }
    
  • Never name a parameterarguments, this will override theargumentsobject passed within the function scope.
    // bad
    function nope(name, options, arguments) {
      // ...stuff...
    }
    
    // good
    function yup(name, options, args) {
      // ...stuff...
    }
    

Properties

  • When using variables and special invalid variable names, you can use square brackets to access properties (.preferred).
    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');
    

Variables

  • Always usevarto declare variables. If not, global variables will be created, and we should avoid polluting the global namespace.
    // bad
    superPower = new SuperPower();
    
    // good
    var superPower = new SuperPower();
    
  • Use a singlevarand newlines to declare multiple variables, indented 4 spaces.
    // bad
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
  • Declare unassigned variables last, which is useful when you want to reference previously assigned variables.
    // 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,
        length,
        i;
    
  • Declare variables at the top of the scope to avoid issues related to variable declaration and assignment.
    // 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() {
      if (!arguments.length) {
        return false;
      }
    
      var name = getName();
    
      return true;
    }
    
    // good
    function() {
      var name = getName();
    
      if (!arguments.length) {
        return false;
      }
    
      return true;
    }
    

Conditional Expressions and Equality

  • Use appropriately===and!==and==and!=。
  • Use logical operations in expressions appropriately.
  • Type coercion in conditional expressions follows these rules:
    • Objectsare evaluated astrue
    • Undefinedare evaluated asfalse
    • Nullare evaluated asfalse
    • Booleansare evaluated asboolean value
    • NumbersIf it is+0, -0, or NaNis evaluated asfalse, otherwise astrue
    • StringsIf it is an empty string''it is evaluated asfalse, otherwise astrue
      if ([0]) {
        // true
        // An array is an object, objects evaluate to true
      }
      
  • Use shortcuts.
    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
    
  • ReadTruth Equality and JavaScriptto learn more.

block

  • Use braces for all multi-line blocks.
    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function() { return false; }
    
    // good
    function() {
      return false;
    }
    
  • Use/** ... */for multi-line comments, including descriptions, specified types, parameter values, and return values.
    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param <String> tag
    // @return <Element> element
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
    
    // good
    /**
     * make() returns a new element
     * based on the passed in tag name
     *
     * @param <String> tag
     * @return <Element> element
     */
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
    
  • Use//for single-line comments. Place the single-line comment above the commented object, and put a blank line before the comment.
    // 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;
    }
    
  • If you have a problem that needs to be reviewed again, or if you suggest a solution that needs to be implemented, prefix your comment withFIXMEorTODOto help others quickly understand.
    function Calculator() {
    
      // FIXME: shouldn't use a global here
      total = 0;
    
      return this;
    }
    
    function Calculator() {
    
      // TODO: total should be configurable by an options param
      this.total = 0;
    
      return this;
    }
    
  • For specification-compliant documentation, when documentation is needed, you can tryjsdoc。

Whitespace

  • Indentation and formatting help the team locate and fix code bugs faster.
  • Set tabs to 4 spaces.
    // bad
    function() {
    ∙∙var name;
    }
    
    // bad
    function() {
    ∙var name;
    }
    
    // good
    function() {
    ∙∙∙∙var name;
    }
    
  • Put a space before the opening brace.
    // 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'
    });
    
  • Use indentation when doing long method chains.
    // 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);
    

Commas

  • Do not put commas at the beginning.
    // 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'
    };
    
  • Do not add extra trailing commas. This may cause errors in IE, and if there is an extra comma, some ES3 implementations will calculate an extra array length.
    // bad
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn',
    };
    
    var heroes = [
      'Batman',
      'Superman',
    ];
    
    // good
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn'
    };
    
    var heroes = [
      'Batman',
      'Superman'
    ];
    

Semicolons

  • Always add semicolons at the end of statements.
    // bad
    (function() {
      var name = 'Skywalker'
      return name
    })()
    
    // good
    (function() {
      var name = 'Skywalker';
      return name;
    })();
    
    // good
    ;(function() {
      var name = 'Skywalker';
      return name;
    })();
    

Type Casting & Coercion

  • Perform type conversion at the beginning of the statement.
  • Strings:
    //  => 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';
    
  • For numbers, useparseIntand always include the radix for type conversion, e.g.,parseInt(value, 10)。
  • 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:
    var age = 0;
    
    // bad
    var hasAge = new Boolean(age);
    
    // good
    var hasAge = Boolean(age);
    
    // good
    var hasAge = !!age;
    

Naming Conventions

  • Avoid single-character names; let your variable names be descriptive.
    // bad
    function q() {
      // ...stuff...
    }
    
    // good
    function query() {
      // ..stuff..
    }
    
  • Use camelCase when naming objects, functions, and instances.
    // 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.
    // 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'
    });
    
  • Prefix private properties with an underscore_:
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';
    
  • When saving a reference tothisuseself(python 风格), avoidthis issue. Angular recommends usingvm(MVVM模式中view-model):
    // good
    function() {
      var self = this;
      return function() {
        console.log(self);
      };
    }
    

Accessors

  • Accessor functions for properties are not required.
  • If you do have accessor functions, use getVal() and setVal('hello'),java getter、setter风格orjQuery风格。
  • If the property is a boolean, use isVal() or hasVal().
    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
    
  • You can create get() and set() functions, but keep them consistent.
    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];
    };
    

Constructors

  • Assign methods to the object's prototype instead of overwriting the prototype with a new object. Overwriting the prototype can cause inheritance problems.
    function Jedi() {
      console.log('new jedi');
    }
    
    // bad
    Jedi.prototype = {
      fight: function fight() {
        console.log('fighting');
      },
    
      block: function block() {
        console.log('blocking');
      }
    };
    
    // good
    Jedi.prototype.fight = function fight() {
      console.log('fighting');
    };
    
    Jedi.prototype.block = function block() {
      console.log('blocking');
    };
    
  • Methods can returnthisto help methods be chainable.
    // 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);
    
  • You can write a custom toString() method, but make sure it works correctly and has no side effects.
    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();
    };
    

Events

  • When attaching data to events, pass a hash instead of a primitive value. This allows later contributors to add more data to the event data without having to find and update the event handler for that event.
    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function(e, listingId) {
      // do something with listingId
    });
    

    Better:

    // good
    $(this).trigger('listingUpdated', { listingId : listing.id });
    
    ...
    
    $(this).on('listingUpdated', function(e, data) {
      // do something with data.listingId
    });
    

Modules

  • The file should be named in camelCase, placed in a folder with the same name, and have a consistent name when exporting.
  • For public API libraries, consider adding a method named noConflict() that sets the exported module to the previous version and returns it.
  • Always declare at the top of the module'use strict';, import[JSHint规范](http://jshint.com/):
    // fancyInput/fancyInput.js
    
    (function(global) {
      'use strict';
    
      var previousFancyInput = global.FancyInput;
    
      function FancyInput(options) {
        this.options = options || {};
      }
    
      FancyInput.noConflict = function noConflict() {
        global.FancyInput = previousFancyInput;
        return FancyInput;
      };
    
      global.FancyInput = FancyInput;
    })(this);
    

jQuery

  • For jQuery objects, start with$at the beginning to distinguish them from native DOM nodes.
    // bad
    var menu = $(".menu");
    
    // good
    var $menu = $(".menu");
    
  • Cache jQuery queries
    // 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'
      });
    }
    
  • Use cascading for DOM queries$('.sidebar ul')or$('.sidebar ul'),jsPerf.
  • For scoped jQuery object queries, usefind:
    // bad
    $('.sidebar', 'ul').hide();
    
    // bad
    $('.sidebar').find('ul').hide();
    
    // good
    $('.sidebar ul').hide();
    
    // good
    $('.sidebar > ul').hide();
    
    // good (slower)
    $sidebar.find('ul');
    
    // good (faster)
    $($sidebar[0]).find('ul');
    
  • Use the document ready event only once per page, making it easier to debug and track behavior flow.
    $(function(){
       //do your page init.  
    });
    
  • Use eventsjQuery.onseparated from the page into JavaScript files.
    // bad
    <a id="myLink" href="../index.html" onclick="myEventHandler();"></a>
    
    // good
    <a id="myLink" href="../index.html"></a>
    
    $("#myLink").on("click", myEventHandler);
    
  • Use the promise approach for Ajax.
    // bad
        $.ajax({
            ...
            success : function(){
            },
            error : function(){
            } 
        })
    
        // good
        $.ajax({.
            ..
        }).then( function( ){
            // success
        }, function( ){
            // error
        })
    
  • Use promise's deferred objects to solve deferred registration issues.
    var dtd = $.Deferred(); // 新建一个deferred对象
      var wait = function(dtd){
        var tasks = function(){
          alert("执行完毕!");
          dtd.resolve(); // 改变deferred对象的执行状态
        };
        setTimeout(tasks,5000);
        return dtd;
      };
    
  • Move the Style in HTML and JavaScript style into CSS classes; use classes in HTML and JavaScript instead of direct styles.

ECMAScript 5 Compatibility

Use ES5 methods as much as possible, especially array map, filter, and forEach methods to simplify daily development. In older IE browsers, introduceES5-shim. Or you can also consider introducingunderscore、lodashcommon helper libraries.
- ReferenceKangaxES5compatibility table。

Separation of HTML, CSS, and JavaScript

  • Use HTML for page DOM structure, CSS for styles, and JavaScript for dynamic DOM operations. Do not mix them in HTML.
  • Separate into different file types and link the files.
  • HTML, CSS, and JavaScript variable names all need business value. CSS uses all-lowercase names separated by hyphens, while JavaScript uses camelCase with the first letter lowercase.
  • CSS can introduceBootstrap, Foundation and other well-known responsive design frameworks. Also use SASS and LESS tools to write CSS.
  • For CSS and JavaScript, it is recommended to merge them into a single file to reduce the number of Ajax connections. You can also introduce the AMD (Require.js) loading method.
  • For most internal enterprise management systems, you can try adopting front-endMVC frameworksto organize code. Such as Angular, React + flux architecture, Knockout, etc.
  • For compatibility, you can useModernizrspecification libraries for assistance.

Using jsHint

  • It is recommended to introducejshintplugins in front-end projects to standardize project coding standards. Also have a complete set of IDE configurations.
  • Note: jshint requires introducing nodejs tools grunt or gulp plugins. Enterprise-level nodejs npm private server is recommended.

Front-end Tools

  • Front-end third-party JavaScript package management tool bower (bower install jQuery), bower can implement dependency resolution, download, upgrade management, etc. for third-party libraries. It is recommended to set up an enterprise-level bower private server.
  • For front-end build tools, grunt or gulp tools can be used to implement all front-end tasks such as html, css, js compression, validation, testing, file merging, watch, and liveload. Enterprise-level nodejs npm private server is recommended.
  • Front-end development IDE: WebStorm (Idea) and Sublime are the best. The project team should use a unified IDE. Unified IDE configuration is very important.

Related Articles

Source: http://greengerong.github.io/blog/2015/05/09/qian-duan-javascriptgui-fan