1. 程式人生 > >JavaScript編碼規範(2)

JavaScript編碼規範(2)

命名法 嵌套 tlist 繼承 spider cat mon .proto 時機

變量

[強制] 變量、函數在使用前必須先定義。
// good
var name = ‘MyName‘;

// bad
name = ‘MyName‘;
[強制] 每個 var 只能聲明一個變量。

解釋:

一個 var 聲明多個變量,容易導致較長的行長度,並且在修改時容易造成逗號和分號的混淆。

// good
var hangModules = [];
var missModules = [];
var visited = {};

// bad
var hangModules = [],
    missModules = [],
    visited = {};
[強制] 變量必須 即用即聲明,不得在函數或其它形式的代碼塊起始位置統一聲明所有變量。

解釋:

變量聲明與使用的距離越遠,出現的跨度越大,代碼的閱讀與維護成本越高。雖然JavaScript的變量是函數作用域,還是應該根據編程中的意圖,縮小變量出現的距離空間。

// good
function kv2List(source) {
    var list = [];

    for (var key in source) {
        if (source.hasOwnProperty(key)) {
            var item = {
                k: key,
                v: source[key]
            };

            list.push(item);
        }
    }

    return list;
}

// bad
function kv2List(source) {
    var list = [];
    var key;
    var item;

    for (key in source) {
        if (source.hasOwnProperty(key)) {
            item = {
                k: key,
                v: source[key]
            };

            list.push(item);
        }
    }

    return list;
}

條件

[強制] 在 Equality Expression 中使用類型嚴格的 ===。僅當判斷 null 或 undefined 時,允許使用 == null。

解釋:

使用 === 可以避免等於判斷中隱式的類型轉換。

// good
if (age === 30) {
    // ......
}

// bad
if (age == 30) {
    // ......
}
[建議] 盡可能使用簡潔的表達式。
// 字符串為空

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

// bad
if (name === ‘‘) {
    // ......
}

// 字符串非空

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

// bad
if (name !== ‘‘) {
    // ......
}
// 數組非空

// good
if (collection.length) {
    // ......
}

// bad
if (collection.length > 0) {
    // ......
}
// 布爾不成立

// good
if (!notTrue) {
    // ......
}

// bad
if (notTrue === false) {
    // ......
}
// null 或 undefined

// good
if (noValue == null) {
  // ......
}

// bad
if (noValue === null || typeof noValue === ‘undefined‘) {
  // ......
}
[建議] 按執行頻率排列分支的順序。

解釋:

按執行頻率排列分支的順序好處是:

閱讀的人容易找到最常見的情況,增加可讀性。
提高執行效率。

[建議] 對於相同變量或表達式的多值條件,用 switch 代替 if。
// good
switch (typeof variable) {
    case ‘object‘:
        // ......
        break;
    case ‘number‘:
    case ‘boolean‘:
    case ‘string‘:
        // ......
        break;
}

// bad
var type = typeof variable;
if (type === ‘object‘) {
    // ......
}
else if (type === ‘number‘ || type === ‘boolean‘ || type === ‘string‘) {
    // ......
}
[建議] 如果函數或全局中的 else 塊後沒有任何語句,可以刪除 else。
// good
function getName() {
    if (name) {
        return name;
    }

    return ‘unnamed‘;
}

// bad
function getName() {
    if (name) {
        return name;
    }
    else {
        return ‘unnamed‘;
    }
}

循環

[建議] 不要在循環體中包含函數表達式,事先將函數提取到循環體外。

解釋:

循環體中的函數表達式,運行過程中會生成循環次數個函數對象。

// good
function clicker() {
    // ......
}

for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    addListener(element, ‘click‘, clicker);
}


// bad
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    addListener(element, ‘click‘, function () {});
}
[建議] 對循環內多次使用的不變值,在循環外用變量緩存。
// good
var width = wrap.offsetWidth + ‘px‘;
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    element.style.width = width;
    // ......
}


// bad
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    element.style.width = wrap.offsetWidth + ‘px‘;
    // ......
}
[建議] 對有序集合進行遍歷時,緩存 length。

解釋:

雖然現代瀏覽器都對數組長度進行了緩存,但對於一些宿主對象和老舊瀏覽器的數組對象,在每次 length 訪問時會動態計算元素個數,此時緩存 length 能有效提高程序性能。

for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    // ......
}
[建議] 對有序集合進行順序無關的遍歷時,使用逆序遍歷。

解釋:

逆序遍歷可以節省變量,代碼比較優化。

var len = elements.length;
while (len--) {
    var element = elements[len];
    // ......
}

類型

類型檢測
[建議] 類型檢測優先使用 typeof。對象類型檢測使用 instanceof。null 或 undefined 的檢測使用 == null。
// string
typeof variable === ‘string‘

// number
typeof variable === ‘number‘

// boolean
typeof variable === ‘boolean‘

// Function
typeof variable === ‘function‘

// Object
typeof variable === ‘object‘

// RegExp
variable instanceof RegExp

// Array
variable instanceof Array

// null
variable === null

// null or undefined
variable == null

// undefined
typeof variable === ‘undefined‘

類型轉換

[建議] 轉換成 string 時,使用 + ‘‘。
// good
num + ‘‘;

// bad
new String(num);
num.toString();
String(num);
[建議] 轉換成 number 時,通常使用 +。
// good
+str;

// bad
Number(str);
[建議] string 轉換成 number,要轉換的字符串結尾包含非數字並期望忽略時,使用 parseInt。
var width = ‘200px‘;
parseInt(width, 10);
[強制] 使用 parseInt 時,必須指定進制。
// good
parseInt(str, 10);

// bad
parseInt(str);
[建議] 轉換成 boolean 時,使用 !!。
var num = 3.14;
!!num;
[建議] number 去除小數點,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt。
// good
var num = 3.14;
Math.ceil(num);

// bad
var num = 3.14;
parseInt(num, 10);

字符串

[強制] 字符串開頭和結束使用單引號 ‘。

解釋:

輸入單引號不需要按住 shift,方便輸入。
實際使用中,字符串經常用來拼接 HTML。為方便 HTML 中包含雙引號而不需要轉義寫法。

var str = ‘我是一個字符串‘;
var html = ‘<div class="cls">拼接HTML可以省去雙引號轉義</div>‘;
[建議] 使用 數組 或 + 拼接字符串。

解釋:

使用 + 拼接字符串,如果拼接的全部是 StringLiteral,壓縮工具可以對其進行自動合並的優化。所以,靜態字符串建議使用 + 拼接。
在現代瀏覽器下,使用 + 拼接字符串,性能較數組的方式要高。
如需要兼顧老舊瀏覽器,應盡量使用數組拼接字符串。

// 使用數組拼接字符串
var str = [
    // 推薦換行開始並縮進開始第一個字符串, 對齊代碼, 方便閱讀.
    ‘<ul>‘,
        ‘<li>第一項</li>‘,
        ‘<li>第二項</li>‘,
    ‘</ul>‘
].join(‘‘);

// 使用 `+` 拼接字符串
var str2 = ‘‘ // 建議第一個為空字符串, 第二個換行開始並縮進開始, 對齊代碼, 方便閱讀
    + ‘<ul>‘,
    +    ‘<li>第一項</li>‘,
    +    ‘<li>第二項</li>‘,
    + ‘</ul>‘;
[建議] 使用字符串拼接的方式生成HTML,需要根據語境進行合理的轉義。

解釋:

在 JavaScript 中拼接,並且最終將輸出到頁面中的字符串,需要進行合理轉義,以防止安全漏洞。下面的示例代碼為場景說明,不能直接運行。

// HTML 轉義
var str = ‘<p>‘ + htmlEncode(content) + ‘</p>‘;

// HTML 轉義
var str = ‘<input type="text" value="‘ + htmlEncode(value) + ‘">‘;

// URL 轉義
var str = ‘<a href="/?key=‘ + htmlEncode(urlEncode(value)) + ‘">link</a>‘;

// JavaScript字符串 轉義 + HTML 轉義
var str = ‘<button onclick="check(\‘‘ + htmlEncode(strLiteral(name)) + ‘\‘)">提交</button>‘;

對象

[強制] 使用對象字面量 {} 創建新 Object。
// good
var obj = {};

// bad
var obj = new Object();
[建議] 對象創建時,如果一個對象的所有 屬性 均可以不添加引號,建議所有 屬性 不添加引號。
var info = {
    name: ‘someone‘,
    age: 28
};
[建議] 對象創建時,如果任何一個 屬性 需要添加引號,則所有 屬性 建議添加 ‘。

解釋:

如果屬性不符合 Identifier 和 NumberLiteral 的形式,就需要以 StringLiteral 的形式提供。

// good
var info = {
    ‘name‘: ‘someone‘,
    ‘age‘: 28,
    ‘more-info‘: ‘...‘
};

// bad
var info = {
    name: ‘someone‘,
    age: 28,
    ‘more-info‘: ‘...‘
};
[建議] 屬性訪問時,盡量使用 .。

解釋:

屬性名符合 Identifier 的要求,就可以通過 . 來訪問,否則就只能通過 [expr] 方式訪問。

通常在 JavaScript 中聲明的對象,屬性命名是使用 Camel 命名法,用 . 來訪問更清晰簡潔。部分特殊的屬性(比如來自後端的 JSON ),可能采用不尋常的命名方式,可以通過 [expr] 方式訪問。

info.age;
info[‘more-info‘];
[建議] for in 遍歷對象時, 使用 hasOwnProperty 過濾掉原型中的屬性。
var newInfo = {};
for (var key in info) {
    if (info.hasOwnProperty(key)) {
        newInfo[key] = info[key];
    }
}

數組

[強制] 使用數組字面量 [] 創建新數組,除非想要創建的是指定長度的數組。
// good
var arr = [];

// bad
var arr = new Array();
[強制] 遍歷數組不使用 for in。

解釋:

數組對象可能存在數字以外的屬性, 這種情況下 for in 不會得到正確結果。

var arr = [‘a‘, ‘b‘, ‘c‘];

// 這裏僅作演示, 實際中應使用 Object 類型
arr.other = ‘other things‘;

// 正確的遍歷方式
for (var i = 0, len = arr.length; i < len; i++) {
    console.log(i);
}

// 錯誤的遍歷方式
for (var i in arr) {
    console.log(i);
}
[建議] 不因為性能的原因自己實現數組排序功能,盡量使用數組的 sort 方法。

解釋:

自己實現的常規排序算法,在性能上並不優於數組默認的 sort 方法。以下兩種場景可以自己實現排序:

需要穩定的排序算法,達到嚴格一致的排序結果。
數據特點鮮明,適合使用桶排。

[建議] 清空數組使用 .length = 0。

函數

函數長度
[建議] 一個函數的長度控制在 50 行以內。

解釋:

將過多的邏輯單元混在一個大函數中,易導致難以維護。一個清晰易懂的函數應該完成單一的邏輯單元。復雜的操作應進一步抽取,通過函數的調用來體現流程。

特定算法等不可分割的邏輯允許例外。

function syncViewStateOnUserAction() {
    if (x.checked) {
        y.checked = true;
        z.value = ‘‘;
    }
    else {
        y.checked = false;
    }

    if (a.value) {
        warning.innerText = ‘‘;
        submitButton.disabled = false;
    }
    else {
        warning.innerText = ‘Please enter it‘;
        submitButton.disabled = true;
    }
}

// 直接閱讀該函數會難以明確其主線邏輯,因此下方是一種更合理的表達方式:

function syncViewStateOnUserAction() {
    syncXStateToView();
    checkAAvailability();
}

function syncXStateToView() {
    y.checked = x.checked;

    if (x.checked) {
        z.value = ‘‘;
    }
}

function checkAAvailability() {
    if (a.value) {
        clearWarnignForA();
    }
    else {
        displayWarningForAMissing();
    }
}
[建議] 一個函數的參數控制在 6 個以內。

解釋:

除去不定長參數以外,函數具備不同邏輯意義的參數建議控制在 6 個以內,過多參數會導致維護難度增大。

某些情況下,如使用 AMD Loader 的 require 加載多個模塊時,其 callback 可能會存在較多參數,因此對函數參數的個數不做強制限制。

[建議] 通過 options 參數傳遞非數據輸入型參數。

解釋:

有些函數的參數並不是作為算法的輸入,而是對算法的某些分支條件判斷之用,此類參數建議通過一個 options 參數傳遞。
如下函數:

/**
 * 移除某個元素
 *
 * @param {Node} element 需要移除的元素
 * @param {boolean} removeEventListeners 是否同時將所有註冊在元素上的事件移除
 */
function removeElement(element, removeEventListeners) {
    element.parent.removeChild(element);

    if (removeEventListeners) {
        element.clearEventListeners();
    }
}

可以轉換為下面的簽名:

/**
 * 移除某個元素
 *
 * @param {Node} element 需要移除的元素
 * @param {Object} options 相關的邏輯配置
 * @param {boolean} options.removeEventListeners 是否同時將所有註冊在元素上的事件移除
 */
function removeElement(element, options) {
    element.parent.removeChild(element);

    if (options.removeEventListeners) {
        element.clearEventListeners();
    }
}
這種模式有幾個顯著的優勢:

boolean 型的配置項具備名稱,從調用的代碼上更易理解其表達的邏輯意義。
當配置項有增長時,無需無休止地增加參數個數,不會出現 removeElement(element, true, false, false, 3) 這樣難以理解的調用代碼。
當部分配置參數可選時,多個參數的形式非常難處理重載邏輯,而使用一個 options 對象只需判斷屬性是否存在,實現得以簡化。

閉包

[建議] 在適當的時候將閉包內大對象置為 null。

解釋:

在 JavaScript 中,無需特別的關鍵詞就可以使用閉包,一個函數可以任意訪問在其定義的作用域外的變量。需要註意的是,函數的作用域是靜態的,即在定義時決定,與調用的時機和方式沒有任何關系。

閉包會阻止一些變量的垃圾回收,對於較老舊的 JavaScript 引擎,可能導致外部所有變量均無法回收。

首先一個較為明確的結論是,以下內容會影響到閉包內變量的回收:

嵌套的函數中是否有使用該變量。
嵌套的函數中是否有 直接調用eval。
是否使用了 with 表達式。
Chakra、V8 和 SpiderMonkey 將受以上因素的影響,表現出不盡相同又較為相似的回收策略,而 JScript.dll 和 Carakan 則完全沒有這方面的優化,會完整保留整個 LexicalEnvironment 中的所有變量綁定,造成一定的內存消耗。

由於對閉包內變量有回收優化策略的 Chakra、V8 和 SpiderMonkey 引擎的行為較為相似,因此可以總結如下,當返回一個函數 fn 時:

  1. 如果 fn 的 [[Scope]] 是 ObjectEnvironment(with 表達式生成 ObjectEnvironment,函數和 catch 表達式生成 DeclarativeEnvironment),則:
    如果是 V8 引擎,則退出全過程。
    如果是 SpiderMonkey,則處理該 ObjectEnvironment 的外層 LexicalEnvironment。
  2. 獲取當前 LexicalEnvironment 下的所有類型為 Function 的對象,對於每一個 Function 對象,分析其 FunctionBody:
    如果 FunctionBody 中含有 直接調用 eval,則退出全過程。
    否則得到所有的 Identifier。
    對於每一個 Identifier,設其為 name,根據查找變量引用的規則,從 LexicalEnvironment 中找出名稱為 name 的綁定 binding。
    對 binding 添加 notSwap 屬性,其值為 true。
  3. 檢查當前 LexicalEnvironment 中的每一個變量綁定,如果該綁定有 notSwap 屬性且值為 true,則:
    如果是 V8 引擎,刪除該綁定。
    如果是 SpiderMonkey,將該綁定的值設為 undefined,將刪除 notSwap 屬性。
    對於 Chakra 引擎,暫無法得知是按 V8 的模式還是按 SpiderMonkey 的模式進行。

如果有 非常龐大 的對象,且預計會在 老舊的引擎 中執行,則使用閉包時,註意將閉包不需要的對象置為空引用。

[建議] 使用 IIFE 避免 Lift 效應。

解釋:

在引用函數外部變量時,函數執行時外部變量的值由運行時決定而非定義時,最典型的場景如下:

var tasks = [];
for (var i = 0; i < 5; i++) {
    tasks[tasks.length] = function () {
        console.log(‘Current cursor is at ‘ + i);
    };
}

var len = tasks.length;
while (len--) {
    tasks[len]();
}

以上代碼對 tasks 中的函數的執行均會輸出 Current cursor is at 5,往往不符合預期。

此現象稱為 Lift 效應 。解決的方式是通過額外加上一層閉包函數,將需要的外部變量作為參數傳遞來解除變量的綁定關系:

var tasks = [];
for (var i = 0; i < 5; i++) {
    // 註意有一層額外的閉包
    tasks[tasks.length] = (function (i) {
        return function () {
            console.log(‘Current cursor is at ‘ + i);
        };
    })(i);
}

var len = tasks.length;
while (len--) {
    tasks[len]();
}
[建議] 空函數不使用 new Function() 的形式。
var emptyFunction = function () {};
[建議] 對於性能有高要求的場合,建議存在一個空函數的常量,供多處使用共享。
var EMPTY_FUNCTION = function () {};

function MyClass() {
}

MyClass.prototype.abstractMethod = EMPTY_FUNCTION;
MyClass.prototype.hooks.before = EMPTY_FUNCTION;
MyClass.prototype.hooks.after = EMPTY_FUNCTION;

面向對象

[強制] 類的繼承方案,實現時需要修正 constructor

解釋:

通常使用其他 library 的類繼承方案都會進行 constructor 修正。如果是自己實現的類繼承方案,需要進行 constructor 修正。

/**
 * 構建類之間的繼承關系
 *
 * @param {Function} subClass 子類函數
 * @param {Function} superClass 父類函數
 */
function inherits(subClass, superClass) {
    var F = new Function();
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructor = subClass;
}
[建議] 聲明類時,保證 constructor 的正確性。
function Animal(name) {
    this.name = name;
}

// 直接prototype等於對象時,需要修正constructor
Animal.prototype = {
    constructor: Animal,

    jump: function () {
        alert(‘animal ‘ + this.name + ‘ jump‘);
    }
};

// 這種方式擴展prototype則無需理會constructor
Animal.prototype.jump = function () {
    alert(‘animal ‘ + this.name + ‘ jump‘);
};
[建議] 屬性在構造函數中聲明,方法在原型中聲明。

解釋:

原型對象的成員被所有實例共享,能節約內存占用。所以編碼時我們應該遵守這樣的原則:原型對象包含程序不會修改的成員,如方法函數或配置項。

function TextNode(value, engine) {
    this.value = value;
    this.engine = engine;
}

TextNode.prototype.clone = function () {
    return this;
};
[強制] 自定義事件的 事件名 必須全小寫。

解釋:

在 JavaScript 廣泛應用的瀏覽器環境,絕大多數 DOM 事件名稱都是全小寫的。為了遵循大多數 JavaScript 開發者的習慣,在設計自定義事件時,事件名也應該全小寫。

[強制] 自定義事件只能有一個 event 參數。如果事件需要傳遞較多信息,應仔細設計事件對象。

解釋:

一個事件對象的好處有:

順序無關,避免事件監聽者需要記憶參數順序。
每個事件信息都可以根據需要提供或者不提供,更自由。
擴展方便,未來添加事件信息時,無需考慮會破壞

[建議] 設計自定義事件時,應考慮禁止默認行為。

解釋:

常見禁止默認行為的方式有兩種:

事件監聽函數中 return false。
事件對象中包含禁止默認行為的方法,如 preventDefault。

禁止使用evail推薦使用new Function()
[建議] 減少 delete 的使用。

解釋:

如果沒有特別的需求,減少或避免使用 delete。delete 的使用會破壞部分 JavaScript 引擎的性能優化。

[建議] 處理 delete 可能產生的異常。

解釋:

對於有被遍歷需求,且值 null 被認為具有業務邏輯意義的值的對象,移除某個屬性必須使用 delete 操作。

在嚴格模式或 IE 下使用 delete 時,不能被刪除的屬性會拋出異常,因此在不確定屬性是否可以刪除的情況下,建議添加 try-catch 塊。

try {
    delete o.x;
}
catch (deleteError) {
    o.x = null;
}
避免修改外部傳入的對象。

解釋:

JavaScript 因其腳本語言的動態特性,當一個對象未被 seal 或 freeze 時,可以任意添加、刪除、修改屬性值。

但是隨意地對 非自身控制的對象 進行修改,很容易造成代碼在不可預知的情況下出現問題。因此,設計良好的組件、函數應該避免對外部傳入的對象的修改。

下面代碼的 selectNode 方法修改了由外部傳入的 datasource 對象。如果 datasource 用在其它場合(如另一個 Tree 實例)下,會造成狀態的混亂。

function Tree(datasource) {
    this.datasource = datasource;
}

Tree.prototype.selectNode = function (id) {
    // 從datasource中找出節點對象
    var node = this.findNode(id);
    if (node) {
        node.selected = true;
        this.flushView();
    }
};

對於此類場景,需要使用額外的對象來維護,使用由自身控制,不與外部產生任何交互的 selectedNodeIndex 對象來維護節點的選中狀態,不對 datasource 作任何修改。

function Tree(datasource) {
    this.datasource = datasource;
    this.selectedNodeIndex = {};
}

Tree.prototype.selectNode = function (id) {

    // 從datasource中找出節點對象
    var node = this.findNode(id);

    if (node) {
        this.selectedNodeIndex[id] = true;
        this.flushView();
    }

};
除此之外,也可以通過 deepClone 等手段將自身維護的對象與外部傳入的分離,保證不會相互影響。

DOM

[建議] 對於單個元素,盡可能使用 document.getElementById 獲取,避免使用document.all。
[建議] 對於多個元素的集合,盡可能使用 context.getElementsByTagName 獲取。其中 context 可以為 document 或其他元素。指定 tagName 參數為 * 可以獲得所有子元素。
[建議] 遍歷元素集合時,盡量緩存集合長度。如需多次操作同一集合,則應將集合轉為數組。
[建議] 獲取元素的直接子元素時使用 children。避免使用childNodes,除非預期是需要包含文本、註釋和屬性類型的節點。

解釋:

原生獲取元素集合的結果並不直接引用 DOM 元素,而是對索引進行讀取,所以 DOM 結構的改變會實時反映到結果中。

<div></div>
<span></span>

<script>
var elements = document.getElementsByTagName(‘*‘);

// 顯示為 DIV
alert(elements[0].tagName);

var div = elements[0];
var p = document.createElement(‘p‘);
docpment.body.insertBefore(p, div);

// 顯示為 P
alert(elements[0].tagName);
</script>
[建議] 獲取元素實際樣式信息時,應使用 getComputedStyle 或 currentStyle。

解釋:

通過 style 只能獲得內聯定義或通過 JavaScript 直接設置的樣式。通過 CSS class 設置的元素樣式無法直接通過 style 獲取。

[建議] 盡可能通過為元素添加預定義的 className 來改變元素樣式,避免直接操作 style 設置
[強制] 通過 style 對象設置元素樣式時,對於帶單位非 0 值的屬性,不允許省略單位。

解釋:

除了 IE,標準瀏覽器會忽略不規範的屬性值,導致兼容性問題。

[建議] 操作 DOM 時,盡量減少頁面 reflow。

解釋:

頁面 reflow 是非常耗時的行為,非常容易導致性能瓶頸。下面一些場景會觸發瀏覽器的reflow:

  • DOM元素的添加、修改(內容)、刪除。
  • 應用新的樣式或者修改任何影響元素布局的屬性。
  • Resize瀏覽器窗口、滾動頁面。
  • 讀取元素的某些屬性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)) 。

    [建議] 盡量減少 DOM 操作。

    解釋:

DOM 操作也是非常耗時的一種操作,減少 DOM 操作有助於提高性能。舉一個簡單的例子,構建一個列表。我們可以用兩種方式:

在循環體中 createElement 並 append 到父元素中。
在循環體中拼接 HTML 字符串,循環結束後寫父元素的 innerHTML。
第一種方法看起來比較標準,但是每次循環都會對 DOM 進行操作,性能極低。在這裏推薦使用第二種方法。

JavaScript編碼規範(2)