天天看點

JavaScript規範

目錄

  1. 類型
  2. 對象
  3. 數組
  4. 字元串
  5. 函數
  6. 屬性
  7. 變量
  8. 條件表達式和等号
  9. 注釋
  10. 空白
  11. 逗号
  12. 分号
  13. 類型轉換
  14. 命名約定
  15. 存取器
  16. 構造器
  17. 事件
  18. 子產品
  19. jQuery
  20. ES5 相容性
  21. HTML、CSS、JavaScript分離
  22. 使用jsHint
  23. 前端工具

  • 原始值: 相當于傳值(JavaScript對象都提供了字面量),使用字面量建立對象。
    • string

    • number

    • boolean

    • null

    • undefined

var foo = 1,
    bar = foo;

bar = 9;

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

複雜類型: 相當于傳引用

  • object

  • array

  • function

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

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

  • 使用字面值建立對象。
    // bad
    var item = new Object();
    
    // good
    var item = {};
          
  • 不要使用保留字 reserved words 作為鍵。
    // bad
    var superman = {
      class: 'superhero',
      default: { clark: 'kent' },
      private: true
    };
    
    // good
    var superman = {
      klass: 'superhero',
      defaults: { clark: 'kent' },
      hidden: true
    };
          

  • 使用字面值建立數組。
    // bad
    var items = new Array();
    
    // good
    var items = [];
          
  • 如果你不知道數組的長度,使用push。
    var someStack = [];
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
          
  • 當你需要拷貝數組時使用slice. jsPerf。
    var len = items.length,
        itemsCopy = [],
        i;
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();
          
  • 使用slice将類數組的對象轉成數組。
    function trigger() {
      var args = [].slice.apply(arguments);
      ...
    }
          

  • 對字元串使用單引号

    ''

    (因為大多時候我們的字元串。特别html會出現

    "

    )。
    // bad
    var name = "Bob Parr";
    
    // good
    var name = 'Bob Parr';
    
    // bad
    var fullName = "Bob " + this.lastName;
    
    // good
    var fullName = 'Bob ' + this.lastName;
          
  • 超過80(也有規定140的,項目具體可制定)個字元的字元串應該使用字元串連接配接換行。
  • 注: 如果過度使用,長字元串連接配接可能會對性能有影響。
    // 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.';
          
  • 程式設計時使用join而不是字元串連接配接來建構字元串。
    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>';
    }
          

  • 函數表達式:
    // 匿名函數表達式
    var anonymous = function() {
      return true;
    };
    
    // 有名函數表達式
    var named = function named() {
      return true;
    };
    
    // 立即調用函數表達式
    (function() {
      console.log('Welcome to the Internet. Please follow me.');
    })();
          
  • 絕對不要在一個非函數塊裡聲明一個函數,把那個函數賦給一個變量。浏覽器允許你這麼做,但是它們解析不同。
  • 注: ECMA-262定義把

    定義為一組語句,函數聲明不是一個語句。閱讀ECMA-262對這個問題的說明。
    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    if (currentUser) {
      var test = function test() {
        console.log('Yup.');
      };
    }
          
  • 絕對不要把參數命名為

    arguments

    , 這将會逾越函數作用域内傳過來的

    arguments

    對象。
    // bad
    function nope(name, options, arguments) {
      // ...stuff...
    }
    
    // good
    function yup(name, options, args) {
      // ...stuff...
    }
          

  • 當使用變量和特殊非法變量名時,通路屬性時可以使用中括号(

    .

    優先)。
    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');
          

  • 總是使用

    var

    來聲明變量,如果不這麼做将導緻産生全局變量,我們要避免污染全局命名空間。
    // bad
    superPower = new SuperPower();
    
    // good
    var superPower = new SuperPower();
          
  • 使用一個

    var

    以及新行聲明多個變量,縮進4個空格。
    // bad
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
          
  • 最後再聲明未指派的變量,當你想引用之前已指派變量的時候很有用。
    // 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;
          
  • 在作用域頂部聲明變量,避免變量聲明和指派引起的相關問題。
    // 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;
    }
          

  • 合理使用

    ===

    !==

    以及

    ==

    !=

  • 合理使用表達式邏輯操作運算。
  • 條件表達式的強制類型轉換遵循以下規則:
    • 對象 被計算為 true
    • Undefined 被計算為 false
    • Null 被計算為 false
    • 布爾值 被計算為 布爾的值
    • 數字 如果是 +0, -0, or NaN 被計算為 false , 否則為 true
    • 字元串 如果是空字元串

      ''

      則被計算為 false, 否則為 true
      if ([0]) {
        // true
        // An array is an object, objects evaluate to true
      }
            
  • 使用快捷方式。
    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
          
  • 閱讀 Truth Equality and JavaScript 了解更多。

  • 給所有多行的塊使用大括号。
    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function() { return false; }
    
    // good
    function() {
      return false;
    }
          

  • 使用

    /** ... */

    進行多行注釋,包括描述,指定類型以及參數值和傳回值。
    // 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;
    }
          
  • //

    進行單行注釋,在評論對象的上面進行單行注釋,注釋前放一個空行。
    // 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;
    }
          
  • 如果你有一個問題需要重新來看一下或如果你建議一個需要被實作的解決方法的話需要在你的注釋前面加上

    FIXME

    TODO

    幫助其他人迅速了解。
    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;
    }
          
  • 滿足規範的文檔,在需要文檔的時候,可以嘗試jsdoc。

  • 縮進、格式化能幫助團隊更快得定位修複代碼BUG。
  • 将tab設為4個空格。
    // bad
    function() {
    ∙∙var name;
    }
    
    // bad
    function() {
    ∙var name;
    }
    
    // good
    function() {
    ∙∙∙∙var name;
    }
          
  • 大括号前放一個空格。
    // 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'
    });
          
  • 在做長方法鍊時使用縮進。
    // 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);
          

  • 不要将逗号放前面。
    // 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'
    };
          
  • 不要加多餘的逗号,這可能會在IE下引起錯誤,同時如果多一個逗号某些ES3的實作會計算多數組的長度。
    // bad
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn',
    };
    
    var heroes = [
      'Batman',
      'Superman',
    ];
    
    // good
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn'
    };
    
    var heroes = [
      'Batman',
      'Superman'
    ];
          

  • 語句結束一定要加分号。
    // bad
    (function() {
      var name = 'Skywalker'
      return name
    })()
    
    // good
    (function() {
      var name = 'Skywalker';
      return name;
    })();
    
    // good
    ;(function() {
      var name = 'Skywalker';
      return name;
    })();
          

  • 在語句的開始執行類型轉換。
  • 字元串:
    //  => 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';
          
  • 對數字使用

    parseInt

    并且總是帶上類型轉換的基數,如

    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;
          
  • 布爾值:
    var age = 0;
    
    // bad
    var hasAge = new Boolean(age);
    
    // good
    var hasAge = Boolean(age);
    
    // good
    var hasAge = !!age;
          

  • 避免單個字元名,讓你的變量名有描述意義。
    // bad
    function q() {
      // ...stuff...
    }
    
    // good
    function query() {
      // ..stuff..
    }
          
  • 當命名對象、函數和執行個體時使用駝峰命名規則。
    // 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'
    });
          
  • 當命名構造函數或類時使用駝峰式大寫。
    // 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'
    });
          
  • 命名私有屬性時前面加個下劃線

    _

    :
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';
          
  • 當儲存對

    this

    的引用時使用

    self(python 風格)

    ,避免

    this issue

    .Angular建議使用

    vm(MVVM模式中view-model)

    // good
    function() {
      var self = this;
      return function() {
        console.log(self);
      };
    }
          

  • 屬性的存取器函數不是必需的。
  • 如果你确實有存取器函數的話使用getVal() 和 setVal('hello'),

    java getter、setter風格

    或者

    jQuery風格

  • 如果屬性是布爾值,使用isVal() 或 hasVal()。
    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
          
  • 可以建立get()和set()函數,但是要保持一緻。
    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];
    };
          

  • 給對象原型配置設定方法,而不是用一個新的對象覆寫原型,覆寫原型會使繼承出現問題。
    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');
    };
          
  • 方法可以傳回

    this

    幫助方法可鍊。
    // 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);
          
  • 可以寫一個自定義的toString()方法,但是確定它工作正常并且不會有副作用。
    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();
    };
          

  • 當給事件附加資料時,傳入一個哈希而不是原始值,這可以讓後面的貢獻者加入更多資料到事件資料裡而不用找出并更新那個事件的事件處理器。
    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function(e, listingId) {
      // do something with listingId
    });
          
    更好:
    // good
    $(this).trigger('listingUpdated', { listingId : listing.id });
    
    ...
    
    $(this).on('listingUpdated', function(e, data) {
      // do something with data.listingId
    });
          

  • 這個檔案應該以駝峰命名,并在同名檔案夾下,同時導出的時候名字一緻。
  • 對于公開API庫可以考慮加入一個名為noConflict()的方法來設定導出的子產品為之前的版本并傳回它。
  • 總是在子產品頂部聲明

    'use strict';

    ,引入

    [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對象以

    $

    開頭,以和原生DOM節點區分。
    // bad
    var menu = $(".menu");
    
    // good
    var $menu = $(".menu");
          
  • 緩存jQuery查詢
    // 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'
      });
    }
          
  • 對DOM查詢使用級聯的

    $('.sidebar ul')

    $('.sidebar ul')

    ,jsPerf。
  • 對有作用域的jQuery對象查詢使用

    find

    // 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');
          
  • 每個頁面隻使用一次document的ready事件,這樣便于調試與行為流跟蹤。
    $(function(){
       //do your page init.  
    });
          
  • 事件利用

    jQuery.on

    從頁面分離到JavaScript檔案。
    // bad
    <a id="myLink" href="#" onclick="myEventHandler();"></a>
    
    // good
    <a id="myLink" href="#"></a>
    
    $("#myLink").on("click", myEventHandler);
          
  • 對于Ajax使用promise方式。
    // bad
        $.ajax({
            ...
            success : function(){
            },
            error : function(){
            } 
        })
    
        // good
        $.ajax({.
            ..
        }).then( function( ){
            // success
        }, function( ){
            // error
        })
          
  • 利用promise的deferred對象解決延遲注冊問題。
    var dtd = $.Deferred(); // 建立一個deferred對象
      var wait = function(dtd){
        var tasks = function(){
          alert("執行完畢!");
          dtd.resolve(); // 改變deferred對象的執行狀态
        };
        setTimeout(tasks,5000);
        return dtd;
      };
          
  • HTML中Style、以及JavaScript中style移到CSS中class,在HTML、JavaScript中引入class,而不是直接style。

ECMAScript 5相容性

盡量采用ES5方法,特别數組map、filter、forEach方法簡化日常開發。在老式IE浏覽器中引入ES5-shim。或者也可以考慮引入underscore、lodash 常用輔助庫。

- 參考Kangax的 ES5 compatibility table。

  • 頁面DOM結構使用HTML,樣式則采用CSS,動态DOM操作JavaScript。不要混用在HTML中。
  • 分離在不同類型檔案,檔案link。
  • HTML、CSS、JavaScript變量名都需要有業務價值。CSS以中劃線分割的全小寫命名,JavaScript則首字母小寫的駝峰命名。
  • CSS可引入Bootstrap、Foundation等出名響應式設計架構。以及SASS、LESS工具書寫CSS。
  • 對于CSS、JavaScript建議合并為單檔案,減少Ajax的連接配接數。也可以引入AMD(Require.js)加載方式。
  • 對于内部大部分企業管理系統,可以嘗試采用前端 MVC架構組織代碼。如Angular、React + flux架構、Knockout等。
  • 對于相容性可用Modernizr規範庫輔助。

  • 前端項目中推薦引入jshint插件來規範項目編碼規範。以及一套完善的IDE配置。
  • 注意:jshint需要引入nodejs 工具grunt或gulp插件,建議企業級nodejs npm私服。

  • 前端第三方JavaScript包管理工具bower(

    bower install jQuery

    ),bower可以實作第三方庫的依賴解析、下載下傳、更新管理等。建議建立企業級bower私服。
  • 前端建構工具,可以采用grunt或者gulp工具,可以實作html、css、js壓縮、驗證、測試,檔案合并、watch和liveload等所有前端任務。建議企業級nodejs npm私服。
  • 前端開發IDE: WebStorm( Idea )、Sublime為最佳 。項目組統一IDE。IDE統一配置很重要。

相關文章

  • Javascript 編碼規範
  • 最流行的JavaScript代碼規範
  • JavaScript 代碼風格指南

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