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

JavaScript編碼規範(1)

其他 length xtend 構造函數 port 類型 ons 示例 java

參考的是百度公司的JS規範,分為兩部分。這是第一部分
[建議] JavaScript 文件使用無 BOM 的 UTF-8 編碼。

空格

[強制] 二元運算符兩側必須有一個空格,一元運算符與操作對象之間不允許有空格。
var a = !arr.length;
a++;
a = b + c;
[強制] 用作代碼塊起始的左花括號 { 前必須有一個空格。
// good
if (condition) {
}

while (condition) {
}

function funcName() {
}

// bad
if (condition){
}

while (condition){
}

function funcName(){
}
[強制] if / else / for / while / function / switch / do / try / catch / finally 關鍵字後,必須有一個空格。
// good
if (condition) {
}

while (condition) {
}

(function () {
})();

// bad
if(condition) {
}

while(condition) {
}

(function() {
})();
[強制] 在對象創建時,屬性中的 : 之後必須有空格,: 之前不允許有空格。
// good
var obj = {
    a: 1,
    b: 2,
    c: 3
};

// bad
var obj = {
    a : 1,
    b:2,
    c :3
};
[強制] 函數聲明、具名函數表達式、函數調用中,函數名和 ( 之間不允許有空格。
// good
function funcName() {
}

var funcName = function funcName() {
};

funcName();

// bad
function funcName () {
}

var funcName = function funcName () {
};

funcName ();
[強制] , 和 ; 前不允許有空格。如果不位於行尾,, 和 ; 後必須跟一個空格。
// good
callFunc(a, b);

// bad
callFunc(a , b) ;
[強制] 在函數調用、函數聲明、括號表達式、屬性訪問、if / for / while / switch / catch 等語句中,() 和 [] 內緊貼括號部分不允許有空格。
// good

callFunc(param1, param2, param3);

save(this.list[this.indexes[i]]);

needIncream && (variable += increament);

if (num > list.length) {
}

while (len--) {
}


// bad

callFunc( param1, param2, param3 );

save( this.list[ this.indexes[ i ] ] );

needIncreament && ( variable += increament );

if ( num > list.length ) {
}

while ( len-- ) {
}
[強制] 單行聲明的數組與對象,如果包含元素,{} 和 [] 內緊貼括號部分不允許包含空格。

解釋:

聲明包含元素的數組與對象,只有當內部元素的形式較為簡單時,才允許寫在一行。元素復雜的情況,還是應該換行書寫。

// good
var arr1 = [];
var arr2 = [1, 2, 3];
var obj1 = {};
var obj2 = {name: ‘obj‘};
var obj3 = {
    name: ‘obj‘,
    age: 20,
    sex: 1
};

// bad
var arr1 = [ ];
var arr2 = [ 1, 2, 3 ];
var obj1 = { };
var obj2 = { name: ‘obj‘ };
var obj3 = {name: ‘obj‘, age: 20, sex: 1};

換行

[強制] 每個獨立語句結束後必須換行。
[強制] 每行不得超過 120 個字符。

解釋:

超長的不可分割的代碼允許例外,比如復雜的正則表達式。長字符串不在例外之列。

[強制] 運算符處換行時,運算符必須在新行的行首。
// good
if (user.isAuthenticated()
    && user.isInRole(‘admin‘)
    && user.hasAuthority(‘add-admin‘)
    || user.hasAuthority(‘delete-admin‘)
) {
    // Code
}

var result = number1 + number2 + number3
    + number4 + number5;


// bad
if (user.isAuthenticated() &&
    user.isInRole(‘admin‘) &&
    user.hasAuthority(‘add-admin‘) ||
    user.hasAuthority(‘delete-admin‘)) {
    // Code
}

var result = number1 + number2 + number3 +
    number4 + number5;
[強制] 在函數聲明、函數表達式、函數調用、對象創建、數組創建、for 語句等場景中,不允許在 , 或 ; 前換行。
// good
var obj = {
    a: 1,
    b: 2,
    c: 3
};

foo(
    aVeryVeryLongArgument,
    anotherVeryLongArgument,
    callback
);


// bad
var obj = {
    a: 1
    , b: 2
    , c: 3
};

foo(
    aVeryVeryLongArgument
    , anotherVeryLongArgument
    , callback
);
[建議] 不同行為或邏輯的語句集,使用空行隔開,更易閱讀。
// 僅為按邏輯換行的示例,不代表setStyle的最優實現
function setStyle(element, property, value) {
    if (element == null) {
        return;
    }

    element.style[property] = value;
}
[建議] 在語句的行長度超過 120 時,根據邏輯條件合理縮進。
// 較復雜的邏輯條件組合,將每個條件獨立一行,邏輯運算符放置在行首進行分隔,或將部分邏輯按邏輯組合進行分隔。
// 建議最終將右括號 ) 與左大括號 { 放在獨立一行,保證與 `if` 內語句塊能容易視覺辨識。
if (user.isAuthenticated()
    && user.isInRole(‘admin‘)
    && user.hasAuthority(‘add-admin‘)
    || user.hasAuthority(‘delete-admin‘)
) {
    // Code
}

// 按一定長度截斷字符串,並使用 + 運算符進行連接。
// 分隔字符串盡量按語義進行,如不要在一個完整的名詞中間斷開。
// 特別的,對於 HTML 片段的拼接,通過縮進,保持和 HTML 相同的結構。
var html = ‘‘ // 此處用一個空字符串,以便整個 HTML 片段都在新行嚴格對齊
    + ‘<article>‘
    +     ‘<h1>Title here</h1>‘
    +     ‘<p>This is a paragraph</p>‘
    +     ‘<footer>Complete</footer>‘
    + ‘</article>‘;

// 也可使用數組來進行拼接,相對 `+` 更容易調整縮進。
var html = [
    ‘<article>‘,
        ‘<h1>Title here</h1>‘,
        ‘<p>This is a paragraph</p>‘,
        ‘<footer>Complete</footer>‘,
    ‘</article>‘
];
html = html.join(‘‘);

// 當參數過多時,將每個參數獨立寫在一行上,並將結束的右括號 ) 獨立一行。
// 所有參數必須增加一個縮進。
foo(
    aVeryVeryLongArgument,
    anotherVeryLongArgument,
    callback
);

// 也可以按邏輯對參數進行組合。
// 最經典的是 baidu.format 函數,調用時將參數分為“模板”和“數據”兩塊
baidu.format(
    dateFormatTemplate,
    year, month, date, hour, minute, second
);

// 當函數調用時,如果有一個或以上參數跨越多行,應當每一個參數獨立一行。
// 這通常出現在匿名函數或者對象初始化等作為參數時,如 `setTimeout` 函數等。
setTimeout(
    function () {
        alert(‘hello‘);
    },
    200
);

order.data.read(
    ‘id=‘ + me.model.id,
    function (data) {
        me.attchToModel(data.result);
        callback();
    },
    300
);

// 鏈式調用較長時采用縮進進行調整。
$(‘#items‘)
    .find(‘.selected‘)
    .highlight()
    .end();

// 三元運算符由3部分組成,因此其換行應當根據每個部分的長度不同,形成不同的情況。
var result = thisIsAVeryVeryLongCondition
    ? resultA : resultB;

var result = condition
    ? thisIsAVeryVeryLongResult
    : resultB;

// 數組和對象初始化的混用,嚴格按照每個對象的 `{` 和結束 `}` 在獨立一行的風格書寫。
var array = [
    {
        // ...
    },
    {
        // ...
    }
];
[建議] 對於 if...else...、try...catch...finally 等語句,推薦使用在 } 號後添加一個換行 的風格,使代碼層次結構更清晰,閱讀性更好。
if (condition) {
    // some statements;
}
else {
    // some statements;
}

try {
    // some statements;
}
catch (ex) {
    // some statements;
}

語句

[強制] 不得省略語句結束的分號。
[強制] 在 if / else / for / do / while 語句中,即使只有一行,也不得省略塊 {...}。
// good
if (condition) {
    callFunc();
}

// bad
if (condition) callFunc();
if (condition)
    callFunc();
[強制] 函數定義結束不允許添加分號。
// good
function funcName() {
}

// bad
function funcName() {
};

// 如果是函數表達式,分號是不允許省略的。
var funcName = function () {
};
[強制] IIFE 必須在函數表達式外添加 (,非 IIFE 不得在函數表達式外添加 (。

解釋:

IIFE = Immediately-Invoked Function Expression.

額外的 ( 能夠讓代碼在閱讀的一開始就能判斷函數是否立即被調用,進而明白接下來代碼的用途。而不是一直拖到底部才恍然大悟。

// good
var task = (function () {
   // Code
   return result;
})();

var func = function () {
};


// bad
var task = function () {
    // Code
    return result;
}();

var func = (function () {
});

命名

[強制] 變量 使用 Camel命名法。
var loadingModules = {};
[強制] 常量 使用 全部字母大寫,單詞間下劃線分隔 的命名方式。
var HTML_ENTITY = {};
[強制] 函數的 參數 使用 Camel命名法。
function hear(theBells) {
}
[強制] 類 使用 Pascal命名法。
function TextNode(options) {
}
[強制] 類的 方法 / 屬性 使用 Camel命名法。
function TextNode(value, engine) {
    this.value = value;
    this.engine = engine;
}

TextNode.prototype.clone = function () {
    return this;
};
[強制] 枚舉變量 使用 Pascal命名法,枚舉的屬性 使用 全部字母大寫,單詞間下劃線分隔 的命名方式。
var TargetState = {
    READING: 1,
    READED: 2,
    APPLIED: 3,
    READY: 4
};
[強制] 命名空間 使用 Camel命名法。
equipments.heavyWeapons = {};
[強制] 由多個單詞組成的縮寫詞,在命名中,根據當前命名法和出現的位置,所有字母的大小寫與首字母的大小寫保持一致。
function XMLParser() {
}

function insertHTML(element, html) {
}

var httpRequest = new HTTPRequest();
[強制] 類名 使用 名詞。
function Engine(options) {
}
[建議] 函數名 使用 動賓短語。
function getStyle(element) {
}
[建議] boolean 類型的變量使用 is 或 has 開頭。
var isReady = false;
var hasMoreCommands = false;
[建議] Promise對象 用 動賓短語的進行時 表達。
var loadingData = ajax.get(‘url‘);
loadingData.then(callback);

註釋

單行註釋 [強制] 必須獨占一行。// 後跟一個空格,縮進與下一行被註釋說明的代碼一致。
多行註釋 [建議] 避免使用 /.../ 這樣的多行註釋。有多行註釋內容時,使用多個單行註釋。
文檔化註釋 [強制] 為了便於代碼閱讀和自文檔化,以下內容必須包含以 /**...*/ 形式的塊註釋中。
解釋:
  • 文件
  • namespace
  • 函數或方法
  • 類屬性
  • 事件
  • 全局變量
  • 常量
  • AMD模塊

    [強制] 文檔註釋前必須空一行。
    [建議] 自文檔化的文檔說明 what,而不是 how。
    文件註釋 [強制] 文件頂部必須包含文件註釋,用 @file 標識文件說明。
/**
 * @file Describe the file
 */
[建議] 文件註釋中可以用 @author 標識開發者信息。

解釋:

開發者信息能夠體現開發人員對文件的貢獻,並且能夠讓遇到問題或希望了解相關信息的人找到維護人。通常情況文件在被創建時標識的是創建者。隨著項目的進展,越來越多的人加入,參與這個文件的開發,新的作者應該被加入 @author 標識。

@author 標識具有多人時,原則是按照 責任 進行排序。通常的說就是如果有問題,就是找第一個人應該比找第二個人有效。比如文件的創建者由於各種原因,模塊移交給了其他人或其他團隊,後來因為新增需求,其他人在新增代碼時,添加 @author 標識應該把自己的名字添加在創建人的前面。

@author 中的名字不允許被刪除。任何勞動成果都應該被尊重。

業務項目中,一個文件可能被多人頻繁修改,並且每個人的維護時間都可能不會很長,不建議為文件增加 @author 標識。通過版本控制系統追蹤變更,按業務邏輯單元確定模塊的維護責任人,通過文檔與wiki跟蹤和查詢,是更好的責任管理方式。

對於業務邏輯無關的技術型基礎項目,特別是開源的公共項目,應使用 @author 標識。

/**
 * @file Describe the file
 * @author author-name([email protected])
 *         author-name2([email protected])
 */
命名空間註釋 [建議] 命名空間使用 @namespace 標識
/**
 * @namespace
 */
var util = {};
類註釋

解釋:

對於使用對象 constructor 屬性來定義的構造函數,可以使用 @constructor 來標記。

/**
 * 描述
 *
 * @class
 */
function Developer() {
    // constructor body
}
[建議] 使用 @extends 標記類的繼承信息。
/**
 * 描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
    Developer.call(this);
    // constructor body
}
util.inherits(Fronteer, Developer);
[強制] 使用包裝方式擴展類成員時, 必須通過 @lends 進行重新指向。

解釋:

沒有 @lends 標記將無法為該類生成包含擴展類成員的文檔。

/**
 * 類描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
    Developer.call(this);
    // constructor body
}

util.extend(
    Fronteer.prototype,
    /** @lends Fronteer.prototype */{
        getLevel: function () {
            // TODO
        }
    }
);
[強制] 類的屬性或方法等成員信息不是 public 的,應使用 @protected 或 @private 標識可訪問性。

解釋:

生成的文檔中將有可訪問性的標記,避免用戶直接使用非 public 的屬性或方法。

/**
 * 類描述
 *
 * @class
 * @extends Developer
 */
var Fronteer = function () {
    Developer.call(this);

    /**
     * 屬性描述
     *
     * @type {string}
     * @private
     */
    this.level = ‘T12‘;

    // constructor body
};
util.inherits(Fronteer, Developer);

/**
 * 方法描述
 *
 * @private
 * @return {string} 返回值描述
 */
Fronteer.prototype.getLevel = function () {
};

函數/方法註釋

[強制] 函數/方法註釋必須包含函數說明,有參數和返回值時必須使用註釋標識。

解釋:

當 return 關鍵字僅作退出函數/方法使用時,無須對返回值作註釋標識。

[強制] 參數和返回值註釋必須包含類型信息,且不允許省略參數的說明。
[建議] 當函數是內部函數,外部不可訪問時,可以使用 @inner 標識。
/**
 * 函數描述
 *
 * @param {string} p1 參數1的說明
 * @param {string} p2 參數2的說明,比較長
 *     那就換行了.
 * @param {number=} p3 參數3的說明(可選)
 * @return {Object} 返回值描述
 */
function foo(p1, p2, p3) {
    var p3 = p3 || 10;
    return {
        p1: p1,
        p2: p2,
        p3: p3
    };
}
[強制] 對 Object 中各項的描述, 必須使用 @param 標識。
/**
 * 函數描述
 *
 * @param {Object} option 參數描述
 * @param {string} option.url option項描述
 * @param {string=} option.method option項描述,可選參數
 */
function foo(option) {
    // TODO
}

事件註釋

[強制] 必須使用 @event 標識事件,事件參數的標識與方法描述的參數標識相同。
/**
 * 值變更時觸發
 *
 * @event Select#change
 * @param {Object} e e描述
 * @param {string} e.before before描述
 * @param {string} e.after after描述
 */
this.fire(
    ‘change‘,
    {
        before: ‘foo‘,
        after: ‘bar‘
    }
);
[強制] 在會廣播事件的函數前使用 @fires 標識廣播的事件,在廣播事件代碼前使用 @event 標識事件。
[建議] 對於事件對象的註釋,使用 @param 標識,生成文檔時可讀性更好。
/**
 * 點擊處理
 *
 * @fires Select#change
 * @private
 */
Select.prototype.clickHandler = function () {

    /**
     * 值變更時觸發
     *
     * @event Select#change
     * @param {Object} e e描述
     * @param {string} e.before before描述
     * @param {string} e.after after描述
     */
    this.fire(
        ‘change‘,
        {
            before: ‘foo‘,
            after: ‘bar‘
        }
    );
};

常量註釋

[強制] 常量必須使用 @const 標記,並包含說明和類型信息。
/**
 * 常量說明
 *
 * @const
 * @type {string}
 */
var REQUEST_URL = ‘myurl.do‘;

復雜類型註釋

[建議] 對於類型未定義的復雜結構的註釋,可以使用 @typedef 標識來定義。
// `namespaceA~` 可以換成其它 namepaths 前綴,目的是為了生成文檔中能顯示 `@typedef` 定義的類型和鏈接。
/**
 * 服務器
 *
 * @typedef {Object} namespaceA~Server
 * @property {string} host 主機
 * @property {number} port 端口
 */

/**
 * 服務器列表
 *
 * @type {Array.<namespaceA~Server>}
 */
var servers = [
    {
        host: ‘1.2.3.4‘,
        port: 8080
    },
    {
        host: ‘1.2.3.5‘,
        port: 8081
    }
];

AMD 模塊註釋

[強制] AMD 模塊使用 @module 或 @exports 標識。

解釋:

@exports 與 @module 都可以用來標識模塊,區別在於 @module 可以省略模塊名稱。而只使用 @exports 時在 namepaths 中可以省略 module: 前綴。

define(
    function (require) {

        /**
         * foo description
         *
         * @exports Foo
         */
        var foo = {
            // TODO
        };

        /**
         * baz description
         *
         * @return {boolean} return description
         */
        foo.baz = function () {
            // TODO
        };

        return foo;

    }
);

也可以在 exports 變量前使用 @module 標識:

define(
    function (require) {

        /**
         * module description.
         *
         * @module foo
         */
        var exports = {};


        /**
         * bar description
         *
         */
        exports.bar = function () {
            // TODO
        };

        return exports;
    }
);

如果直接使用 factory 的 exports 參數,還可以:

/**
 * module description.
 *
 * @module
 */
define(
    function (require, exports) {

        /**
         * bar description
         *
         */
        exports.bar = function () {
            // TODO
        };
        return exports;
    }
);
[強制] 對於已使用 @module 標識為 AMD模塊 的引用,在 namepaths 中必須增加 module: 作前綴。

解釋:

namepaths 沒有 module: 前綴時,生成的文檔中將無法正確生成鏈接。

/**
 * 點擊處理
 *
 * @fires module:Select#change
 * @private
 */
Select.prototype.clickHandler = function () {
    /**
     * 值變更時觸發
     *
     * @event module:Select#change
     * @param {Object} e e描述
     * @param {string} e.before before描述
     * @param {string} e.after after描述
     */
    this.fire(
        ‘change‘,
        {
            before: ‘foo‘,
            after: ‘bar‘
        }
    );
};
[建議] 對於類定義的模塊,可以使用 @alias 標識構建函數。
/**
 * A module representing a jacket.
 * @module jacket
 */
define(
    function () {

        /**
         * @class
         * @alias module:jacket
         */
        var Jacket = function () {
        };

        return Jacket;
    }
);
[建議] 多模塊定義時,可以使用 @exports 標識各個模塊。
// one module
define(‘html/utils‘,
    /**
     * Utility functions to ease working with DOM elements.
     * @exports html/utils
     */
    function () {
        var exports = {
        };

        return exports;
    }
);

// another module
define(‘tag‘,
    /** @exports tag */
    function () {
        var exports = {
        };

        return exports;
    }
);
[建議] 對於 exports 為 Object 的模塊,可以使用@namespace標識。

解釋:

使用 @namespace 而不是 @module 或 @exports 時,對模塊的引用可以省略 module: 前綴。

[建議] 對於 exports 為類名的模塊,使用 @class 和 @exports 標識。
// 只使用 @class Bar 時,類方法和屬性都必須增加 @name Bar#methodName 來標識,與 @exports 配合可以免除這一麻煩,並且在引用時可以省去 module: 前綴。
// 另外需要註意類名需要使用 var 定義的方式。

/**
 * Bar description
 *
 * @see foo
 * @exports  Bar
 * @class
 */
var Bar = function () {
    // TODO
};

/**
 * baz description
 *
 * @return {(string|Array)} return description
 */
Bar.prototype.baz = function () {
    // TODO
};

細節註釋

對於內部實現、不容易理解的邏輯說明、摘要信息等,我們可能需要編寫細節註釋。

[建議] 細節註釋遵循單行註釋的格式。說明必須換行時,每行是一個單行註釋的起始。

function foo(p1, p2, opt_p3) {
    // 這裏對具體內部邏輯進行說明
    // 說明太長需要換行
    for (...) {
        ....
    }
}
[強制] 有時我們會使用一些特殊標記進行說明。特殊標記必須使用單行註釋的形式。下面列舉了一些常用標記:
  • TODO: 有功能待實現。此時需要對將要實現的功能進行簡單說明。
  • FIXME: 該處代碼運行沒問題,但可能由於時間趕或者其他原因,需要修正。此時需要對如何修正進行簡單說明。
  • HACK: 為修正某些問題而寫的不太好或者使用了某些詭異手段的代碼。此時需要對思路或詭異手段進行描述。
  • XXX: 該處存在陷阱。此時需要對陷阱進行描述。

JavaScript編碼規範(1)