js開發(fā)規(guī)范

JavaScript編碼規(guī)范

1 前言

[2 代碼風(fēng)格]

[2.1 文件]

[2.2 結(jié)構(gòu)]

[2.2.1 縮進(jìn)]

2.2.2 空格

2.2.3 換行

2.2.4 語句

2.3 命名

2.4 注釋

2.4.1 單行注釋

2.4.2 多行注釋

2.4.3 文檔化注釋

2.4.4 類型定義

2.4.5 文件注釋

2.4.6 命名空間注釋

2.4.7 類注釋

2.4.8 函數(shù)/方法注釋

2.4.9 事件注釋

2.4.10 常量注釋

2.4.11 復(fù)雜類型注釋

2.4.12 AMD 模塊注釋

2.4.13 細(xì)節(jié)注釋

3 語言特性

3.1 變量

3.2 條件

3.3 循環(huán)

3.4 類型

3.4.1 類型檢測

3.4.2 類型轉(zhuǎn)換

3.5 字符串

3.6 對象

3.7 數(shù)組

3.8 函數(shù)

3.8.1 函數(shù)長度

3.8.2 參數(shù)設(shè)計

3.8.3 閉包

3.8.4 空函數(shù)

3.9 面向?qū)ο?/a>

3.10 動態(tài)特性

3.10.1 eval

3.10.2 動態(tài)執(zhí)行代碼

3.10.3 with

3.10.4 delete

3.10.5 對象屬性

4 瀏覽器環(huán)境

4.1 模塊化

4.1.1 AMD

4.1.2 define

4.1.3 require

4.2 DOM

4.2.1 元素獲取

4.2.2 樣式獲取

4.2.3 樣式設(shè)置

4.2.4 DOM 操作

4.2.5 DOM 事件

1 前言

JavaScript 在百度一直有著廣泛的應(yīng)用缰趋,特別是在瀏覽器端的行為管理。本文檔的目標(biāo)是使 JavaScript 代碼風(fēng)格保持一致缚态,容易被理解和被維護(hù)井氢。

雖然本文檔是針對 JavaScript 設(shè)計的,但是在使用各種 JavaScript 的預(yù)編譯語言時(如 TypeScript 等)時,適用的部分也應(yīng)盡量遵循本文檔的約定祖灰。

2 代碼風(fēng)格

2.1 文件

[建議] JavaScript 文件使用無 BOMUTF-8 編碼诀豁。

解釋:

UTF-8 編碼具有更廣泛的適應(yīng)性。BOM 在使用程序或工具處理文件時可能造成不必要的干擾阱驾。

[建議] 在文件結(jié)尾處就谜,保留一個空行。

2.2 結(jié)構(gòu)

2.2.1 縮進(jìn)

[強(qiáng)制] 使用 4 個空格做為一個縮進(jìn)層級里覆,不允許使用 2 個空格 或 tab 字符丧荐。
[強(qiáng)制] switch 下的 casedefault 必須增加一個縮進(jìn)層級。

示例:

// good
switch (variable) {

    case '1':
        // do...
        break;

    case '2':
        // do...
        break;

    default:
        // do...

}

// bad
switch (variable) {

case '1':
    // do...
    break;

case '2':
    // do...
    break;

default:
    // do...

}

2.2.2 空格

[強(qiáng)制] 二元運(yùn)算符兩側(cè)必須有一個空格喧枷,一元運(yùn)算符與操作對象之間不允許有空格虹统。

示例:

var a = !arr.length;
a++;
a = b + c;
[強(qiáng)制] 用作代碼塊起始的左花括號 { 前必須有一個空格弓坞。

示例:

// good
if (condition) {
}

while (condition) {
}

function funcName() {
}

// bad
if (condition){
}

while (condition){
}

function funcName(){
}
[強(qiáng)制] if / else / for / while / function / switch / do / try / catch / finally 關(guān)鍵字后,必須有一個空格窟却。

示例:

// good
if (condition) {
}

while (condition) {
}

(function () {
})();

// bad
if(condition) {
}

while(condition) {
}

(function() {
})();
[強(qiáng)制] 在對象創(chuàng)建時昼丑,屬性中的 : 之后必須有空格,: 之前不允許有空格夸赫。

示例:

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

// bad
var obj = {
    a : 1,
    b:2,
    c :3
};
[強(qiáng)制] 函數(shù)聲明菩帝、具名函數(shù)表達(dá)式、函數(shù)調(diào)用中茬腿,函數(shù)名和 ( 之間不允許有空格呼奢。

示例:

// good
function funcName() {
}

var funcName = function funcName() {
};

funcName();

// bad
function funcName () {
}

var funcName = function funcName () {
};

funcName ();
[強(qiáng)制] ,; 前不允許有空格。如果不位于行尾切平,,; 后必須跟一個空格握础。

示例:

// good
callFunc(a, b);

// bad
callFunc(a , b) ;
[強(qiáng)制] 在函數(shù)調(diào)用、函數(shù)聲明悴品、括號表達(dá)式禀综、屬性訪問、if / for / while / switch / catch 等語句中苔严,()[] 內(nèi)緊貼括號部分不允許有空格定枷。

示例:

// 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-- ) {
}
[強(qiáng)制] 單行聲明的數(shù)組與對象,如果包含元素届氢,{}[] 內(nèi)緊貼括號部分不允許包含空格欠窒。

解釋:

聲明包含元素的數(shù)組與對象,只有當(dāng)內(nèi)部元素的形式較為簡單時退子,才允許寫在一行岖妄。元素復(fù)雜的情況,還是應(yīng)該換行書寫寂祥。

示例:

// 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};
[強(qiáng)制] 行尾不得有多余的空格荐虐。

2.2.3 換行

[強(qiáng)制] 每個獨立語句結(jié)束后必須換行。
[強(qiáng)制] 每行不得超過 120 個字符丸凭。

解釋:

超長的不可分割的代碼允許例外福扬,比如復(fù)雜的正則表達(dá)式。長字符串不在例外之列贮乳。

[強(qiáng)制] 運(yùn)算符處換行時,運(yùn)算符必須在新行的行首恬惯。

示例:

// 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;
[強(qiáng)制] 在函數(shù)聲明向拆、函數(shù)表達(dá)式、函數(shù)調(diào)用酪耳、對象創(chuàng)建浓恳、數(shù)組創(chuàng)建刹缝、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的最優(yōu)實現(xiàn)
function setStyle(element, property, value) {
    if (element == null) {
        return;
    }

    element.style[property] = value;
}
[建議] 在語句的行長度超過 120 時,根據(jù)邏輯條件合理縮進(jìn)死姚。

示例:

// 較復(fù)雜的邏輯條件組合人乓,將每個條件獨立一行,邏輯運(yùn)算符放置在行首進(jìn)行分隔都毒,或?qū)⒉糠诌壿嫲催壿嫿M合進(jìn)行分隔色罚。
// 建議最終將右括號 ) 與左大括號 { 放在獨立一行,保證與 `if` 內(nèi)語句塊能容易視覺辨識账劲。
if (user.isAuthenticated()
    && user.isInRole('admin')
    && user.hasAuthority('add-admin')
    || user.hasAuthority('delete-admin')
) {
    // Code
}

// 按一定長度截斷字符串戳护,并使用 + 運(yùn)算符進(jìn)行連接。
// 分隔字符串盡量按語義進(jìn)行瀑焦,如不要在一個完整的名詞中間斷開腌且。
// 特別的,對于 HTML 片段的拼接蝠猬,通過縮進(jìn)切蟋,保持和 HTML 相同的結(jié)構(gòu)。
var html = '' // 此處用一個空字符串榆芦,以便整個 HTML 片段都在新行嚴(yán)格對齊
    + '<article>'
    +     '<h1>Title here</h1>'
    +     '<p>This is a paragraph</p>'
    +     '<footer>Complete</footer>'
    + '</article>';

// 也可使用數(shù)組來進(jìn)行拼接柄粹,相對 `+` 更容易調(diào)整縮進(jìn)。
var html = [
    '<article>',
        '<h1>Title here</h1>',
        '<p>This is a paragraph</p>',
        '<footer>Complete</footer>',
    '</article>'
];
html = html.join('');

// 當(dāng)參數(shù)過多時匆绣,將每個參數(shù)獨立寫在一行上驻右,并將結(jié)束的右括號 ) 獨立一行。
// 所有參數(shù)必須增加一個縮進(jìn)崎淳。
foo(
    aVeryVeryLongArgument,
    anotherVeryLongArgument,
    callback
);

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

// 當(dāng)函數(shù)調(diào)用時拣凹,如果有一個或以上參數(shù)跨越多行森爽,應(yīng)當(dāng)每一個參數(shù)獨立一行。
// 這通常出現(xiàn)在匿名函數(shù)或者對象初始化等作為參數(shù)時嚣镜,如 `setTimeout` 函數(shù)等爬迟。
setTimeout(
    function () {
        alert('hello');
    },
    200
);

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

// 鏈?zhǔn)秸{(diào)用較長時采用縮進(jìn)進(jìn)行調(diào)整。
$('#items')
    .find('.selected')
    .highlight()
    .end();

// 三元運(yùn)算符由3部分組成菊匿,因此其換行應(yīng)當(dāng)根據(jù)每個部分的長度不同付呕,形成不同的情況计福。
var result = thisIsAVeryVeryLongCondition
    ? resultA : resultB;

var result = condition
    ? thisIsAVeryVeryLongResult
    : resultB;

// 數(shù)組和對象初始化的混用,嚴(yán)格按照每個對象的 `{` 和結(jié)束 `}` 在獨立一行的風(fēng)格書寫徽职。
var array = [
    {
        // ...
    },
    {
        // ...
    }
];
[建議] 對于 if...else...象颖、try...catch...finally 等語句,推薦使用在 } 號后添加一個換行 的風(fēng)格姆钉,使代碼層次結(jié)構(gòu)更清晰说订,閱讀性更好。

示例:

if (condition) {
    // some statements;
}
else {
    // some statements;
}

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

2.2.4 語句

[強(qiáng)制] 不得省略語句結(jié)束的分號育韩。
[強(qiáng)制] 在 if / else / for / do / while 語句中克蚂,即使只有一行,也不得省略塊 {...}筋讨。

示例:

// good
if (condition) {
    callFunc();
}

// bad
if (condition) callFunc();
if (condition)
    callFunc();
[強(qiáng)制] 函數(shù)定義結(jié)束不允許添加分號埃叭。

示例:

// good
function funcName() {
}

// bad
function funcName() {
};

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

解釋:

IIFE = Immediately-Invoked Function Expression.

額外的 ( 能夠讓代碼在閱讀的一開始就能判斷函數(shù)是否立即被調(diào)用壁袄,進(jìn)而明白接下來代碼的用途类早。而不是一直拖到底部才恍然大悟。

示例:

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

var func = function () {
};


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

var func = (function () {
});

2.3 命名

[強(qiáng)制] 變量 使用 Camel命名法嗜逻。

示例:

var loadingModules = {};
[強(qiáng)制] 常量 使用 全部字母大寫涩僻,單詞間下劃線分隔 的命名方式。

示例:

var HTML_ENTITY = {};
[強(qiáng)制] 函數(shù) 使用 Camel命名法栈顷。

示例:

function stringFormat(source) {
}
[強(qiáng)制] 函數(shù)的 參數(shù) 使用 Camel命名法逆日。

示例:

function hear(theBells) {
}
[強(qiáng)制] 使用 Pascal命名法

示例:

function TextNode(options) {
}
[強(qiáng)制] 類的 方法 / 屬性 使用 Camel命名法萄凤。

示例:

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

TextNode.prototype.clone = function () {
    return this;
};
[強(qiáng)制] 枚舉變量 使用 Pascal命名法室抽,枚舉的屬性 使用 全部字母大寫,單詞間下劃線分隔 的命名方式靡努。

示例:

var TargetState = {
    READING: 1,
    READED: 2,
    APPLIED: 3,
    READY: 4
};
[強(qiáng)制] 命名空間 使用 Camel命名法坪圾。

示例:

equipments.heavyWeapons = {};
[強(qiáng)制] 由多個單詞組成的縮寫詞,在命名中惑朦,根據(jù)當(dāng)前命名法和出現(xiàn)的位置兽泄,所有字母的大小寫與首字母的大小寫保持一致。

示例:

function XMLParser() {
}

function insertHTML(element, html) {
}

var httpRequest = new HTTPRequest();
[強(qiáng)制] 類名 使用 名詞漾月。

示例:

function Engine(options) {
}
[建議] 函數(shù)名 使用 動賓短語病梢。

示例:

function getStyle(element) {
}
[建議] boolean 類型的變量使用 ishas 開頭。

示例:

var isReady = false;
var hasMoreCommands = false;
[建議] Promise對象動賓短語的進(jìn)行時 表達(dá)栅屏。

示例:

var loadingData = ajax.get('url');
loadingData.then(callback);

2.4 注釋

2.4.1 單行注釋

[強(qiáng)制] 必須獨占一行飘千。// 后跟一個空格,縮進(jìn)與下一行被注釋說明的代碼一致栈雳。

2.4.2 多行注釋

[建議] 避免使用 /*...*/ 這樣的多行注釋护奈。有多行注釋內(nèi)容時,使用多個單行注釋哥纫。

2.4.3 文檔化注釋

[強(qiáng)制] 為了便于代碼閱讀和自文檔化霉旗,以下內(nèi)容必須包含以 /**...*/ 形式的塊注釋中。

解釋:

  1. 文件
  2. namespace
  3. 函數(shù)或方法
  4. 類屬性
  5. 事件
  6. 全局變量
  7. 常量
  8. AMD 模塊
[強(qiáng)制] 文檔注釋前必須空一行蛀骇。
[建議] 自文檔化的文檔說明 what厌秒,而不是 how。

2.4.4 類型定義

[強(qiáng)制] 類型定義都是以 { 開始, 以 } 結(jié)束擅憔。

解釋:

常用類型如:{string}, {number}, {boolean}, {Object}, {Function}, {RegExp}, {Array}, {Date}鸵闪。

類型不僅局限于內(nèi)置的類型,也可以是自定義的類型暑诸。比如定義了一個類 Developer蚌讼,就可以使用它來定義一個參數(shù)和返回值的類型。

[強(qiáng)制] 對于基本類型 {string}, {number}, {boolean}个榕,首字母必須小寫篡石。
類型定義 語法示例 解釋
String {string} --
Number {number} --
Boolean {boolean} --
Object {Object} --
Function {Function} --
RegExp {RegExp} --
Array {Array} --
Date {Date} --
單一類型集合 {Array.<string>} string 類型的數(shù)組
多類型 {(number|boolean)} 可能是 number 類型, 也可能是 boolean 類型
允許為null {?number} 可能是 number, 也可能是 null
不允許為null {!Object} Object 類型, 但不是 null
Function類型 {function(number, boolean)} 函數(shù), 形參類型
Function帶返回值 {function(number, boolean):string} 函數(shù), 形參, 返回值類型
Promise Promise.<resolveType, rejectType> Promise,成功返回的數(shù)據(jù)類型西采,失敗返回的錯誤類型
參數(shù)可選 @param {string=} name 可選參數(shù), =為類型后綴
可變參數(shù) @param {...number} args 變長參數(shù), ...為類型前綴
任意類型 {*} 任意類型
可選任意類型 @param {*=} name 可選參數(shù)凰萨,類型不限
可變?nèi)我忸愋?/td> @param {...*} args 變長參數(shù),類型不限

2.4.5 文件注釋

[強(qiáng)制] 文件頂部必須包含文件注釋械馆,用 @file 標(biāo)識文件說明胖眷。

示例:

/**
 * @file Describe the file
 */
[建議] 文件注釋中可以用 @author 標(biāo)識開發(fā)者信息。

解釋:

開發(fā)者信息能夠體現(xiàn)開發(fā)人員對文件的貢獻(xiàn)狱杰,并且能夠讓遇到問題或希望了解相關(guān)信息的人找到維護(hù)人瘦材。通常情況文件在被創(chuàng)建時標(biāo)識的是創(chuàng)建者。隨著項目的進(jìn)展仿畸,越來越多的人加入食棕,參與這個文件的開發(fā),新的作者應(yīng)該被加入 @author 標(biāo)識错沽。

@author 標(biāo)識具有多人時簿晓,原則是按照 責(zé)任 進(jìn)行排序。通常的說就是如果有問題千埃,就是找第一個人應(yīng)該比找第二個人有效憔儿。比如文件的創(chuàng)建者由于各種原因,模塊移交給了其他人或其他團(tuán)隊放可,后來因為新增需求谒臼,其他人在新增代碼時朝刊,添加 @author 標(biāo)識應(yīng)該把自己的名字添加在創(chuàng)建人的前面。

@author 中的名字不允許被刪除蜈缤。任何勞動成果都應(yīng)該被尊重拾氓。

業(yè)務(wù)項目中,一個文件可能被多人頻繁修改底哥,并且每個人的維護(hù)時間都可能不會很長咙鞍,不建議為文件增加 @author 標(biāo)識。通過版本控制系統(tǒng)追蹤變更,按業(yè)務(wù)邏輯單元確定模塊的維護(hù)責(zé)任人,通過文檔與wiki跟蹤和查詢躯枢,是更好的責(zé)任管理方式。

對于業(yè)務(wù)邏輯無關(guān)的技術(shù)型基礎(chǔ)項目疲酌,特別是開源的公共項目,應(yīng)使用 @author 標(biāo)識了袁。

示例:

/**
 * @file Describe the file
 * @author author-name(mail-name@domain.com)
 *         author-name2(mail-name2@domain.com)
 */

2.4.6 命名空間注釋

[建議] 命名空間使用 @namespace 標(biāo)識徐勃。

示例:

/**
 * @namespace
 */
var util = {};

2.4.7 類注釋

[建議] 使用 @class 標(biāo)記類或構(gòu)造函數(shù)。

解釋:

對于使用對象 constructor 屬性來定義的構(gòu)造函數(shù)早像,可以使用 @constructor 來標(biāo)記僻肖。

示例:

/**
 * 描述
 *
 * @class
 */
function Developer() {
    // constructor body
}
[建議] 使用 @extends 標(biāo)記類的繼承信息。

示例:

/**
 * 描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
    Developer.call(this);
    // constructor body
}
util.inherits(Fronteer, Developer);
[強(qiáng)制] 使用包裝方式擴(kuò)展類成員時卢鹦, 必須通過 @lends 進(jìn)行重新指向臀脏。

解釋:

沒有 @lends 標(biāo)記將無法為該類生成包含擴(kuò)展類成員的文檔。

示例:

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

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

解釋:

生成的文檔中將有可訪問性的標(biāo)記,避免用戶直接使用非 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 () {
};

2.4.8 函數(shù)/方法注釋

[強(qiáng)制] 函數(shù)/方法注釋必須包含函數(shù)說明搀玖,有參數(shù)和返回值時必須使用注釋標(biāo)識。

解釋:

當(dāng) return 關(guān)鍵字僅作退出函數(shù)/方法使用時驻呐,無須對返回值作注釋標(biāo)識灌诅。

[強(qiáng)制] 參數(shù)和返回值注釋必須包含類型信息,且不允許省略參數(shù)的說明含末。
[建議] 當(dāng)函數(shù)是內(nèi)部函數(shù)猜拾,外部不可訪問時,可以使用 @inner 標(biāo)識佣盒。

示例:

/**
 * 函數(shù)描述
 *
 * @param {string} p1 參數(shù)1的說明
 * @param {string} p2 參數(shù)2的說明挎袜,比較長
 *     那就換行了.
 * @param {number=} p3 參數(shù)3的說明(可選)
 * @return {Object} 返回值描述
 */
function foo(p1, p2, p3) {
    var p3 = p3 || 10;
    return {
        p1: p1,
        p2: p2,
        p3: p3
    };
}
[強(qiáng)制] 對 Object 中各項的描述, 必須使用 @param 標(biāo)識。

示例:

/**
 * 函數(shù)描述
 *
 * @param {Object} option 參數(shù)描述
 * @param {string} option.url option項描述
 * @param {string=} option.method option項描述盯仪,可選參數(shù)
 */
function foo(option) {
    // TODO
}
[建議] 重寫父類方法時紊搪, 應(yīng)當(dāng)添加 @override 標(biāo)識。如果重寫的形參個數(shù)全景、類型嗦明、順序和返回值類型均未發(fā)生變化,可省略 @param蚪燕、@return,僅用 @override 標(biāo)識奔浅,否則仍應(yīng)作完整注釋馆纳。

解釋:

簡而言之,當(dāng)子類重寫的方法能直接套用父類的方法注釋時可省略對參數(shù)與返回值的注釋汹桦。

2.4.9 事件注釋

[強(qiáng)制] 必須使用 @event 標(biāo)識事件鲁驶,事件參數(shù)的標(biāo)識與方法描述的參數(shù)標(biāo)識相同。

示例:

/**
 * 值變更時觸發(fā)
 *
 * @event Select#change
 * @param {Object} e e描述
 * @param {string} e.before before描述
 * @param {string} e.after after描述
 */
this.fire(
    'change',
    {
        before: 'foo',
        after: 'bar'
    }
);
[強(qiáng)制] 在會廣播事件的函數(shù)前使用 @fires 標(biāo)識廣播的事件舞骆,在廣播事件代碼前使用 @event 標(biāo)識事件钥弯。
[建議] 對于事件對象的注釋,使用 @param 標(biāo)識督禽,生成文檔時可讀性更好脆霎。

示例:

/**
 * 點擊處理
 *
 * @fires Select#change
 * @private
 */
Select.prototype.clickHandler = function () {

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

2.4.10 常量注釋

[強(qiáng)制] 常量必須使用 @const 標(biāo)記,并包含說明和類型信息狈惫。

示例:

/**
 * 常量說明
 *
 * @const
 * @type {string}
 */
var REQUEST_URL = 'myurl.do';

2.4.11 復(fù)雜類型注釋

[建議] 對于類型未定義的復(fù)雜結(jié)構(gòu)的注釋睛蛛,可以使用 @typedef 標(biāo)識來定義。

示例:

// `namespaceA~` 可以換成其它 namepaths 前綴胧谈,目的是為了生成文檔中能顯示 `@typedef` 定義的類型和鏈接忆肾。
/**
 * 服務(wù)器
 *
 * @typedef {Object} namespaceA~Server
 * @property {string} host 主機(jī)
 * @property {number} port 端口
 */

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

2.4.12 AMD 模塊注釋

[強(qiáng)制] AMD 模塊使用 @module@exports 標(biāo)識。

解釋:

@exports 與 @module 都可以用來標(biāo)識模塊菱肖,區(qū)別在于 @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 標(biāo)識:

define(
    function (require) {

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


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

        return exports;
    }
);

如果直接使用 factory 的 exports 參數(shù)稳强,還可以:

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

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

解釋:

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

示例:

/**
 * 點擊處理
 *
 * @fires module:Select#change
 * @private
 */
Select.prototype.clickHandler = function () {
    /**
     * 值變更時觸發(fā)
     *
     * @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 標(biāo)識構(gòu)建函數(shù)蹄咖。

示例:

/**
 * A module representing a jacket.
 * @module jacket
 */
define(
    function () {

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

        return Jacket;
    }
);
[建議] 多模塊定義時褐健,可以使用 @exports 標(biāo)識各個模塊。

示例:

// 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標(biāo)識蚜迅。

解釋:

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

[建議] 對于 exports 為類名的模塊谁不,使用 @class@exports 標(biāo)識坐梯。

示例:


// 只使用 @class Bar 時,類方法和屬性都必須增加 @name Bar#methodName 來標(biāo)識刹帕,與 @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
};

2.4.13 細(xì)節(jié)注釋

對于內(nèi)部實現(xiàn)蹋辅、不容易理解的邏輯說明、摘要信息等挫掏,我們可能需要編寫細(xì)節(jié)注釋侦另。

[建議] 細(xì)節(jié)注釋遵循單行注釋的格式。說明必須換行時尉共,每行是一個單行注釋的起始褒傅。

示例:

function foo(p1, p2, opt_p3) {
    // 這里對具體內(nèi)部邏輯進(jìn)行說明
    // 說明太長需要換行
    for (...) {
        ....
    }
}
[強(qiáng)制] 有時我們會使用一些特殊標(biāo)記進(jìn)行說明。特殊標(biāo)記必須使用單行注釋的形式袄友。下面列舉了一些常用標(biāo)記:

解釋:

  1. TODO: 有功能待實現(xiàn)殿托。此時需要對將要實現(xiàn)的功能進(jìn)行簡單說明。
  2. FIXME: 該處代碼運(yùn)行沒問題剧蚣,但可能由于時間趕或者其他原因碌尔,需要修正。此時需要對如何修正進(jìn)行簡單說明券敌。
  3. HACK: 為修正某些問題而寫的不太好或者使用了某些詭異手段的代碼唾戚。此時需要對思路或詭異手段進(jìn)行描述。
  4. XXX: 該處存在陷阱待诅。此時需要對陷阱進(jìn)行描述叹坦。

3 語言特性

3.1 變量

[強(qiáng)制] 變量、函數(shù)在使用前必須先定義卑雁。

解釋:

不通過 var 定義變量將導(dǎo)致變量污染全局環(huán)境募书。

示例:

// good
var name = 'MyName';

// bad
name = 'MyName';

原則上不建議使用全局變量,對于已有的全局變量或第三方框架引入的全局變量测蹲,需要根據(jù)檢查工具的語法標(biāo)識莹捡。

示例:

/* globals jQuery */
var element = jQuery('#element-id');
[強(qiáng)制] 每個 var 只能聲明一個變量。

解釋:

一個 var 聲明多個變量扣甲,容易導(dǎo)致較長的行長度篮赢,并且在修改時容易造成逗號和分號的混淆齿椅。

示例:

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

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

解釋:

變量聲明與使用的距離越遠(yuǎn)涣脚,出現(xiàn)的跨度越大,代碼的閱讀與維護(hù)成本越高寥茫。雖然JavaScript的變量是函數(shù)作用域遣蚀,還是應(yīng)該根據(jù)編程中的意圖,縮小變量出現(xiàn)的距離空間纱耻。

示例:

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

3.2 條件

[強(qiáng)制] 在 Equality Expression 中使用類型嚴(yán)格的 ===芭梯。僅當(dāng)判斷 nullundefined 時,允許使用 == null弄喘。

解釋:

使用 === 可以避免等于判斷中隱式的類型轉(zhuǎn)換玖喘。

示例:

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

// bad
if (age == 30) {
    // ......
}
[建議] 盡可能使用簡潔的表達(dá)式。

示例:

// 字符串為空

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

// bad
if (name === '') {
    // ......
}
// 字符串非空

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

// bad
if (name !== '') {
    // ......
}
// 數(shù)組非空

// 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') {
  // ......
}
[建議] 按執(zhí)行頻率排列分支的順序限次。

解釋:

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

  1. 閱讀的人容易找到最常見的情況,增加可讀性柴灯。
  2. 提高執(zhí)行效率卖漫。
[建議] 對于相同變量或表達(dá)式的多值條件,用 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') {
    // ......
}
[建議] 如果函數(shù)或全局中的 else 塊后沒有任何語句羊始,可以刪除 else

示例:

// good
function getName() {
    if (name) {
        return name;
    }

    return 'unnamed';
}

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

3.3 循環(huán)

[建議] 不要在循環(huán)體中包含函數(shù)表達(dá)式查描,事先將函數(shù)提取到循環(huán)體外突委。

解釋:

循環(huán)體中的函數(shù)表達(dá)式,運(yùn)行過程中會生成循環(huán)次數(shù)個函數(shù)對象冬三。

示例:

// 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 () {});
}
[建議] 對循環(huán)內(nèi)多次使用的不變值匀油,在循環(huán)外用變量緩存。

示例:

// 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';
    // ......
}
[建議] 對有序集合進(jìn)行遍歷時勾笆,緩存 length敌蚜。

解釋:

雖然現(xiàn)代瀏覽器都對數(shù)組長度進(jìn)行了緩存,但對于一些宿主對象和老舊瀏覽器的數(shù)組對象窝爪,在每次 length 訪問時會動態(tài)計算元素個數(shù)弛车,此時緩存 length 能有效提高程序性能。

示例:

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

解釋:

逆序遍歷可以節(jié)省變量,代碼比較優(yōu)化邀杏。

示例:

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

3.4 類型

3.4.1 類型檢測

[建議] 類型檢測優(yōu)先使用 typeof贫奠。對象類型檢測使用 instanceofnullundefined 的檢測使用 == 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'

3.4.2 類型轉(zhuǎn)換

[建議] 轉(zhuǎn)換成 string 時叮阅,使用 + ''刁品。

示例:

// good
num + '';

// bad
new String(num);
num.toString();
String(num);
[建議] 轉(zhuǎn)換成 number 時,通常使用 +浩姥。

示例:

// good
+str;

// bad
Number(str);
[建議] string 轉(zhuǎn)換成 number挑随,要轉(zhuǎn)換的字符串結(jié)尾包含非數(shù)字并期望忽略時,使用 parseInt勒叠。

示例:

var width = '200px';
parseInt(width, 10);
[強(qiáng)制] 使用 parseInt 時兜挨,必須指定進(jìn)制。

示例:

// good
parseInt(str, 10);

// bad
parseInt(str);
[建議] 轉(zhuǎn)換成 boolean 時眯分,使用 !!拌汇。

示例:

var num = 3.14;
!!num;
[建議] number 去除小數(shù)點,使用 Math.floor / Math.round / Math.ceil弊决,不使用 parseInt噪舀。

示例:

// good
var num = 3.14;
Math.ceil(num);

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

3.5 字符串

[強(qiáng)制] 字符串開頭和結(jié)束使用單引號 '

解釋:

  1. 輸入單引號不需要按住 shift飘诗,方便輸入与倡。
  2. 實際使用中,字符串經(jīng)常用來拼接 HTML昆稿。為方便 HTML 中包含雙引號而不需要轉(zhuǎn)義寫法纺座。

示例:

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

解釋:

  1. 使用 + 拼接字符串溉潭,如果拼接的全部是 StringLiteral净响,壓縮工具可以對其進(jìn)行自動合并的優(yōu)化。所以喳瓣,靜態(tài)字符串建議使用 + 拼接馋贤。
  2. 在現(xiàn)代瀏覽器下,使用 + 拼接字符串畏陕,性能較數(shù)組的方式要高掸掸。
  3. 如需要兼顧老舊瀏覽器,應(yīng)盡量使用數(shù)組拼接字符串蹭秋。

示例:

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

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

解釋:

JavaScript 中拼接仁讨,并且最終將輸出到頁面中的字符串羽莺,需要進(jìn)行合理轉(zhuǎn)義,以防止安全漏洞洞豁。下面的示例代碼為場景說明盐固,不能直接運(yùn)行荒给。

示例:

// HTML 轉(zhuǎn)義
var str = '<p>' + htmlEncode(content) + '</p>';

// HTML 轉(zhuǎn)義
var str = '<input type="text" value="' + htmlEncode(value) + '">';

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

// JavaScript字符串 轉(zhuǎn)義 + HTML 轉(zhuǎn)義
var str = '<button onclick="check(\'' + htmlEncode(strLiteral(name)) + '\')">提交</button>';
[建議] 復(fù)雜的數(shù)據(jù)到視圖字符串的轉(zhuǎn)換過程,選用一種模板引擎刁卜。

解釋:

使用模板引擎有如下好處:

  1. 在開發(fā)過程中專注于數(shù)據(jù)志电,將視圖生成的過程由另外一個層級維護(hù),使程序邏輯結(jié)構(gòu)更清晰蛔趴。
  2. 優(yōu)秀的模板引擎挑辆,通過模板編譯技術(shù)和高質(zhì)量的編譯產(chǎn)物,能獲得比手工拼接字符串更高的性能孝情。
  3. 模板引擎能方便的對動態(tài)數(shù)據(jù)進(jìn)行相應(yīng)的轉(zhuǎn)義鱼蝉,部分模板引擎默認(rèn)進(jìn)行HTML轉(zhuǎn)義,安全性更好箫荡。
  • artTemplate: 體積較小魁亦,在所有環(huán)境下性能高,語法靈活羔挡。
  • dot.js: 體積小洁奈,在現(xiàn)代瀏覽器下性能高,語法靈活绞灼。
  • etpl: 體積較小利术,在所有環(huán)境下性能高,模板復(fù)用性高镀赌,語法靈活氯哮。
  • handlebars: 體積大际跪,在所有環(huán)境下性能高商佛,擴(kuò)展性高。
  • hogon: 體積小姆打,在現(xiàn)代瀏覽器下性能高良姆。
  • nunjucks: 體積較大,性能一般幔戏,模板復(fù)用性高玛追。

3.6 對象

[強(qiáng)制] 使用對象字面量 {} 創(chuàng)建新 Object

示例:

// good
var obj = {};

// bad
var obj = new Object();
[建議] 對象創(chuàng)建時闲延,如果一個對象的所有 屬性 均可以不添加引號痊剖,建議所有 屬性 不添加引號。

示例:

var info = {
    name: 'someone',
    age: 28
};
[建議] 對象創(chuàng)建時垒玲,如果任何一個 屬性 需要添加引號陆馁,則所有 屬性 建議添加 '

解釋:

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

示例:

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

// bad
var info = {
    name: 'someone',
    age: 28,
    'more-info': '...'
};
[強(qiáng)制] 不允許修改和擴(kuò)展任何原生對象和宿主對象的原型击狮。

示例:

// 以下行為絕對禁止
String.prototype.trim = function () {
};
[建議] 屬性訪問時,盡量使用 .益老。

解釋:

屬性名符合 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];
    }
}

3.7 數(shù)組

[強(qiáng)制] 使用數(shù)組字面量 [] 創(chuàng)建新數(shù)組,除非想要創(chuàng)建的是指定長度的數(shù)組驮配。

示例:

// good
var arr = [];

// bad
var arr = new Array();
[強(qiáng)制] 遍歷數(shù)組不使用 for in娘扩。

解釋:

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

示例:

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

// 這里僅作演示, 實際中應(yīng)使用 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);
}
[建議] 不因為性能的原因自己實現(xiàn)數(shù)組排序功能壮锻,盡量使用數(shù)組的 sort 方法琐旁。

解釋:

自己實現(xiàn)的常規(guī)排序算法,在性能上并不優(yōu)于數(shù)組默認(rèn)的 sort 方法猜绣。以下兩種場景可以自己實現(xiàn)排序:

  1. 需要穩(wěn)定的排序算法灰殴,達(dá)到嚴(yán)格一致的排序結(jié)果。
  2. 數(shù)據(jù)特點鮮明掰邢,適合使用桶排牺陶。
[建議] 清空數(shù)組使用 .length = 0

3.8 函數(shù)

3.8.1 函數(shù)長度

[建議] 一個函數(shù)的長度控制在 50 行以內(nèi)辣之。

解釋:

將過多的邏輯單元混在一個大函數(shù)中掰伸,易導(dǎo)致難以維護(hù)。一個清晰易懂的函數(shù)應(yīng)該完成單一的邏輯單元怀估。復(fù)雜的操作應(yīng)進(jìn)一步抽取狮鸭,通過函數(shù)的調(diào)用來體現(xiàn)流程。

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

示例:

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

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

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

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

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

function checkAAvailability() {
    if (a.value) {
        clearWarnignForA();
    }
    else {
        displayWarningForAMissing();
    }
}

3.8.2 參數(shù)設(shè)計

[建議] 一個函數(shù)的參數(shù)控制在 6 個以內(nèi)。

解釋:

除去不定長參數(shù)以外康铭,函數(shù)具備不同邏輯意義的參數(shù)建議控制在 6 個以內(nèi)惯退,過多參數(shù)會導(dǎo)致維護(hù)難度增大。

某些情況下麻削,如使用 AMD Loader 的 require 加載多個模塊時蒸痹,其 callback 可能會存在較多參數(shù)春弥,因此對函數(shù)參數(shù)的個數(shù)不做強(qiáng)制限制。

[建議] 通過 options 參數(shù)傳遞非數(shù)據(jù)輸入型參數(shù)叠荠。

解釋:

有些函數(shù)的參數(shù)并不是作為算法的輸入匿沛,而是對算法的某些分支條件判斷之用,此類參數(shù)建議通過一個 options 參數(shù)傳遞榛鼎。

如下函數(shù):

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

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

可以轉(zhuǎn)換為下面的簽名:

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

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

這種模式有幾個顯著的優(yōu)勢:

  • boolean 型的配置項具備名稱逃呼,從調(diào)用的代碼上更易理解其表達(dá)的邏輯意義。
  • 當(dāng)配置項有增長時者娱,無需無休止地增加參數(shù)個數(shù)抡笼,不會出現(xiàn) removeElement(element, true, false, false, 3) 這樣難以理解的調(diào)用代碼。
  • 當(dāng)部分配置參數(shù)可選時黄鳍,多個參數(shù)的形式非常難處理重載邏輯推姻,而使用一個 options 對象只需判斷屬性是否存在,實現(xiàn)得以簡化框沟。

3.8.3 閉包

[建議] 在適當(dāng)?shù)臅r候?qū)㈤]包內(nèi)大對象置為 null藏古。

解釋:

在 JavaScript 中,無需特別的關(guān)鍵詞就可以使用閉包忍燥,一個函數(shù)可以任意訪問在其定義的作用域外的變量拧晕。需要注意的是,函數(shù)的作用域是靜態(tài)的梅垄,即在定義時決定厂捞,與調(diào)用的時機(jī)和方式?jīng)]有任何關(guān)系。

閉包會阻止一些變量的垃圾回收队丝,對于較老舊的 JavaScript 引擎靡馁,可能導(dǎo)致外部所有變量均無法回收。

首先一個較為明確的結(jié)論是炭玫,以下內(nèi)容會影響到閉包內(nèi)變量的回收:

  • 嵌套的函數(shù)中是否有使用該變量奈嘿。
  • 嵌套的函數(shù)中是否有 直接調(diào)用eval貌虾。
  • 是否使用了 with 表達(dá)式吞加。

Chakra、V8 和 SpiderMonkey 將受以上因素的影響尽狠,表現(xiàn)出不盡相同又較為相似的回收策略衔憨,而 JScript.dll 和 Carakan 則完全沒有這方面的優(yōu)化,會完整保留整個 LexicalEnvironment 中的所有變量綁定袄膏,造成一定的內(nèi)存消耗践图。

由于對閉包內(nèi)變量有回收優(yōu)化策略的 Chakra、V8 和 SpiderMonkey 引擎的行為較為相似沉馆,因此可以總結(jié)如下码党,當(dāng)返回一個函數(shù) fn 時:

  1. 如果 fn[[Scope]] 是 ObjectEnvironment(with 表達(dá)式生成 ObjectEnvironment德崭,函數(shù)和 catch 表達(dá)式生成 DeclarativeEnvironment),則:
    1. 如果是 V8 引擎揖盘,則退出全過程眉厨。
    2. 如果是 SpiderMonkey,則處理該 ObjectEnvironment 的外層 LexicalEnvironment兽狭。
  2. 獲取當(dāng)前 LexicalEnvironment 下的所有類型為 Function 的對象憾股,對于每一個 Function 對象,分析其 FunctionBody:
    1. 如果 FunctionBody 中含有 直接調(diào)用 eval箕慧,則退出全過程服球。
    2. 否則得到所有的 Identifier。
    3. 對于每一個 Identifier颠焦,設(shè)其為 name斩熊,根據(jù)查找變量引用的規(guī)則,從 LexicalEnvironment 中找出名稱為 name 的綁定 binding伐庭。
    4. 對 binding 添加 notSwap 屬性座享,其值為 true
  3. 檢查當(dāng)前 LexicalEnvironment 中的每一個變量綁定似忧,如果該綁定有 notSwap 屬性且值為 true渣叛,則:
    1. 如果是 V8 引擎,刪除該綁定盯捌。
    2. 如果是 SpiderMonkey淳衙,將該綁定的值設(shè)為 undefined,將刪除 notSwap 屬性饺著。

對于 Chakra 引擎箫攀,暫無法得知是按 V8 的模式還是按 SpiderMonkey 的模式進(jìn)行。

如果有 非常龐大 的對象幼衰,且預(yù)計會在 老舊的引擎 中執(zhí)行靴跛,則使用閉包時,注意將閉包不需要的對象置為空引用渡嚣。

[建議] 使用 IIFE 避免 Lift 效應(yīng)梢睛。

解釋:

在引用函數(shù)外部變量時,函數(shù)執(zhí)行時外部變量的值由運(yùn)行時決定而非定義時识椰,最典型的場景如下:

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 中的函數(shù)的執(zhí)行均會輸出 Current cursor is at 5绝葡,往往不符合預(yù)期。

此現(xiàn)象稱為 Lift 效應(yīng) 腹鹉。解決的方式是通過額外加上一層閉包函數(shù)藏畅,將需要的外部變量作為參數(shù)傳遞來解除變量的綁定關(guān)系:

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]();
}

3.8.4 空函數(shù)

[建議] 空函數(shù)不使用 new Function() 的形式。

示例:

var emptyFunction = function () {};
[建議] 對于性能有高要求的場合功咒,建議存在一個空函數(shù)的常量愉阎,供多處使用共享绞蹦。

示例:

var EMPTY_FUNCTION = function () {};

function MyClass() {
}

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

3.9 面向?qū)ο?/h3>

[強(qiáng)制] 類的繼承方案,實現(xiàn)時需要修正 constructor榜旦。

解釋:

通常使用其他 library 的類繼承方案都會進(jìn)行 constructor 修正坦辟。如果是自己實現(xiàn)的類繼承方案,需要進(jìn)行 constructor 修正章办。

示例:

/**
 * 構(gòu)建類之間的繼承關(guān)系
 *
 * @param {Function} subClass 子類函數(shù)
 * @param {Function} superClass 父類函數(shù)
 */
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');
    }
};

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

解釋:

原型對象的成員被所有實例共享休偶,能節(jié)約內(nèi)存占用梁厉。所以編碼時我們應(yīng)該遵守這樣的原則:原型對象包含程序不會修改的成員,如方法函數(shù)或配置項踏兜。

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

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

解釋:

在 JavaScript 廣泛應(yīng)用的瀏覽器環(huán)境已添,絕大多數(shù) DOM 事件名稱都是全小寫的昭灵。為了遵循大多數(shù) JavaScript 開發(fā)者的習(xí)慣拓轻,在設(shè)計自定義事件時郎任,事件名也應(yīng)該全小寫。

[強(qiáng)制] 自定義事件只能有一個 event 參數(shù)辫继。如果事件需要傳遞較多信息腊状,應(yīng)仔細(xì)設(shè)計事件對象酱吝。

解釋:

一個事件對象的好處有:

  1. 順序無關(guān)纳本,避免事件監(jiān)聽者需要記憶參數(shù)順序窍蓝。
  2. 每個事件信息都可以根據(jù)需要提供或者不提供,更自由繁成。
  3. 擴(kuò)展方便吓笙,未來添加事件信息時,無需考慮會破壞監(jiān)聽器參數(shù)形式而無法向后兼容巾腕。
[建議] 設(shè)計自定義事件時面睛,應(yīng)考慮禁止默認(rèn)行為。

解釋:

常見禁止默認(rèn)行為的方式有兩種:

  1. 事件監(jiān)聽函數(shù)中 return false祠墅。
  2. 事件對象中包含禁止默認(rèn)行為的方法侮穿,如 preventDefault歌径。

3.10 動態(tài)特性

3.10.1 eval

[強(qiáng)制] 避免使用直接 eval 函數(shù)毁嗦。

解釋:

直接 eval,指的是以函數(shù)方式調(diào)用 eval 的調(diào)用方法回铛。直接 eval 調(diào)用執(zhí)行代碼的作用域為本地作用域狗准,應(yīng)當(dāng)避免克锣。

如果有特殊情況需要使用直接 eval,需在代碼中用詳細(xì)的注釋說明為何必須使用直接 eval腔长,不能使用其它動態(tài)執(zhí)行代碼的方式袭祟,同時需要其他資深工程師進(jìn)行 Code Review。

[建議] 盡量避免使用 eval 函數(shù)捞附。

3.10.2 動態(tài)執(zhí)行代碼

[建議] 使用 new Function 執(zhí)行動態(tài)代碼巾乳。

解釋:

通過 new Function 生成的函數(shù)作用域是全局使用域,不會影響當(dāng)當(dāng)前的本地作用域鸟召。如果有動態(tài)代碼執(zhí)行的需求胆绊,建議使用 new Function

示例:

var handler = new Function('x', 'y', 'return x + y;');
var result = handler($('#x').val(), $('#y').val());

3.10.3 with

[建議] 盡量不要使用 with欧募。

解釋:

使用 with 可能會增加代碼的復(fù)雜度压状,不利于閱讀和管理;也會對性能有影響跟继。大多數(shù)使用 with 的場景都能使用其他方式較好的替代种冬。所以,盡量不要使用 with舔糖。

3.10.4 delete

[建議] 減少 delete 的使用娱两。

解釋:

如果沒有特別的需求,減少或避免使用 delete金吗。delete 的使用會破壞部分 JavaScript 引擎的性能優(yōu)化谷婆。

[建議] 處理 delete 可能產(chǎn)生的異常。

解釋:

對于有被遍歷需求辽聊,且值 null 被認(rèn)為具有業(yè)務(wù)邏輯意義的值的對象纪挎,移除某個屬性必須使用 delete 操作。

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

示例:

try {
    delete o.x;
}
catch (deleteError) {
    o.x = null;
}

3.10.5 對象屬性

[建議] 避免修改外部傳入的對象烤蜕。

解釋:

JavaScript 因其腳本語言的動態(tài)特性,當(dāng)一個對象未被 seal 或 freeze 時迹冤,可以任意添加讽营、刪除、修改屬性值泡徙。

但是隨意地對 非自身控制的對象 進(jìn)行修改橱鹏,很容易造成代碼在不可預(yù)知的情況下出現(xiàn)問題。因此,設(shè)計良好的組件莉兰、函數(shù)應(yīng)該避免對外部傳入的對象的修改挑围。

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

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

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

對于此類場景,需要使用額外的對象來維護(hù)捶朵,使用由自身控制蜘矢,不與外部產(chǎn)生任何交互的 selectedNodeIndex 對象來維護(hù)節(jié)點的選中狀態(tài),不對 datasource 作任何修改综看。

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

Tree.prototype.selectNode = function (id) {

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

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

};

除此之外硼端,也可以通過 deepClone 等手段將自身維護(hù)的對象與外部傳入的分離,保證不會相互影響寓搬。

[建議] 具備強(qiáng)類型的設(shè)計珍昨。

解釋:

  • 如果一個屬性被設(shè)計為 boolean 類型,則不要使用 10 作為其值句喷。對于標(biāo)識性的屬性镣典,如對代碼體積有嚴(yán)格要求,可以從一開始就設(shè)計為 number 類型且將 0 作為否定值唾琼。
  • 從 DOM 中取出的值通常為 string 類型兄春,如果有對象或函數(shù)的接收類型為 number 類型,提前作好轉(zhuǎn)換锡溯,而不是期望對象赶舆、函數(shù)可以處理多類型的值。

4 瀏覽器環(huán)境

4.1 模塊化

4.1.1 AMD

[強(qiáng)制] 使用 AMD 作為模塊定義祭饭。

解釋:

AMD 作為由社區(qū)認(rèn)可的模塊定義形式芜茵,提供多種重載提供靈活的使用方式,并且絕大多數(shù)優(yōu)秀的 Library 都支持 AMD倡蝙,適合作為規(guī)范九串。

目前,比較成熟的 AMD Loader 有:

  • 官方實現(xiàn)的 requirejs
  • 百度自己實現(xiàn)的 esl
[強(qiáng)制] 模塊 id 必須符合標(biāo)準(zhǔn)寺鸥。

解釋:

模塊 id 必須符合以下約束條件:

  1. 類型為 string猪钮,并且是由 / 分割的一系列 terms 來組成。例如:this/is/a/module胆建。
  2. term 應(yīng)該符合 [a-zA-Z0-9_-]+ 規(guī)則烤低。
  3. 不應(yīng)該有 .js 后綴。
  4. 跟文件的路徑保持一致笆载。

4.1.2 define

[建議] 定義模塊時不要指明 iddependencies扑馁。

解釋:

在 AMD 的設(shè)計思想里涯呻,模塊名稱是和所在路徑相關(guān)的,匿名的模塊更利于封包和遷移檐蚜。模塊依賴應(yīng)在模塊定義內(nèi)部通過 local require 引用魄懂。

所以沿侈,推薦使用 define(factory) 的形式進(jìn)行模塊定義闯第。

示例:

define(
    function (require) {
    }
);
[建議] 使用 return 來返回模塊定義。

解釋:

使用 return 可以減少 factory 接收的參數(shù)(不需要接收 exports 和 module)缀拭,在沒有 AMD Loader 的場景下也更容易進(jìn)行簡單的處理來偽造一個 Loader咳短。

示例:

define(
    function (require) {
        var exports = {};

        // ...

        return exports;
    }
);

4.1.3 require

[強(qiáng)制] 全局運(yùn)行環(huán)境中,require 必須以 async require 形式調(diào)用蛛淋。

解釋:

模塊的加載過程是異步的咙好,同步調(diào)用并無法保證得到正確的結(jié)果。

示例:

// good
require(['foo'], function (foo) {
});

// bad
var foo = require('foo');
[強(qiáng)制] 模塊定義中只允許使用 local require褐荷,不允許使用 global require勾效。

解釋:

  1. 在模塊定義中使用 global require,對封裝性是一種破壞叛甫。
  2. 在 AMD 里层宫,global require 是可以被重命名的。并且 Loader 甚至沒有全局的 require 變量其监,而是用 Loader 名稱做為 global require萌腿。模塊定義不應(yīng)該依賴使用的 Loader。
[強(qiáng)制] Package 在實現(xiàn)時抖苦,內(nèi)部模塊的 require 必須使用 relative id毁菱。

解釋:

對于任何可能通過 發(fā)布-引入 的形式復(fù)用的第三方庫、框架锌历、包贮庞,開發(fā)者所定義的名稱不代表使用者使用的名稱。因此不要基于任何名稱的假設(shè)究西。在實現(xiàn)源碼中贸伐,require 自身的其它模塊時使用 relative id

示例:

define(
    function (require) {
        var util = require('./util');
    }
);
[建議] 不會被調(diào)用的依賴模塊怔揩,在 factory 開始處統(tǒng)一 require捉邢。

解釋:

有些模塊是依賴的模塊,但不會在模塊實現(xiàn)中被直接調(diào)用商膊,最為典型的是 css / js / tpl 等 Plugin 所引入的外部內(nèi)容伏伐。此類內(nèi)容建議放在模塊定義最開始處統(tǒng)一引用。

示例:

define(
    function (require) {
        require('css!foo.css');
        require('tpl!bar.tpl.html');

        // ...
    }
);

4.2 DOM

4.2.1 元素獲取

[建議] 對于單個元素晕拆,盡可能使用 document.getElementById 獲取藐翎,避免使用document.all材蹬。
[建議] 對于多個元素的集合,盡可能使用 context.getElementsByTagName 獲取吝镣。其中 context 可以為 document 或其他元素堤器。指定 tagName 參數(shù)為 * 可以獲得所有子元素。
[建議] 遍歷元素集合時末贾,盡量緩存集合長度闸溃。如需多次操作同一集合,則應(yīng)將集合轉(zhuǎn)為數(shù)組拱撵。

解釋:

原生獲取元素集合的結(jié)果并不直接引用 DOM 元素辉川,而是對索引進(jìn)行讀取,所以 DOM 結(jié)構(gòu)的改變會實時反映到結(jié)果中拴测。

示例:

<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>
[建議] 獲取元素的直接子元素時使用 children乓旗。避免使用childNodes,除非預(yù)期是需要包含文本集索、注釋和屬性類型的節(jié)點屿愚。

4.2.2 樣式獲取

[建議] 獲取元素實際樣式信息時,應(yīng)使用 getComputedStylecurrentStyle务荆。

解釋:

通過 style 只能獲得內(nèi)聯(lián)定義或通過 JavaScript 直接設(shè)置的樣式妆距。通過 CSS class 設(shè)置的元素樣式無法直接通過 style 獲取。

4.2.3 樣式設(shè)置

[建議] 盡可能通過為元素添加預(yù)定義的 className 來改變元素樣式蛹含,避免直接操作 style 設(shè)置毅厚。
[強(qiáng)制] 通過 style 對象設(shè)置元素樣式時,對于帶單位非 0 值的屬性浦箱,不允許省略單位吸耿。

解釋:

除了 IE,標(biāo)準(zhǔn)瀏覽器會忽略不規(guī)范的屬性值酷窥,導(dǎo)致兼容性問題咽安。

4.2.4 DOM 操作

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

解釋:

頁面 reflow 是非常耗時的行為妆棒,非常容易導(dǎo)致性能瓶頸。下面一些場景會觸發(fā)瀏覽器的reflow:

  • DOM元素的添加沸伏、修改(內(nèi)容)糕珊、刪除。
  • 應(yīng)用新的樣式或者修改任何影響元素布局的屬性毅糟。
  • Resize瀏覽器窗口红选、滾動頁面。
  • 讀取元素的某些屬性(offsetLeft姆另、offsetTop喇肋、offsetHeight坟乾、offsetWidth、scrollTop/Left/Width/Height蝶防、clientTop/Left/Width/Height甚侣、getComputedStyle()、currentStyle(in IE)) 间学。
[建議] 盡量減少 DOM 操作殷费。

解釋:

DOM 操作也是非常耗時的一種操作,減少 DOM 操作有助于提高性能菱鸥。舉一個簡單的例子宗兼,構(gòu)建一個列表躏鱼。我們可以用兩種方式:

  1. 在循環(huán)體中 createElement 并 append 到父元素中氮采。
  2. 在循環(huán)體中拼接 HTML 字符串,循環(huán)結(jié)束后寫父元素的 innerHTML染苛。

第一種方法看起來比較標(biāo)準(zhǔn)鹊漠,但是每次循環(huán)都會對 DOM 進(jìn)行操作,性能極低茶行。在這里推薦使用第二種方法躯概。

4.2.5 DOM 事件

[建議] 優(yōu)先使用 addEventListener / attachEvent 綁定事件,避免直接在 HTML 屬性中或 DOM 的 expando 屬性綁定事件處理畔师。

解釋:

expando 屬性綁定事件容易導(dǎo)致互相覆蓋娶靡。

[建議] 使用 addEventListener 時第三個參數(shù)使用 false

解釋:

標(biāo)準(zhǔn)瀏覽器中的 addEventListener 可以通過第三個參數(shù)指定兩種時間觸發(fā)模型:冒泡和捕獲看锉。而 IE 的 attachEvent 僅支持冒泡的事件觸發(fā)姿锭。所以為了保持一致性,通常 addEventListener 的第三個參數(shù)都為 false伯铣。

[建議] 在沒有事件自動管理的框架支持下呻此,應(yīng)持有監(jiān)聽器函數(shù)的引用,在適當(dāng)時候(元素釋放腔寡、頁面卸載等)移除添加的監(jiān)聽器焚鲜。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市放前,隨后出現(xiàn)的幾起案子忿磅,更是在濱河造成了極大的恐慌,老刑警劉巖凭语,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葱她,死亡現(xiàn)場離奇詭異,居然都是意外死亡叽粹,警方通過查閱死者的電腦和手機(jī)览效,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門却舀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锤灿,你說我怎么就攤上這事挽拔。” “怎么了但校?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵螃诅,是天一觀的道長。 經(jīng)常有香客問我状囱,道長术裸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任亭枷,我火速辦了婚禮袭艺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叨粘。我一直安慰自己猾编,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布升敲。 她就那樣靜靜地躺著答倡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驴党。 梳的紋絲不亂的頭發(fā)上瘪撇,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音港庄,去河邊找鬼倔既。 笑死,一個胖子當(dāng)著我的面吹牛攘轩,可吹牛的內(nèi)容都是我干的叉存。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼度帮,長吁一口氣:“原來是場噩夢啊……” “哼歼捏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笨篷,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瞳秽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后率翅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體练俐,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年冕臭,在試婚紗的時候發(fā)現(xiàn)自己被綠了腺晾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片燕锥。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖悯蝉,靈堂內(nèi)的尸體忽然破棺而出归形,到底是詐尸還是另有隱情,我是刑警寧澤鼻由,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布暇榴,位于F島的核電站,受9級特大地震影響蕉世,放射性物質(zhì)發(fā)生泄漏蔼紧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一狠轻、第九天 我趴在偏房一處隱蔽的房頂上張望奸例。 院中可真熱鬧,春花似錦哈误、人聲如沸哩至。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卢佣,卻和暖如春重荠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虚茶。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工戈鲁, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘹叫。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓婆殿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親罩扇。 傳聞我的和親對象是個殘疾皇子婆芦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock閱讀 3,372評論 2 36
  • JavaScript編碼規(guī)范 1 前言 2 代碼風(fēng)格 2.1 文件 2.2 結(jié)構(gòu) 2.2.1 縮進(jìn) 2.2.2 空...
    春木橙云閱讀 554評論 0 0
  • JavaScript編碼規(guī)范 1 前言 JavaScript 在百度一直有著廣泛的應(yīng)用,特別是在瀏覽器端的行為管理...
    Top_Chenxi閱讀 503評論 1 2
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理喂饥,服務(wù)發(fā)現(xiàn)消约,斷路器,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 本篇主要介紹JS的命名規(guī)范员帮、注釋規(guī)范以及框架開發(fā)的一些問題或粮。 目錄 [命名規(guī)范]:介紹變量、函數(shù)捞高、常量氯材、構(gòu)造函數(shù)渣锦、...
    謝大見閱讀 277評論 0 1