前端JavaScript規範

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對象都提供了字面量),使用字面量建立對象。javascript

    • string
    • number
    • boolean
    • null
    • undefined
    var foo = 1,
        bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
  • 複雜類型: 至關於傳引用css

    • object
    • array
    • function
    var foo = [1, 2],
        bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9

對象

  • 使用字面值建立對象html

    // 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
    };

數組

  • 使用字面值建立數組java

    // bad
    var items = new Array();
    
    // good
    var items = [];
  • 若是你不知道數組的長度,使用pushnode

    var someStack = [];
    
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
  • 當你須要拷貝數組時使用slice. jsPerfpython

    var len = items.length,
        itemsCopy = [],
        i;
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();
  • 使用slice將類數組的對象轉成數組.jquery

    function trigger() {
      var args = [].slice.apply(arguments);
      ...
    }

字符串

  • 對字符串使用單引號 ''(由於大多時候咱們的字符串。特別html會出現")git

    // bad
    var name = "Bob Parr";
    
    // good
    var name = 'Bob Parr';
    
    // bad
    var fullName = "Bob " + this.lastName;
    
    // good
    var fullName = 'Bob ' + this.lastName;
  • 超過80(也有規定140的,項目具體可制定)個字符的字符串應該使用字符串鏈接換行github

  • 注: 若是過分使用,長字符串鏈接可能會對性能有影響. jsPerf & Discussion

    // 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而不是字符串鏈接來構建字符串,特別是IE: jsPerf.

    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() {
      var name = getName();
    
      if (!arguments.length) {
        return false;
      }
    
      return true;
    }
    
    // good
    function() {
      if (!arguments.length) {
        return false;
      }
    
      var name = getName();
    
      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;
    }
  • 若是你有一個問題須要從新來看一下或若是你建議一個須要被實現的解決方法的話須要在你的註釋前面加上 FIXMETODO 幫助其餘人迅速理解

    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.jsfunction(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

  • 對於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。或者也能夠考慮引入underscorelodash 經常使用輔助庫.
- 參考Kangax的 ES5 compatibility table
- JavaScript工具庫之Lodash
- Babel-如今開始使用 ES6

HTML、CSS、JavaScript分離

  • 頁面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

  • 前端項目中推薦引入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統一配置很重要。
相關文章
相關標籤/搜索