js規範

目錄

嵌入規則

Javascript程序應該儘量放在.js的文件中,需要調用的時候在頁面中以<script src="filename.js">的形式包含進來。Javascript代碼若不是該頁面專用的,則應儘量避免在頁面中直接編寫Javascript代碼。

對齊縮進與換行

  • 縮進
    • 在同一系統中應採用同一種縮進標準,本文提倡縮進大小爲4個空格。各編譯器對Tab鍵所代替的空白大小定義不同。建議在設置開發環境時,將編輯器裏的Tab快捷鍵重新設置成4個空格。多數編譯器提供了此功能。否則建議按4次空格來進行縮進。
  • 換行

    在以下位置必須換行:

    • 每個獨立語句結束後;
    • if、else、catch、finally、while等關鍵字前;
    • 運算符處換行時,運算符必須在新行的行首。
    • 對於因爲單行長度超過限制時產生的換行,參考行長度中的策略進行分隔。
      1. 字符串過長截斷

每行代碼應小於80個字符。若代碼較長應儘量換行,換行應選擇在操作符和標點符號之後,最好是在分號“;”或逗號“,”之後。下一行代碼相對上一行縮進4個空格。這樣可以有效防止複製粘貼引起的代碼缺失等錯誤並增強可讀性。

  1. 三元運算符過長
三元運算符由3部分組成,因此其換行應當根據每個部分的長度不同,形成3種不同的情況:
<script>
    // 無需換行
    var result = condition ? resultA : resultB;
    // 條件超長的情況
    var result = thisIsAVeryVeryLongCondition     ? resultA : resultB;
    //  結果分支超長的情況
    var result = condition
        ? thisIsAVeryVeryLongCondition
        :resultB;
    var result = condition
        ? resultA
        : thisIsAVeryVeryLongCondition;
</script>
不得出現以下情況:
<script>
    // 最後一個結果很長,但不建議合併條件和第一個分支
    // 不要這麼幹
    var result = condition ? resultA
        : thisIsAVeryVeryLongCondition;
</script>
  1. 過長的邏輯條件組合
當因爲較複雜的邏輯條件組合導致80個字符無法滿足需求時,應當將每個條件獨立一行,邏輯運算符放置在行首進行分隔,或將部分邏輯按邏輯組合進行分隔。最終將右括號)與左大括號{放在獨立一行,保證與if內語句塊能容易視覺辨識。如:
<script>
    // 注意邏輯運算符前的縮進
    if (user.isAuthenticated()
        && user.isInRole('admin')
        && user.hasAuthority('add-admin')
        || user.hasAuthority('delete-admin')
    ) {
        // code
    }
</script>
  1. 過長的JSON和數組
如果對象屬性較多導致每個屬性一行佔用空間過大,可以按語義或邏輯進行分組的組織,如:
<script>
    // 引文-數字的映射
    var mapping = {
        one: 1, two: 2, three: 3, four: 4, five: 5,
        six: 6, seven: 7, eight:8, nine: 9, ten: 10,
        eleven: 11
    }
</script>
通過5個一組的分組,將每一行控制在合理的範圍內,並且按邏輯進行了切分。 對於項目較多的數組,也可以採用相同的方法
  1. eturn語句
return如果用表達式的執行作爲返回值,請把表達式和 return 放在同一行中,以免換行符被誤解析爲語句的結束而引起返回錯誤。return 關鍵字後若沒有返回表達式,則返回 undefined。構造器的默認返回值爲 this。

命名

命名的方法通常有以下幾類:
1. 命名法說明
- 1).camel命名法,形如thisIsAnApple
- 2).pascal命名法,形如ThisIsAnApple
- 3).下劃線命名法,形如this_is_an_apple ·
- 4).中劃線命名法,形如this-is-an-apple
根據不同類型的內容,必須嚴格採用如下的命名法:

  1. 變量名:必須使用camel命名法
  2. 參數名:必須使用camel命名法
  3. 函數名:必須使用camel命名法
  4. 方法/屬性:必須使用camel命名法
  5. 私有(保護)成員:必須以下劃線_開頭
  6. 常量名:必須使用全部大寫的下劃線命名法,如IS_DEBUG_ENABLED
  7. 類名:必須使用pascal命名法
  8. 枚舉名:必須使用pascal命名法
  9. 枚舉的屬性:必須使用全部大寫的下劃線命名法
  10. 命名空間:必須使用camel命名法
  11. 語義:命名同時還需要關注語義,如:
    • 變量名應當使用名詞;
    • boolean類型的應當使用is、has等起頭,表示其類型;·
    • 函數名應當用動賓短語;
    • 類名應當用名詞。

聲明

變量的聲明

儘管 JavaScript 語言並不要求在變量使用前先對變量進行聲明。但我們還是應該養成這個好習慣。這樣可以比較容易的檢測出那些未經聲明的變量,避免其變爲隱藏的全局變量,造成隱患。

在函數的開始應先用 var 關鍵字聲明函數中要使用的局部變量,註釋變量的功能及代表的含義,且應以字母順序排序。每個變量單獨佔一行,以便添加註釋。這是因爲 JavaScript 中只有函數的 {} 表明作用域,用 var 關鍵字聲明的局部變量只在函數內有效,而未經 var 聲明的變量則被視爲全局變量。示例:

<script>
    var valueA = "a";
    var valueB = "b";
    function f1() {
        var valueA = "c";
        alert("valueA=" + valueA); // output: valueA=c
        valueB = "d";
        alert("valueB=" + valueB); // output: valueB=d
    }
    f1();
    alert("valueA=" + valueA); // output: valueA=a
    alert("valueB=" + valueB); // output: valueB=d
</script>
用 var 聲明過的變量 valueA 和沒有聲明的變量 valueB 是有區別的。特別需要注意的是,在函數內部用 var 聲明的變量爲局部變量,這樣可以有效地避免因局部變量和全局變量同名而產生的錯誤。

類型

  • 原始值: 相當於傳值
    • string
    • number
    • boolean
    • null
    • undefined
<script>
    var foo = 1,
        bar = foo;
    bar = 9;
console.log(foo, bar); // => 1, 9
</script>
  • 複雜類型: 相當於傳引用
    • object
    • array
    • function
<script>
    var foo = [1, 2],
        bar = foo;
    bar[0] = 9;
    console.log(foo[0], bar[0]); // => 9, 9
</script>

對象

  • 使用字面值創建對象
<script>
// bad
var item = new Object();

// good
var item = {};
</script>
  • 不要使用保留字 reserved words 作爲鍵
<script>
// bad
var superman = {
  class: 'superhero',
  default: { clark: 'kent' },
  private: true
};

// good
var superman = {
  klass: 'superhero',
  defaults: { clark: 'kent' },
  hidden: true
};
</script>

數組

  • 使用字面值創建數組
<script>
// bad
var items = new Array();

// good
var items = [];
</script>
  • 如果你不知道數組的長度,使用push
<script>
var someStack = [];

// bad
someStack[someStack.length] = 'abracadabra';

// good
someStack.push('abracadabra');
</script>
  • 當你需要拷貝數組時使用slice. jsPerf
<script>
var len = items.length,
    itemsCopy = [],
    i;

// bad
for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// good
itemsCopy = items.slice();
</script>
  • 使用slice將類數組的對象轉成數組
<script>
function trigger() {
  var args = Array.prototype.slice.call(arguments);
  ...
}
</script>

字符串

  • 對字符串使用單引號 ”
<script>
// bad
var name = "Bob Parr";

// good
var name = 'Bob Parr';

// bad
var fullName = "Bob " + this.lastName;

// good
var fullName = 'Bob ' + this.lastName;
</script>
  • 超過80個字符的字符串應該使用字符串連接換行
  • 注: 如果過度使用,長字符串連接可能會對性能有影響
<script>
// 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.';
</script>
  • 編程時使用join而不是字符串連接來構建字符串,特別是IE
<script>
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>';
}
</script>

函數

  • 函數表達式:
<script>
// 匿名函數表達式
var anonymous = function() {
  return true;
};

// 有名函數表達式
var named = function named() {
  return true;
};

// 立即調用函數表達式
(function() {
  console.log('Welcome to the Internet. Please follow me.');
})();

</script>
  • 絕對不要在一個非函數塊裏聲明一個函數,把那個函數賦給一個變量。瀏覽器允許你這麼做,但是它們解析不同。
  • 注: ECMA-262定義把塊定義爲一組語句,函數聲明不是一個語句.
<script>
// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// good
if (currentUser) {
  var test = function test() {
    console.log('Yup.');
  };
}

</script>
  • 絕對不要把參數命名爲 arguments, 這將會逾越函數作用域內傳過來的 arguments 對象.
<script>
// bad
function nope(name, options, arguments) {
  // ...stuff...
}

// good
function yup(name, options, args) {
  // ...stuff...
}
</script>

屬性

  • 當使用變量訪問屬性時使用中括號.
<script>
var luke = {
  jedi: true,
  age: 28
};

function getProp(prop) {
  return luke[prop];
}

var isJedi = getProp('jedi');
</script>

變量

  • 總是使用 var 來聲明變量,如果不這麼做將導致產生全局變量,我們要避免污染全局命名空間。
<script>
// bad
superPower = new SuperPower();

// good
var superPower = new SuperPower();
</script>
  • 使用一個 var 以及新行聲明多個變量,縮進4個空格。
<script>
// bad
var items = getItems();
var goSportsTeam = true;
var dragonball = 'z';

// good
var items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';
</script>
  • 最後再聲明未賦值的變量,當你想引用之前已賦值變量的時候很有用。
<script>
// 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;

</script>
  • 在作用域頂部聲明變量,避免變量聲明和賦值引起的相關問題。
<script>
// 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;
}
</script>

條件表達式和等號

  • 適當使用 === 和 !== 以及 == 和 !=
  • 條件表達式的強制類型轉換遵循以下規則:
    • 對象 被計算爲 true
    • Undefined 被計算爲 false
    • Null 被計算爲 false
    • 布爾值 被計算爲 布爾的值
    • 數字 如果是 +0, -0, or NaN 被計算爲 false , 否則爲 true
    • 字符串 如果是空字符串 ” 則被計算爲 false, 否則爲 true
      javascript
      <script>
      if ([0]) {
      // true
      // An array is an object, objects evaluate to true
      }
      </script>
  • 使用快捷方式
<script>
// bad
if (name !== '') {
  // ...stuff...
}

// good
if (name) {
  // ...stuff...
}

// bad
if (collection.length > 0) {
  // ...stuff...
}

// good
if (collection.length) {
  // ...stuff...
}
</script>

  • 給所有多行的塊使用大括號
<script>
// bad
if (test)
  return false;

// good
if (test) return false;

// good
if (test) {
  return false;
}

// bad
function() { return false; }

// good
function() {
  return false;
}
</script>

註釋

註釋要儘量簡單,清晰明瞭。着重註釋的意思,對不太直觀的部分進行註解

JavaScript 的註釋有兩種”//” 和”/* …. */”
- 建議”//”用作代碼行註釋
- “/* …. */”形式用作對整個代碼段的註銷,或較正式的聲明中,如函數參數、功能、文件功能等的描述中
- >另:複製粘貼應注意註釋是否與代碼對應。

  • 使用 /* … / 進行多行註釋,包括描述,指定類型以及參數值和返回值
<script>
// 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;
}
</script>
  • 使用 // 進行單行註釋,在評論對象的上面進行單行註釋,註釋前放一個空行.
<script>
// 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;
}
</script>
  • 如果你有一個問題需要重新來看一下或如果你建議一個需要被實現的解決方法的話需要在你的註釋前面加上 FIXME 或 TODO 幫助其他人迅速理解
<script>
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;
}
</script>

空白

  • 將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 並且總是帶上類型轉換的基數.

    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 的引用時使用 _this.

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

存取器

  • 屬性的存取器函數不是必需的
  • 如果你確實有存取器函數的話使用getVal() 和 setVal(‘hello’)

    // bad
    dragon.age();
    
    // good
    dragon.getAge();
    
    // bad
    dragon.age(25);
    
    // good
    dragon.setAge(25);
  • 如果屬性是布爾值,使用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
    });

模塊

  • 模塊應該以 ! 開始,這保證瞭如果一個有問題的模塊忘記包含最後的分號在合併後不會出現錯誤
  • 這個文件應該以駝峯命名,並在同名文件夾下,同時導出的時候名字一致
  • 加入一個名爲noConflict()的方法來設置導出的模塊爲之前的版本並返回它
  • 總是在模塊頂部聲明 'use strict';

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

  • 緩存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');
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章