<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Rising Sun

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      148 隨筆 :: 0 文章 :: 22 評論 :: 0 Trackbacks

    Google JavaScript 編碼規(guī)范指南

    修訂版: 2.9

    Aaron Whyte
    Bob Jervis
    Dan Pupius
    Eric Arvidsson
    Fritz Schneider
    Robby Walker

    每個條目都有概述信息, 點擊  查看詳細(xì)的內(nèi)容. 你也可以點擊下面的按鈕

     展開全部
    目錄

    重要注意事項

    顯示被隱藏的內(nèi)容

    link
    這份指南中, 可以點擊旁邊的按鈕來顯示更多的細(xì)節(jié).

    Hooray! 這里是更多詳細(xì)的內(nèi)容, 你也可以點擊最上面的"顯示/隱藏全部按鈕"來切換顯示更多內(nèi)容.

    背景

    JavaScript 是一種客戶端腳本語言, Google 的許多開源工程中都有用到它. 這份指南列出了編寫 JavaScript 時需要遵守的規(guī)則.

    JavaScript 語言規(guī)范

    變量

    link
    聲明變量必須加上 var 關(guān)鍵字.

    Decision:當(dāng)你沒有寫 var, 變量就會暴露在全局上下文中, 這樣很可能會和現(xiàn)有變量沖突. 另外, 如果沒有加上, 很難明確該變量的作用域是什么, 變量也很可能像在局部作用域中, 很輕易地泄漏到 Document 或者 Window 中, 所以務(wù)必用 var 去聲明變量.

    常量

    link
    常量的形式如: NAMES_LIKE_THIS, 即使用大寫字符, 并用下劃線分隔. 你也可用 @const 標(biāo)記來指明它是一個常量. 但請永遠(yuǎn)不要使用 const 關(guān)鍵詞.

    Decision:

    對于基本類型的常量, 只需轉(zhuǎn)換命名.

    /**  * The number of seconds in a minute.  * @type {number}  */ goog.example.SECONDS_IN_A_MINUTE = 60;

    對于非基本類型, 使用 @const 標(biāo)記.

    /**  * The number of seconds in each of the given units.  * @type {Object.<number>}  * @const  */ goog.example.SECONDS_TABLE = {   minute: 60,   hour: 60 * 60,   day: 60 * 60 * 24 }

    這標(biāo)記告訴編譯器它是常量.

    至于關(guān)鍵詞 const, 因為 IE 不能識別, 所以不要使用.

     

    分號

    link
    總是使用分號.

    如果僅依靠語句間的隱式分隔, 有時會很麻煩. 你自己更能清楚哪里是語句的起止.

    而且有些情況下, 漏掉分號會很危險:

    // 1. MyClass.prototype.myMethod = function() {   return 42; }  // No semicolon here.  (function() {   // Some initialization code wrapped in a function to create a scope for locals. })();   var x = {   'i': 1,   'j': 2 }  // No semicolon here.  // 2.  Trying to do one thing on Internet Explorer and another on Firefox. // I know you'd never write code like this, but throw me a bone. [normalVersion, ffVersion][isIE]();   var THINGS_TO_EAT = [apples, oysters, sprayOnCheese]  // No semicolon here.  // 3. conditional execution a la bash -1 == resultOfOperation() || die();

    這段代碼會發(fā)生些什么詭異事呢?

    1. 報 JavaScript 錯誤 - 例子1上的語句會解釋成, 一個函數(shù)帶一匿名函數(shù)作為參數(shù)而被調(diào)用, 返回42后, 又一次被"調(diào)用", 這就導(dǎo)致了錯誤.
    2. 例子2中, 你很可能會在運行時遇到 'no such property in undefined' 錯誤, 原因是代碼試圖這樣 x[ffVersion][isIE]() 執(zhí)行.
    3. 當(dāng) resultOfOperation() 返回非 NaN 時, 就會調(diào)用die, 其結(jié)果也會賦給 THINGS_TO_EAT.

     

    為什么?

    JavaScript 的語句以分號作為結(jié)束符, 除非可以非常準(zhǔn)確推斷某結(jié)束位置才會省略分號. 上面的幾個例子產(chǎn)出錯誤, 均是在語句中聲明了函數(shù)/對象/數(shù)組直接量, 但 閉括號('}'或']')并不足以表示該語句的結(jié)束. 在 JavaScript 中, 只有當(dāng)語句后的下一個符號是后綴或括號運算符時, 才會認(rèn)為該語句的結(jié)束.

    遺漏分號有時會出現(xiàn)很奇怪的結(jié)果, 所以確保語句以分號結(jié)束.

     

    嵌套函數(shù)

    link
    可以使用

    嵌套函數(shù)很有用, 比如,減少重復(fù)代碼, 隱藏幫助函數(shù), 等. 沒什么其他需要注意的地方, 隨意使用.

    塊內(nèi)函數(shù)聲明

    link
    不要在塊內(nèi)聲明一個函數(shù)

    不要寫成:

    if (x) {   function foo() {} }

    雖然很多 JS 引擎都支持塊內(nèi)聲明函數(shù), 但它不屬于 ECMAScript 規(guī)范 (見 ECMA-262, 第13和14條). 各個瀏覽器糟糕的實現(xiàn)相互不兼容, 有些也與未來 ECMAScript 草案相違背. ECMAScript 只允許在腳本的根語句或函數(shù)中聲明函數(shù). 如果確實需要在塊中定義函數(shù), 建議使用函數(shù)表達(dá)式來初始化變量:

    if (x) {   var foo = function() {} }

    異常

    link
    可以

    你在寫一個比較復(fù)雜的應(yīng)用時, 不可能完全避免不會發(fā)生任何異常. 大膽去用吧.

    自定義異常

    link
    可以

    有時發(fā)生異常了, 但返回的錯誤信息比較奇怪, 也不易讀. 雖然可以將含錯誤信息的引用對象或者可能產(chǎn)生錯誤的完整對象傳遞過來, 但這樣做都不是很好, 最好還是自定義異常類, 其實這些基本上都是最原始的異常處理技巧. 所以在適當(dāng)?shù)臅r候使用自定義異常.

    標(biāo)準(zhǔn)特性

    link
    總是優(yōu)于非標(biāo)準(zhǔn)特性.

    最大化可移植性和兼容性, 盡量使用標(biāo)準(zhǔn)方法而不是用非標(biāo)準(zhǔn)方法, (比如, 優(yōu)先用string.charAt(3) 而不用 string[3] , 通過 DOM 原生函數(shù)訪問元素, 而不是使用應(yīng)用封裝好的快速接口.

    封裝基本類型

    link
    不要

    沒有任何理由去封裝基本類型, 另外還存在一些風(fēng)險:

    var x = new Boolean(false); if (x) {   alert('hi');  // Shows 'hi'. }

    除非明確用于類型轉(zhuǎn)換, 其他情況請千萬不要這樣做!

    var x = Boolean(0); if (x) {   alert('hi');  // This will never be alerted. } typeof Boolean(0) == 'boolean'; typeof new Boolean(0) == 'object';

    有時用作 numberstring 或 boolean時, 類型的轉(zhuǎn)換會非常實用.

    多級原型結(jié)構(gòu)

    link
    不是首選

    多級原型結(jié)構(gòu)是指 JavaScript 中的繼承關(guān)系. 當(dāng)你自定義一個D類, 且把B類作為其原型, 那么這就獲得了一個多級原型結(jié)構(gòu). 這些原型結(jié)構(gòu)會變得越來越復(fù)雜!

    使用 the Closure 庫 中的 goog.inherits() 或其他類似的用于繼承的函數(shù), 會是更好的選擇.

    function D() {   goog.base(this) } goog.inherits(D, B);  D.prototype.method = function() {   ... };

    方法定義

    link
    Foo.prototype.bar = function() { ... };

    有很多方法可以給構(gòu)造器添加方法或成員, 我們更傾向于使用如下的形式:

    Foo.prototype.bar = function() {   /* ... */ };

    閉包

    link
    可以, 但小心使用.

    閉包也許是 JS 中最有用的特性了. 有一份比較好的介紹閉包原理的文檔.

    有一點需要牢記, 閉包保留了一個指向它封閉作用域的指針, 所以, 在給 DOM 元素附加閉包時, 很可能會產(chǎn)生循環(huán)引用, 進(jìn)一步導(dǎo)致內(nèi)存泄漏. 比如下面的代碼:

    function foo(element, a, b) {   element.onclick = function() { /* uses a and b */ }; }

    這里, 即使沒有使用 element, 閉包也保留了 elementa 和 b 的引用, . 由于 element 也保留了對閉包的引用, 這就產(chǎn)生了循環(huán)引用, 這就不能被 GC 回收. 這種情況下, 可將代碼重構(gòu)為:

    function foo(element, a, b) {   element.onclick = bar(a, b); }  function bar(a, b) {   return function() { /* uses a and b */ } }

    eval()

    link
    只用于解析序列化串 (如: 解析 RPC 響應(yīng))

    eval() 會讓程序執(zhí)行的比較混亂, 當(dāng) eval() 里面包含用戶輸入的話就更加危險. 可以用其他更佳的, 更清晰, 更安全的方式寫你的代碼, 所以一般情況下請不要使用 eval(). 當(dāng)碰到一些需要解析序列化串的情況下(如, 計算 RPC 響應(yīng)), 使用 eval 很容易實現(xiàn).

    解析序列化串是指將字節(jié)流轉(zhuǎn)換成內(nèi)存中的數(shù)據(jù)結(jié)構(gòu). 比如, 你可能會將一個對象輸出成文件形式:

    users = [   {     name: 'Eric',     id: 37824,     email: 'jellyvore@myway.com'   },   {     name: 'xtof',     id: 31337,     email: 'b4d455h4x0r@google.com'   },   ... ];

    很簡單地調(diào)用 eval 后, 把表示成文件的數(shù)據(jù)讀取回內(nèi)存中.

    類似的, eval() 對 RPC 響應(yīng)值進(jìn)行解碼. 例如, 你在使用 XMLHttpRequest 發(fā)出一個 RPC 請求后, 通過 eval () 將服務(wù)端的響應(yīng)文本轉(zhuǎn)成 JavaScript 對象:

    var userOnline = false; var user = 'nusrat'; var xmlhttp = new XMLHttpRequest(); xmlhttp.open('GET', 'http://chat.google.com/isUserOnline?user=' + user, false); xmlhttp.send(''); // Server returns: // userOnline = true; if (xmlhttp.status == 200) {   eval(xmlhttp.responseText); } // userOnline is now true.

    with() {}

    link
    不要使用

    使用 with 讓你的代碼在語義上變得不清晰. 因為 with 的對象, 可能會與局部變量產(chǎn)生沖突, 從而改變你程序原本的用義. 下面的代碼是干嘛的?

    with (foo) {   var x = 3;   return x; }

    答案: 任何事. 局部變量 x 可能被 foo 的屬性覆蓋, 當(dāng)它定義一個 setter 時, 在賦值 3 后會執(zhí)行很多其他代碼. 所以不要使用 with 語句.

    this

    link
    僅在對象構(gòu)造器, 方法, 閉包中使用.

    this 的語義很特別. 有時它引用一個全局對象(大多數(shù)情況下), 調(diào)用者的作用域(使用 eval時), DOM 樹中的節(jié)點(添加事件處理函數(shù)時), 新創(chuàng)建的對象(使用一個構(gòu)造器), 或者其他對象(如果函數(shù)被 call() 或 apply()).

    使用時很容易出錯, 所以只有在下面兩個情況時才能使用:

    • 在構(gòu)造器中
    • 對象的方法(包括創(chuàng)建的閉包)中

    for-in 循環(huán)

    link
    只用于 object/map/hash 的遍歷

    對 Array 用 for-in 循環(huán)有時會出錯. 因為它并不是從 0 到 length - 1 進(jìn)行遍歷, 而是所有出現(xiàn)在對象及其原型鏈的鍵值. 下面就是一些失敗的使用案例:

    function printArray(arr) {   for (var key in arr) {     print(arr[key]);   } }  printArray([0,1,2,3]);  // This works.  var a = new Array(10); printArray(a);  // This is wrong.  a = document.getElementsByTagName('*'); printArray(a);  // This is wrong.  a = [0,1,2,3]; a.buhu = 'wine'; printArray(a);  // This is wrong again.  a = new Array; a[3] = 3; printArray(a);  // This is wrong again.

    而遍歷數(shù)組通常用最普通的 for 循環(huán).

    function printArray(arr) {   var l = arr.length;   for (var i = 0; i < l; i++) {     print(arr[i]);   } }

    關(guān)聯(lián)數(shù)組

    link
    永遠(yuǎn)不要使用 Array 作為 map/hash/associative 數(shù)組.

    數(shù)組中不允許使用非整型作為索引值, 所以也就不允許用關(guān)聯(lián)數(shù)組. 而取代它使用 Object 來表示 map/hash 對象. Array 僅僅是擴展自 Object (類似于其他 JS 中的對象, 就像 DateRegExp 和 String)一樣來使用.

    多行字符串

    link
    不要使用

    不要這樣寫長字符串:

    var myString = 'A rather long string of English text, an error message \                 actually that just keeps going and going -- an error \                 message to make the Energizer bunny blush (right through \                 those Schwarzenegger shades)! Where was I? Oh yes, \                 you\'ve got an error and all the extraneous whitespace is \                 just gravy.  Have a nice day.';

    在編譯時, 不能忽略行起始位置的空白字符; "\" 后的空白字符會產(chǎn)生奇怪的錯誤; 雖然大多數(shù)腳本引擎支持這種寫法, 但它不是 ECMAScript 的標(biāo)準(zhǔn)規(guī)范.

    Array 和 Object 直接量

    link
    使用

    使用 Array 和 Object 語法, 而不使用 Array 和 Object 構(gòu)造器.

    使用 Array 構(gòu)造器很容易因為傳參不恰當(dāng)導(dǎo)致錯誤.

    // Length is 3. var a1 = new Array(x1, x2, x3);  // Length is 2. var a2 = new Array(x1, x2);  // If x1 is a number and it is a natural number the length will be x1. // If x1 is a number but not a natural number this will throw an exception. // Otherwise the array will have one element with x1 as its value. var a3 = new Array(x1);  // Length is 0. var a4 = new Array();

    如果傳入一個參數(shù)而不是2個參數(shù), 數(shù)組的長度很有可能就不是你期望的數(shù)值了.

    為了避免這些歧義, 我們應(yīng)該使用更易讀的直接量來聲明.

    var a = [x1, x2, x3]; var a2 = [x1, x2]; var a3 = [x1]; var a4 = [];

    雖然 Object 構(gòu)造器沒有上述類似的問題, 但鑒于可讀性和一致性考慮, 最好還是在字面上更清晰地指明.

    var o = new Object();  var o2 = new Object(); o2.a = 0; o2.b = 1; o2.c = 2; o2['strange key'] = 3;

    應(yīng)該寫成:

    var o = {};  var o2 = {   a: 0,   b: 1,   c: 2,   'strange key': 3 };

    修改內(nèi)置對象的原型

    link
    不要

    千萬不要修改內(nèi)置對象, 如 Object.prototype 和 Array.prototype 的原型. 而修改內(nèi)置對象, 如 Function.prototype 的原型, 雖然少危險些, 但仍會導(dǎo)致調(diào)試時的詭異現(xiàn)象. 所以也要避免修改其原型.

    IE下的條件注釋

    link
    不要使用

    不要這樣子寫:

    var f = function () {     /*@cc_on if (@_jscript) { return 2* @*/  3; /*@ } @*/ };

    條件注釋妨礙自動化工具的執(zhí)行, 因為在運行時, 它們會改變 JavaScript 語法樹.

    JavaScript 編碼風(fēng)格

    命名

    link

    通常, 使用 functionNamesLikeThisvariableNamesLikeThisClassNamesLikeThisEnumNamesLikeThismethodNamesLikeThis, 和SYMBOLIC_CONSTANTS_LIKE_THIS.

    展開見細(xì)節(jié).

    屬性和方法

    • 文件或類中的 私有 屬性, 變量和方法名應(yīng)該以下劃線 "_" 開頭.
    • 保護(hù) 屬性, 變量和方法名不需要下劃線開頭, 和公共變量名一樣.

    更多有關(guān) 私有 和 保護(hù)的信息見, visibility.

     

    方法和函數(shù)參數(shù)

    可選參數(shù)以 opt_ 開頭.

    函數(shù)的參數(shù)個數(shù)不固定時, 應(yīng)該添加最后一個參數(shù) var_args 為參數(shù)的個數(shù). 你也可以不設(shè)置 var_args而取代使用 arguments.

    可選和可變參數(shù)應(yīng)該在 @param 標(biāo)記中說明清楚. 雖然這兩個規(guī)定對編譯器沒有任何影響, 但還是請盡量遵守

     

    Getters 和 Setters

    Getters 和 setters 并不是必要的. 但只要使用它們了, 就請將 getters 命名成 getFoo() 形式, 將 setters 命名成 setFoo(value) 形式. (對于布爾類型的 getters, 使用 isFoo() 也可.)

     

    命名空間

    JavaScript 不支持包和命名空間.

    不容易發(fā)現(xiàn)和調(diào)試全局命名的沖突, 多個系統(tǒng)集成時還可能因為命名沖突導(dǎo)致很嚴(yán)重的問題. 為了提高 JavaScript 代碼復(fù)用率, 我們遵循下面的約定以避免沖突.

    為全局代碼使用命名空間

    在全局作用域上, 使用一個唯一的, 與工程/庫相關(guān)的名字作為前綴標(biāo)識. 比如, 你的工程是 "Project Sloth", 那么命名空間前綴可取為 sloth.*.

    var sloth = {};  sloth.sleep = function() {   ... };

    許多 JavaScript 庫, 包括 the Closure Library and Dojo toolkit 為你提供了聲明你自己的命名空間的函數(shù). 比如:

    goog.provide('sloth');  sloth.sleep = function() {   ... };

     

    明確命名空間所有權(quán)

    當(dāng)選擇了一個子命名空間, 請確保父命名空間的負(fù)責(zé)人知道你在用哪個子命名空間, 比如說, 你為工程 'sloths' 創(chuàng)建一個 'hats' 子命名空間, 那確保 Sloth 團隊人員知道你在使用 sloth.hats.

     

    外部代碼和內(nèi)部代碼使用不同的命名空間

    "外部代碼" 是指來自于你代碼體系的外部, 可以獨立編譯. 內(nèi)外部命名應(yīng)該嚴(yán)格保持獨立. 如果你使用了外部庫, 他的所有對象都在 foo.hats.* 下, 那么你自己的代碼不能在 foo.hats.*下命名, 因為很有可能其他團隊也在其中命名.

    foo.require('foo.hats');  /**  * WRONG -- Do NOT do this.  * @constructor  * @extend {foo.hats.RoundHat}  */ foo.hats.BowlerHat = function() { };

    如果你需要在外部命名空間中定義新的 API, 那么你應(yīng)該直接導(dǎo)出一份外部庫, 然后在這份代碼中修改. 在你的內(nèi)部代碼中, 應(yīng)該通過他們的內(nèi)部名字來調(diào)用內(nèi)部 API , 這樣保持一致性可讓編譯器更好的優(yōu)化你的代碼.

    foo.provide('googleyhats.BowlerHat');  foo.require('foo.hats');  /**  * @constructor  * @extend {foo.hats.RoundHat}  */ googleyhats.BowlerHat = function() {   ... };  goog.exportSymbol('foo.hats.BowlerHat', googleyhats.BowlerHat);

     

    重命名那些名字很長的變量, 提高可讀性

    主要是為了提高可讀性. 局部空間中的變量別名只需要取原名字的最后部分.

    /**  * @constructor  */ some.long.namespace.MyClass = function() { };  /**  * @param {some.long.namespace.MyClass} a  */ some.long.namespace.MyClass.staticHelper = function(a) {   ... };  myapp.main = function() {   var MyClass = some.long.namespace.MyClass;   var staticHelper = some.long.namespace.MyClass.staticHelper;   staticHelper(new MyClass()); };

    不要對命名空間創(chuàng)建別名.

    myapp.main = function() {   var namespace = some.long.namespace;   namespace.MyClass.staticHelper(new namespace.MyClass()); };

    除非是枚舉類型, 不然不要訪問別名變量的屬性.

    /** @enum {string} */ some.long.namespace.Fruit = {   APPLE: 'a',   BANANA: 'b' };  myapp.main = function() {   var Fruit = some.long.namespace.Fruit;   switch (fruit) {     case Fruit.APPLE:       ...     case Fruit.BANANA:       ...   } };
    myapp.main = function() {   var MyClass = some.long.namespace.MyClass;   MyClass.staticHelper(null); };

    不要在全局范圍內(nèi)創(chuàng)建別名, 而僅在函數(shù)塊作用域中使用.

     

     

    文件名

    文件名應(yīng)該使用小寫字符, 以避免在有些系統(tǒng)平臺上不識別大小寫的命名方式. 文件名以.js結(jié)尾, 不要包含除 - 和 _ 外的標(biāo)點符號(使用 - 優(yōu)于 _).

     

    自定義 toString() 方法

    link
    應(yīng)該總是成功調(diào)用且不要拋異常.

    可自定義 toString() 方法, 但確保你的實現(xiàn)方法滿足: (1) 總是成功 (2) 沒有其他負(fù)面影響. 如果不滿足這兩個條件, 那么可能會導(dǎo)致嚴(yán)重的問題, 比如, 如果 toString() 調(diào)用了包含 assert 的函數(shù), assert 輸出導(dǎo)致失敗的對象, 這在 toString() 也會被調(diào)用.

    延遲初始化

    link
    可以

    沒必要在每次聲明變量時就將其初始化.

    明確作用域

    link
    任何時候都需要

    任何時候都要明確作用域 - 提高可移植性和清晰度. 例如, 不要依賴于作用域鏈中的 window 對象. 可能在其他應(yīng)用中, 你函數(shù)中的 window 不是指之前的那個窗口對象.

    代碼格式化

    link
    展開見詳細(xì)描述.

    主要依照C++ 格式規(guī)范 ( 中文版 ), 針對 JavaScript, 還有下面一些附加說明.

    大括號

    分號會被隱式插入到代碼中, 所以你務(wù)必在同一行上插入大括號. 例如:

    if (something) {   // ... } else {   // ... }

     

    數(shù)組和對象的初始化

    如果初始值不是很長, 就保持寫在單行上:

    var arr = [1, 2, 3];  // No space after [ or before ]. var obj = {a: 1, b: 2, c: 3};  // No space after { or before }.

    初始值占用多行時, 縮進(jìn)2個空格.

    // Object initializer. var inset = {   top: 10,   right: 20,   bottom: 15,   left: 12 };  // Array initializer. this.rows_ = [   '"Slartibartfast" <fjordmaster@magrathea.com>',   '"Zaphod Beeblebrox" <theprez@universe.gov>',   '"Ford Prefect" <ford@theguide.com>',   '"Arthur Dent" <has.no.tea@gmail.com>',   '"Marvin the Paranoid Android" <marv@googlemail.com>',   'the.mice@magrathea.com' ];  // Used in a method call. goog.dom.createDom(goog.dom.TagName.DIV, {   id: 'foo',   className: 'some-css-class',   style: 'display:none' }, 'Hello, world!');

    比較長的標(biāo)識符或者數(shù)值, 不要為了讓代碼好看些而手工對齊. 如:

    CORRECT_Object.prototype = {   a: 0,   b: 1,   lengthyName: 2 };

    不要這樣做:

    WRONG_Object.prototype = {   a          : 0,   b          : 1,   lengthyName: 2 };

     

    函數(shù)參數(shù)

    盡量讓函數(shù)參數(shù)在同一行上. 如果一行超過 80 字符, 每個參數(shù)獨占一行, 并以4個空格縮進(jìn), 或者與括號對齊, 以提高可讀性. 盡可能不要讓每行超過80個字符. 比如下面這樣:

    // Four-space, wrap at 80.  Works with very long function names, survives // renaming without reindenting, low on space. goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(     veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,     tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {   // ... };  // Four-space, one argument per line.  Works with long function names, // survives renaming, and emphasizes each argument. goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(     veryDescriptiveArgumentNumberOne,     veryDescriptiveArgumentTwo,     tableModelEventHandlerProxy,     artichokeDescriptorAdapterIterator) {   // ... };  // Parenthesis-aligned indentation, wrap at 80.  Visually groups arguments, // low on space. function foo(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,              tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {   // ... }  // Parenthesis-aligned, one argument per line.  Visually groups and // emphasizes each individual argument. function bar(veryDescriptiveArgumentNumberOne,              veryDescriptiveArgumentTwo,              tableModelEventHandlerProxy,              artichokeDescriptorAdapterIterator) {   // ... }

     

    傳遞匿名函數(shù)

    如果參數(shù)中有匿名函數(shù), 函數(shù)體從調(diào)用該函數(shù)的左邊開始縮進(jìn)2個空格, 而不是從 function 這個關(guān)鍵字開始. 這讓匿名函數(shù)更加易讀 (不要增加很多沒必要的縮進(jìn)讓函數(shù)體顯示在屏幕的右側(cè)).

    var names = items.map(function(item) {                         return item.name;                       });  prefix.something.reallyLongFunctionName('whatever', function(a1, a2) {   if (a1.equals(a2)) {     someOtherLongFunctionName(a1);   } else {     andNowForSomethingCompletelyDifferent(a2.parrot);   } });

     

    更多的縮進(jìn)

    事實上, 除了 初始化數(shù)組和對象 , 和傳遞匿名函數(shù)外, 所有被拆開的多行文本要么選擇與之前的表達(dá)式左對齊, 要么以4個(而不是2個)空格作為一縮進(jìn)層次.

    someWonderfulHtml = '' +                     getEvenMoreHtml(someReallyInterestingValues, moreValues,                                     evenMoreParams, 'a duck', true, 72,                                     slightlyMoreMonkeys(0xfff)) +                     '';  thisIsAVeryLongVariableName =     hereIsAnEvenLongerOtherFunctionNameThatWillNotFitOnPrevLine();  thisIsAVeryLongVariableName = 'expressionPartOne' + someMethodThatIsLong() +     thisIsAnEvenLongerOtherFunctionNameThatCannotBeIndentedMore();  someValue = this.foo(     shortArg,     'Some really long string arg - this is a pretty common case, actually.',     shorty2,     this.bar());  if (searchableCollection(allYourStuff).contains(theStuffYouWant) &&     !ambientNotification.isActive() && (client.isAmbientSupported() ||                                         client.alwaysTryAmbientAnyways()) {   ambientNotification.activate(); }

     

    空行

    使用空行來劃分一組邏輯上相關(guān)聯(lián)的代碼片段.

    doSomethingTo(x); doSomethingElseTo(x); andThen(x);  nowDoSomethingWith(y);  andNowWith(z);

     

    二元和三元操作符

    操作符始終跟隨著前行, 這樣就不用顧慮分號的隱式插入問題. 如果一行實在放不下, 還是按照上述的縮進(jìn)風(fēng)格來換行.

    var x = a ? b : c;  // All on one line if it will fit.  // Indentation +4 is OK. var y = a ?     longButSimpleOperandB : longButSimpleOperandC;  // Indenting to the line position of the first operand is also OK. var z = a ?         moreComplicatedB :         moreComplicatedC;

     

    括號

    link
    只在需要的時候使用

    不要濫用括號, 只在必要的時候使用它.

    對于一元操作符(如deletetypeof 和 void ), 或是在某些關(guān)鍵詞(如 returnthrowcasenew )之后, 不要使用括號.

    字符串

    link
    使用 ' 優(yōu)于 "

    單引號 (') 優(yōu)于雙引號 ("). 當(dāng)你創(chuàng)建一個包含 HTML 代碼的字符串時就知道它的好處了.

    var msg = 'This is some HTML';

    可見性 (私有域和保護(hù)域)

    link
    推薦使用 JSDoc 中的兩個標(biāo)記: @private 和 @protected

    JSDoc 的兩個標(biāo)記 @private 和 @protected 用來指明類, 函數(shù), 屬性的可見性域.

    標(biāo)記為 @private 的全局變量和函數(shù), 表示它們只能在當(dāng)前文件中訪問.

    標(biāo)記為 @private 的構(gòu)造器, 表示該類只能在當(dāng)前文件或是其靜態(tài)/普通成員中實例化; 私有構(gòu)造器的公共靜態(tài)屬性在當(dāng)前文件的任何地方都可訪問, 通過instanceof 操作符也可.

    永遠(yuǎn)不要為 全局變量, 函數(shù), 構(gòu)造器加 @protected 標(biāo)記.

    // File 1. // AA_PrivateClass_ and AA_init_ are accessible because they are global // and in the same file.  /**  * @private  * @constructor  */ AA_PrivateClass_ = function() { };  /** @private */ function AA_init_() {   return new AA_PrivateClass_(); }  AA_init_();

    標(biāo)記為 @private 的屬性, 在當(dāng)前文件中可訪問它; 如果是類屬性私有, "擁有"該屬性的類的所有靜態(tài)/普通成員也可訪問, 但它們不能被不同文件中的子類訪問或覆蓋.

    標(biāo)記為 @protected 的屬性, 在當(dāng)前文件中可訪問它, 如果是類屬性保護(hù), 那么"擁有"該屬性的類及其子類中的所有靜態(tài)/普通成員也可訪問.

    注意: 這與 C++, Java 中的私有和保護(hù)不同, 它們是在當(dāng)前文件中, 檢查是否具有訪問私有/保護(hù)屬性的權(quán)限, 有權(quán)限即可訪問, 而不是只能在同一個類或類層次上. 而 C++ 中的私有屬性不能被子類覆蓋. (C++/Java 中的私有/保護(hù)是指作用域上的可訪問性, 在可訪問性上的限制. JS 中是在限制在作用域上. PS: 可見性是與作用域?qū)?yīng))

    // File 1.  /** @constructor */   AA_PublicClass = function() { };  /** @private */ AA_PublicClass.staticPrivateProp_ = 1;  /** @private */ AA_PublicClass.prototype.privateProp_ = 2;  /** @protected */ AA_PublicClass.staticProtectedProp = 31;  /** @protected */ AA_PublicClass.prototype.protectedProp = 4;  // File 2.  /**  * @return {number} The number of ducks we've arranged in a row.  */ AA_PublicClass.prototype.method = function() {   // Legal accesses of these two properties.   return this.privateProp_ + AA_PublicClass.staticPrivateProp_; };  // File 3.  /**  * @constructor  * @extends {AA_PublicClass}  */ AA_SubClass = function() {   // Legal access of a protected static property.   AA_PublicClass.staticProtectedProp = this.method(); }; goog.inherits(AA_SubClass, AA_PublicClass);  /**  * @return {number} The number of ducks we've arranged in a row.  */ AA_SubClass.prototype.method = function() {   // Legal access of a protected instance property.   return this.protectedProp; };

    JavaScript 類型

    link
    強烈建議你去使用編譯器.

    如果使用 JSDoc, 那么盡量具體地, 準(zhǔn)確地根據(jù)它的規(guī)則來書寫類型說明. 目前支持兩種 JS2 和 JS1.x 類型規(guī)范.

    JavaScript 類型語言

    JS2 提議中包含了一種描述 JavaScript 類型的規(guī)范語法, 這里我們在 JSDoc 中采用其來描述函數(shù)參數(shù)和返回值的類型.

    JSDoc 的類型語言, 按照 JS2 規(guī)范, 也進(jìn)行了適當(dāng)改變, 但編譯器仍然支持舊語法.

    名稱語法描述棄用語法
    普通類型{boolean}{Window},{goog.ui.Menu}普通類型的描述方法.
    復(fù)雜類型{Array.<string>}
    字符串?dāng)?shù)組.

     

    {Object.<string, number>} 
    鍵為字符串, 值為整數(shù)的對象類型.
    參數(shù)化類型, 即指定了該類型中包含的一系列"類型參數(shù)". 類似于 Java 中的泛型.
    聯(lián)合類型{(number|boolean)}
    一個整數(shù)或者布爾值.
    表示其值可能是 A 類型, 也可能是 B 類型{(number,boolean)},{number|boolean},{(number||boolean)}
    記錄類型{{myNum: number, myObject}} 
    由現(xiàn)有類型組成的類型.

    表示包含指定成員及類型的值. 這個例子中, myNum為 number 類型, myObject 為任意類型.

    注意大括號為類型語法的一部分. 比如, Array.<{length}>, 表示一具有 length 屬性的 Array 對象.

    可為空類型{?number}
    一個整型數(shù)或者為 NULL
    表示一個值可能是 A 類型或者 null. 默認(rèn), 每個對象都是可為空的. 注意: 函數(shù)類型不可為空.{number?}
    非空類型{!Object}
    一個對象, 但絕不會是 null 值.
    說明一個值是類型 A 且肯定不是 null. 默認(rèn)情況下, 所有值類型 (boolean, number, string, 和 undefined) 不可為空.{Object!}
    函數(shù)類型{function(string, boolean)}
    具有兩個參數(shù) ( string 和 boolean) 的函數(shù)類型, 返回值未知.
    說明一個函數(shù).
    函數(shù)返回類型{function(): number}
    函數(shù)返回一個整數(shù).
    說明函數(shù)的返回類型.
    函數(shù)的 this類型{function(this:goog.ui.Menu, string)}
    函數(shù)只帶一個參數(shù) (string), 并且在上下文 goog.ui.Menu 中執(zhí)行.
    說明函數(shù)類型的上下文類型.
    可變參數(shù){function(string, ...[number]): number}
    帶一個參數(shù) (字符類型) 的函數(shù)類型, 并且函數(shù)的參數(shù)個數(shù)可變, 但參數(shù)類型必須為 number.
    說明函數(shù)的可變長參數(shù).
    可變長的參數(shù) (使用@param 標(biāo)記)@param {...number} var_args
    函數(shù)參數(shù)個數(shù)可變.
    使用標(biāo)記, 說明函數(shù)具有不定長參數(shù).
    函數(shù)的 缺省參數(shù){function(?string=, number=)}
    函數(shù)帶一個可空且可選的字符串型參數(shù), 一個可選整型參數(shù). = 語法只針對function 類型有效.
    說明函數(shù)的可選參數(shù).
    函數(shù) 可選參數(shù) (使用@param 標(biāo)記)@param {number=} opt_argument
    number類型的可選參數(shù).
    使用標(biāo)記, 說明函數(shù)具有可選參數(shù).
    所有類型{*}表示變量可以是任何類型.

     

    JavaScript中的類型

     

    類型示例值示例描述
    number
    1 1.0 -5 1e5 Math.PI
    Number
    new Number(true)
    Number 對象
    string
    'Hello' "World" String(42)
    字符串值
    String
    new String('Hello') new String(42)
    字符串對象
    boolean
    true false Boolean(0)
    布爾值
    Boolean
    new Boolean(true)
    布爾對象
    RegExp
    new RegExp('hello') /world/g
    Date
    new Date new Date()
    null
    null
    undefined
    undefined
    void
    function f() {   return; }
    沒有返回值
    Array
    ['foo', 0.3, null] []
    類型不明確的數(shù)組
    Array.<number>
    [11, 22, 33]
    整型數(shù)組
    Array.<Array.<string>>
    [['one', 'two', 'three'], ['foo', 'bar']]
    字符串?dāng)?shù)組的數(shù)組
    Object
    {} {foo: 'abc', bar: 123, baz: null}
    Object.<string>
    {'foo': 'bar'}
    值為字符串的對象.
    Object.<number, string>
    var obj = {}; obj[1] = 'bar';
    鍵為整數(shù), 值為字符串的對象.

     

    注意, JavaScript 中, 鍵總是被轉(zhuǎn)換成字符串, 所以obj['1'] == obj[1]. 也所以, 鍵在 for...in 循環(huán)中是字符串類型. 但在編譯器中會明確根據(jù)鍵的類型來查找對象.
    Function
    function(x, y) {   return x * y; }
    函數(shù)對象
    function(number, number): number
    function(x, y) {   return x * y; }
    函數(shù)值
    SomeClass
    /** @constructor */ function SomeClass() {}  new SomeClass();
    SomeInterface
    /** @interface */ function SomeInterface() {}  SomeInterface.prototype.draw = function() {};
    project.MyClass
    /** @constructor */ project.MyClass = function () {}  new project.MyClass()
    project.MyEnum
    /** @enum {string} */ project.MyEnum = {   BLUE: '#0000dd',   RED: '#dd0000' };
    枚舉
    Element
    document.createElement('div')
    DOM 中的元素
    Node
    document.body.firstChild
    DOM 中的節(jié)點.
    HTMLInputElement
    htmlDocument.getElementsByTagName('input')[0]
    DOM 中, 特定類型的元素.

     

    可空 vs. 可選 參數(shù)和屬性

    JavaScript 是一種弱類型語言, 明白可選, 非空和未定義參數(shù)或?qū)傩灾g的細(xì)微差別還是很重要的.

    對象類型(引用類型)默認(rèn)非空. 注意: 函數(shù)類型默認(rèn)不能為空. 除了字符串, 整型, 布爾, undefined 和 null 外, 對象可以是任何類型.

    /**  * Some class, initialized with a value.  * @param {Object} value Some value.  * @constructor  */ function MyClass(value) {   /**    * Some value.    * @type {Object}    * @private    */   this.myValue_ = value; }

    告訴編譯器 myValue_ 屬性為一對象或 null. 如果 myValue_ 永遠(yuǎn)都不會為 null, 就應(yīng)該如下聲明:

    /**  * Some class, initialized with a non-null value.  * @param {!Object} value Some value.  * @constructor  */ function MyClass(value) {   /**    * Some value.    * @type {!Object}    * @private    */   this.myValue_ = value; }

    這樣, 當(dāng)編譯器在代碼中碰到 MyClass 為 null 時, 就會給出警告.

    函數(shù)的可選參數(shù)可能在運行時沒有定義, 所以如果他們又被賦給類屬性, 需要聲明成:

    /**  * Some class, initialized with an optional value.  * @param {Object=} opt_value Some value (optional).  * @constructor  */ function MyClass(opt_value) {   /**    * Some value.    * @type {Object|undefined}    * @private    */   this.myValue_ = opt_value; }

    這告訴編譯器 myValue_ 可能是一個對象, 或 null, 或 undefined.

    注意: 可選參數(shù) opt_value 被聲明成 {Object=}, 而不是 {Object|undefined}. 這是因為可選參數(shù)可能是 undefined. 雖然直接寫 undefined 也并無害處, 但鑒于可閱讀性還是寫成上述的樣子.

    最后, 屬性的非空和可選并不矛盾, 屬性既可是非空, 也可是可選的. 下面的四種聲明各不相同:

    /**  * Takes four arguments, two of which are nullable, and two of which are  * optional.  * @param {!Object} nonNull Mandatory (must not be undefined), must not be null.  * @param {Object} mayBeNull Mandatory (must not be undefined), may be null.  * @param {!Object=} opt_nonNull Optional (may be undefined), but if present,  *     must not be null!  * @param {Object=} opt_mayBeNull Optional (may be undefined), may be null.  */ function strangeButTrue(nonNull, mayBeNull, opt_nonNull, opt_mayBeNull) {   // ... };

     

    注釋

    link
    使用 JSDoc

    我們使用 JSDoc 中的注釋風(fēng)格. 行內(nèi)注釋使用 // 變量 的形式. 另外, 我們也遵循 C++ 代碼注釋風(fēng)格 . 這也就是說你需要:

    • 版權(quán)和著作權(quán)的信息,
    • 文件注釋中應(yīng)該寫明該文件的基本信息(如, 這段代碼的功能摘要, 如何使用, 與哪些東西相關(guān)), 來告訴那些不熟悉代碼的讀者.
    • 類, 函數(shù), 變量和必要的注釋,
    • 期望在哪些瀏覽器中執(zhí)行,
    • 正確的大小寫, 標(biāo)點和拼寫.

    為了避免出現(xiàn)句子片段, 請以合適的大/小寫單詞開頭, 并以合適的標(biāo)點符號結(jié)束這個句子.

    現(xiàn)在假設(shè)維護(hù)這段代碼的是一位初學(xué)者. 這可能正好是這樣的!

    目前很多編譯器可從 JSDoc 中提取類型信息, 來對代碼進(jìn)行驗證, 刪除和壓縮. 因此, 你很有必要去熟悉正確完整的 JSDoc .

    頂層/文件注釋

    頂層注釋用于告訴不熟悉這段代碼的讀者這個文件中包含哪些東西. 應(yīng)該提供文件的大體內(nèi)容, 它的作者, 依賴關(guān)系和兼容性信息. 如下:

    // Copyright 2009 Google Inc. All Rights Reserved.  /**  * @fileoverview Description of file, its uses and information  * about its dependencies.  * @author user@google.com (Firstname Lastname)  */

     

    類注釋

    每個類的定義都要附帶一份注釋, 描述類的功能和用法. 也需要說明構(gòu)造器參數(shù). 如果該類繼承自其它類, 應(yīng)該使用 @extends 標(biāo)記. 如果該類是對接口的實現(xiàn), 應(yīng)該使用 @implements 標(biāo)記.

    /**  * Class making something fun and easy.  * @param {string} arg1 An argument that makes this more interesting.  * @param {Array.<number>} arg2 List of numbers to be processed.  * @constructor  * @extends {goog.Disposable}  */ project.MyClass = function(arg1, arg2) {   // ... }; goog.inherits(project.MyClass, goog.Disposable);

     

    方法與函數(shù)的注釋

    提供參數(shù)的說明. 使用完整的句子, 并用第三人稱來書寫方法說明.

    /**  * Converts text to some completely different text.  * @param {string} arg1 An argument that makes this more interesting.  * @return {string} Some return value.  */ project.MyClass.prototype.someMethod = function(arg1) {   // ... };  /**  * Operates on an instance of MyClass and returns something.  * @param {project.MyClass} obj Instance of MyClass which leads to a long  *     comment that needs to be wrapped to two lines.  * @return {boolean} Whether something occured.  */ function PR_someMethod(obj) {   // ... }

    對于一些簡單的, 不帶參數(shù)的 getters, 說明可以忽略.

    /**  * @return {Element} The element for the component.  */ goog.ui.Component.prototype.getElement = function() {   return this.element_; };

     

    屬性注釋

    也需要對屬性進(jìn)行注釋.

    /**  * Maximum number of things per pane.  * @type {number}  */ project.MyClass.prototype.someProperty = 4;

     

    類型轉(zhuǎn)換的注釋

    有時, 類型檢查不能很準(zhǔn)確地推斷出表達(dá)式的類型, 所以應(yīng)該給它添加類型標(biāo)記注釋來明確之, 并且必須在表達(dá)式和類型標(biāo)簽外面包裹括號.

    /** @type {number} */ (x) (/** @type {number} */ x)

     

    JSDoc 縮進(jìn)

    如果你在 @param@return@supported@this 或 @deprecated 中斷行, 需要像在代碼中一樣, 使用4個空格作為一個縮進(jìn)層次.

    /**  * Illustrates line wrapping for long param/return descriptions.  * @param {string} foo This is a param with a description too long to fit in  *     one line.  * @return {number} This returns something that has a description too long to  *     fit in one line.  */ project.MyClass.prototype.method = function(foo) {   return 5; };

    不要在 @fileoverview 標(biāo)記中進(jìn)行縮進(jìn).

    雖然不建議, 但也可對說明文字進(jìn)行適當(dāng)?shù)呐虐鎸R. 不過, 這樣帶來一些負(fù)面影響, 就是當(dāng)你每次修改變量名時, 都得重新排版說明文字以保持和變量名對齊.

    /**  * This is NOT the preferred indentation method.  * @param {string} foo This is a param with a description too long to fit in  *                     one line.  * @return {number} This returns something that has a description too long to  *                  fit in one line.  */ project.MyClass.prototype.method = function(foo) {   return 5; };

     

    枚舉

    /**  * Enum for tri-state values.  * @enum {number}  */ project.TriState = {   TRUE: 1,   FALSE: -1,   MAYBE: 0 };

    注意一下, 枚舉也具有有效類型, 所以可以當(dāng)成參數(shù)類型來用.

    /**  * Sets project state.  * @param {project.TriState} state New project state.  */ project.setState = function(state) {   // ... };

     

    Typedefs

    有時類型會很復(fù)雜. 比如下面的函數(shù), 接收 Element 參數(shù):

    /**  * @param {string} tagName  * @param {(string|Element|Text|Array.<Element>|Array.<Text>)} contents  * @return {Element}  */ goog.createElement = function(tagName, contents) {   ... };

    你可以使用 @typedef 標(biāo)記來定義個常用的類型表達(dá)式.

    /** @typedef {(string|Element|Text|Array.<Element>|Array.<Text>)} */ goog.ElementContent;  /** * @param {string} tagName * @param {goog.ElementContent} contents * @return {Element} */ goog.createElement = function(tagName, contents) { ... };

     

    JSDoc 標(biāo)記表
    標(biāo)記模板 & 例子描述類型檢測支持
    @param@param {Type} 變量名 描述

    如:

    /**  * Queries a Baz for items.  * @param {number} groupNum Subgroup id to query.  * @param {string|number|null} term An itemName,  *     or itemId, or null to search everything.  */ goog.Baz.prototype.query = function(groupNum, term) {   // ... };
    給方法, 函數(shù), 構(gòu)造器中的參數(shù)添加說明.完全支持.
    @return@return {Type} 描述

    如:

    /**  * @return {string} The hex ID of the last item.  */ goog.Baz.prototype.getLastId = function() {   // ...   return id; };
    給方法, 函數(shù)的返回值添加說明. 在描述布爾型參數(shù)時, 用 "Whether the component is visible" 這種描述優(yōu)于 "True if the component is visible, false otherwise". 如果函數(shù)沒有返回值, 就不需要添加 @return標(biāo)記.完全支持.
    @author@author username@google.com (first last)

    如:

    /**  * @fileoverview Utilities for handling textareas.  * @author kuth@google.com (Uthur Pendragon)  */
    表明文件的作者, 通常僅會在 @fileoverview 注釋中使用到它.不需要.
    @see@see Link

    如:

    /**  * Adds a single item, recklessly.  * @see #addSafely  * @see goog.Collect  * @see goog.RecklessAdder#add  ...
    給出引用鏈接, 用于進(jìn)一步查看函數(shù)/方法的相關(guān)細(xì)節(jié).不需要.
    @fileoverview@fileoverview 描述

    如:

    /**  * @fileoverview Utilities for doing things that require this very long  * but not indented comment.  * @author kuth@google.com (Uthur Pendragon)  */
    文件通覽.不需要.
    @constructor@constructor

    如:

    /**  * A rectangle.  * @constructor  */ function GM_Rect() {   ... }
    指明類中的構(gòu)造器.會檢查. 如果省略了, 編譯器將禁止實例化.
    @interface@interface

    如:

    /**  * A shape.  * @interface  */ function Shape() {}; Shape.prototype.draw = function() {};  /**  * A polygon.  * @interface  * @extends {Shape}  */ function Polygon() {}; Polygon.prototype.getSides = function() {};
    指明這個函數(shù)是一個接口.會檢查. 如果實例化一個接口, 編譯器會警告.
    @type@type Type
    @type {Type}

    如:

    /**  * The message hex ID.  * @type {string}  */ var hexId = hexId;
    標(biāo)識變量, 屬性或表達(dá)式的類型. 大多數(shù)類型是不需要加大括號的, 但為了保持一致, 建議統(tǒng)一加大括號.會檢查
    @extends@extends Type
    @extends {Type}

    如:

    /**  * Immutable empty node list.  * @constructor  * @extends goog.ds.BasicNodeList  */ goog.ds.EmptyNodeList = function() {   ... };
    與 @constructor 一起使用, 用來表明該類是擴展自其它類的. 類型外的大括號可寫可不寫.會檢查
    @implements@implements Type
    @implements {Type}

    如:

    /**  * A shape.  * @interface  */ function Shape() {}; Shape.prototype.draw = function() {};  /**  * @constructor  * @implements {Shape}  */ function Square() {}; Square.prototype.draw = function() {   ... };
    與 @constructor 一起使用, 用來表明該類實現(xiàn)自一個接口. 類型外的大括號可寫可不寫.會檢查. 如果接口不完整, 編譯器會警告.
    @lends@lends objectName
    @lends {objectName}

    如:

    goog.object.extend(     Button.prototype,     /** @lends {Button.prototype} */ {       isButton: function() { return true; }     });
    表示把對象的鍵看成是其他對象的屬性. 該標(biāo)記只能出現(xiàn)在對象語法中.

     

    注意, 括號中的名稱和其他標(biāo)記中的類型名稱不一樣, 它是一個對象名, 以"借過來"的屬性名命名. 如, @type {Foo} 表示 "Foo 的一個實例", but @lends {Foo} 表示 "Foo 構(gòu)造器".

     

    更多有關(guān)此標(biāo)記的內(nèi)容見 JSDoc Toolkit docs.
    會檢查
    @private@private

    如:

    /**  * Handlers that are listening to this logger.  * @type Array.<Function>  * @private  */ this.handlers_ = [];
    指明那些以下劃線結(jié)尾的方法和屬性是 私有的. 不推薦使用后綴下劃線, 而應(yīng)改用 @private.需要指定標(biāo)志來開啟.
    @protected@protected

    如:

    /**  * Sets the component's root element to the given element.  Considered  * protected and final.  * @param {Element} element Root element for the component.  * @protected  */ goog.ui.Component.prototype.setElementInternal = function(element) {   // ... };
    指明接下來的方法和屬性是 被保護(hù)的. 被保護(hù)的方法和屬性的命名不需要以下劃線結(jié)尾, 和普通變量名沒區(qū)別.需要指定標(biāo)志來開啟.
    @this@this Type
    @this {Type}

    如:

    pinto.chat.RosterWidget.extern('getRosterElement', /**  * Returns the roster widget element.  * @this pinto.chat.RosterWidget  * @return {Element}  */ function() {   return this.getWrappedComponent_().getElement(); });
    指明調(diào)用這個方法時, 需要在哪個上下文中. 當(dāng) this 指向的不是原型方法的函數(shù)時必須使用這個標(biāo)記.會檢查
    @supported@supported 描述

    如:

    /**  * @fileoverview Event Manager  * Provides an abstracted interface to the  * browsers' event systems.  * @supported So far tested in IE6 and FF1.5  */
    在文件概述中用到, 表明支持哪些瀏覽器.不需要.
    @enum@enum {Type}

    如:

    /**  * Enum for tri-state values.  * @enum {number}  */ project.TriState = {   TRUE: 1,   FALSE: -1,   MAYBE: 0 };
    用于枚舉類型.完全支持. 如果省略, 會認(rèn)為是整型.
    @deprecated@deprecated 描述

    如:

    /**  * Determines whether a node is a field.  * @return {boolean} True if the contents of  *     the element are editable, but the element  *     itself is not.  * @deprecated Use isField().  */ BN_EditUtil.isTopEditableField = function(node) {   // ... };
    告訴其他開發(fā)人員, 此方法, 函數(shù)已經(jīng)過時, 不要再使用. 同時也會給出替代方法或函數(shù).不需要
    @override@override

    如:

    /**  * @return {string} Human-readable representation of project.SubClass.  * @override  */ project.SubClass.prototype.toString() {   // ... };
    指明子類的方法和屬性是故意隱藏了父類的方法和屬性. 如果子類的方法和屬性沒有自己的文檔, 就會繼承父類的.會檢查
    @inheritDoc@inheritDoc

    如:

    /** @inheritDoc */ project.SubClass.prototype.toString() {   // ... };
    指明子類的方法和屬性是故意隱藏了父類的方法和屬性, 它們具有相同的文檔. 注意: 使用 @inheritDoc 意味著也同時使用了 @override.會檢查
    @code{@code ...}

    如:

    /**  * Moves to the next position in the selection.  * Throws {@code goog.iter.StopIteration} when it  * passes the end of the range.  * @return {Node} The node at the next position.  */ goog.dom.RangeIterator.prototype.next = function() {   // ... };
    說明這是一段代碼, 讓它能在生成的文檔中正確的格式化.不適用.
    @license or@preserve@license 描述

    如:

    /**  * @preserve Copyright 2009 SomeThirdParty.  * Here is the full license text and copyright  * notice for this file. Note that the notice can span several  * lines and is only terminated by the closing star and slash:  */
    所有被標(biāo)記為 @license 或 @preserve 的, 會被編譯器保留不做任何修改而直接輸出到最終文擋中. 這個標(biāo)記讓一些重要的信息(如法律許可或版權(quán)信息)原樣保留, 同樣, 文本中的換行也會被保留.不需要.
    @noalias@noalias

    如:

    /** @noalias */ function Range() {}
    在外部文件中使用, 告訴編譯器不要為這個變量或函數(shù)重命名.不需要.
    @define@define {Type} 描述

    如:

    /** @define {boolean} */ var TR_FLAGS_ENABLE_DEBUG = true;  /** @define {boolean} */ goog.userAgent.ASSUME_IE = false;
    表示該變量可在編譯時被編譯器重新賦值. 在上面例子中, BUILD 文件中指定了 --define='goog.userAgent.ASSUME_IE=true' 這個編譯之后, 常量 goog.userAgent.ASSUME_IE 將被全部直接替換為 true.不需要.
    @export@export

    如:

    /** @export */ foo.MyPublicClass.prototype.myPublicMethod = function() {   // ... };

    上面的例子代碼, 當(dāng)編譯器運行時指定 --generate_exports 標(biāo)志, 會生成下面的代碼:

    goog.exportSymbol('foo.MyPublicClass.prototype.myPublicMethod',     foo.MyPublicClass.prototype.myPublicMethod);

    編譯后, 將源代碼中的名字原樣導(dǎo)出. 使用 @export 標(biāo)記時, 應(yīng)該

    1. 包含 //javascript/closure/base.js, 或者
    2. 在代碼庫中自定義 goog.exportSymbol 和 goog.exportProperty 兩個方法, 并保證有相同的調(diào)用方式.
    不需要.
    @const@const

    如:

    /** @const */ var MY_BEER = 'stout';  /**  * My namespace's favorite kind of beer.  * @const  * @type {string}  */ mynamespace.MY_BEER = 'stout';  /** @const */ MyClass.MY_BEER = 'stout';

    聲明變量為只讀, 直接寫在一行上. 如果其他代碼中重寫該變量值, 編譯器會警告.

    常量應(yīng)全部用大寫字符, 不過使用這個標(biāo)記, 可以幫你消除命名上依賴. 雖然 jsdoc.org 上列出的 @final 標(biāo)記作用等價于 @const , 但不建議使用. @const 與 JS1.5 中的 const 關(guān)鍵字一致. 注意, 編譯器不禁止修改常量對象的屬性(這與 C++ 中的常量定義不一樣). 如果可以準(zhǔn)確推測出常量類型的話, 那么類型申明可以忽略. 如果指定了類型, 應(yīng)該也寫在同一行上. 變量的額外注釋可寫可不寫.

    支持.
    @nosideeffects@nosideeffects

    如:

    /** @nosideeffects */ function noSideEffectsFn1() {   // ... };  /** @nosideeffects */ var noSideEffectsFn2 = function() {   // ... };  /** @nosideeffects */ a.prototype.noSideEffectsFn3 = function() {   // ... };
    用于對函數(shù)或構(gòu)造器聲明, 說明調(diào)用此函數(shù)不會有副作用. 編譯器遇到此標(biāo)記時, 如果調(diào)用函數(shù)的返回值沒有其他地方使用到, 則會將這個函數(shù)整個刪除.不需要檢查.
    @typedef@typedef

    如:

    /** @typedef {(string|number)} */ goog.NumberLike;  /** @param {goog.NumberLike} x A number or a string. */ goog.readNumber = function(x) {   ... }
    這個標(biāo)記用于給一個復(fù)雜的類型取一個別名.會檢查
    @externs@externs

    如:

    /**  * @fileoverview This is an externs file.  * @externs  */  var document;

    指明一個外部文件.

    不會檢查

    在第三方代碼中, 你還會見到其他一些 JSDoc 標(biāo)記. 這些標(biāo)記在 JSDoc Toolkit Tag Reference 都有介紹到, 但在 Google 的代碼中, 目前不推薦使用. 你可以認(rèn)為這些是將來會用到的 "保留" 名. 它們包含:

    • @augments
    • @argument
    • @borrows
    • @class
    • @constant
    • @constructs
    • @default
    • @event
    • @example
    • @field
    • @function
    • @ignore
    • @inner
    • @link
    • @memberOf
    • @name
    • @namespace
    • @property
    • @public
    • @requires
    • @returns
    • @since
    • @static
    • @version

     

     

    JSDoc 中的 HTML

    類似于 JavaDoc, JSDoc 支持許多 HTML 標(biāo)簽, 如 <code>, <pre>, <tt>, <strong>, <ul>, <ol>, <li>, <a>, 等等.

    這就是說 JSDoc 不會完全依照純文本中書寫的格式. 所以, 不要在 JSDoc 中, 使用空白字符來做格式化:

    /**  * Computes weight based on three factors:  *   items sent  *   items received  *   last timestamp  */

    上面的注釋, 出來的結(jié)果是:

    Computes weight based on three factors: items sent items received items received

    應(yīng)該這樣寫:

    /**  * Computes weight based on three factors:  * <ul>  * <li>items sent  * <li>items received  * <li>last timestamp  * </ul>  */

    另外, 也不要包含任何 HTML 或類 HTML 標(biāo)簽, 除非你就想讓它們解析成 HTML 標(biāo)簽.

    /**  * Changes <b> tags to <span> tags.  */

    出來的結(jié)果是:

    Changes tags to tags.

    另外, 也應(yīng)該在源代碼文件中讓其他人更可讀, 所以不要過于使用 HTML 標(biāo)簽:

    /**  * Changes &lt;b&gt; tags to &lt;span&gt; tags.  */

    上面的代碼中, 其他人就很難知道你想干嘛, 直接改成下面的樣子就清楚多了:

    /** * Changes 'b' tags to 'span' tags. */

     

    編譯

    link
    推薦使用

    建議您去使用 JS 編譯器, 如 Closure Compiler.

    Tips and Tricks

    link
    JavaScript 小技巧

    True 和 False 布爾表達(dá)式

    下面的布爾表達(dá)式都返回 false:

    • null
    • undefined
    • '' 空字符串
    • 0 數(shù)字0

    但小心下面的, 可都返回 true:

    • '0' 字符串0
    • [] 空數(shù)組
    • {} 空對象

    下面段比較糟糕的代碼:

    while (x != null) {

    你可以直接寫成下面的形式(只要你希望 x 不是 0 和空字符串, 和 false):

    while (x) {

    如果你想檢查字符串是否為 null 或空:

    if (y != null && y != '') {

    但這樣會更好:

    if (y) {

    注意: 還有很多需要注意的地方, 如:

    • Boolean('0') == true
      '0' != true
    • 0 != null
      0 == []
      0 == false
    • Boolean(null) == false
      null != true
      null != false
    • Boolean(undefined) == false
      undefined != true
      undefined != false
    • Boolean([]) == true
      [] != true
      [] == false
    • Boolean({}) == true
      {} != true
      {} != false

     

    條件(三元)操作符 (?:)

    三元操作符用于替代下面的代碼:

    if (val != 0) {   return foo(); } else {   return bar(); }

    你可以寫成:

    return val ? foo() : bar();

    在生成 HTML 代碼時也是很有用的:

    var html = '<input type="checkbox"' +     (isChecked ? ' checked' : '') +     (isEnabled ? '' : ' disabled') +     ' name="foo">';

     

    && 和 ||

    二元布爾操作符是可短路的, 只有在必要時才會計算到最后一項.

    "||" 被稱作為 'default' 操作符, 因為可以這樣:

    /** @param {*=} opt_win */ function foo(opt_win) {   var win;   if (opt_win) {     win = opt_win;   } else {     win = window;   }   // ... }

    你可以使用它來簡化上面的代碼:

    /** @param {*=} opt_win */ function foo(opt_win) {   var win = opt_win || window;   // ... }

    "&&" 也可簡短代碼.比如:

    if (node) {   if (node.kids) {     if (node.kids[index]) {       foo(node.kids[index]);     }   } }

    你可以像這樣來使用:

    if (node && node.kids && node.kids[index]) {   foo(node.kids[index]); }

    或者:

    var kid = node && node.kids && node.kids[index]; if (kid) {   foo(kid); }

    不過這樣就有點兒過頭了:

    node && node.kids && node.kids[index] && foo(node.kids[index]);

     

    使用 join() 來創(chuàng)建字符串

    通常是這樣使用的:

    function listHtml(items) {   var html = '<div class="foo">';   for (var i = 0; i < items.length; ++i) {     if (i > 0) {       html += ', ';     }     html += itemHtml(items[i]);   }   html += '</div>';   return html; }

    但這樣在 IE 下非常慢, 可以用下面的方式:

    function listHtml(items) {   var html = [];   for (var i = 0; i < items.length; ++i) {     html[i] = itemHtml(items[i]);   }   return '<div class="foo">' + html.join(', ') + '</div>'; }

    你也可以是用數(shù)組作為字符串構(gòu)造器, 然后通過 myArray.join('') 轉(zhuǎn)換成字符串. 不過由于賦值操作快于數(shù)組的 push(), 所以盡量使用賦值操作.

     

    遍歷 Node List

    Node lists 是通過給節(jié)點迭代器加一個過濾器來實現(xiàn)的. 這表示獲取他的屬性, 如 length 的時間復(fù)雜度為 O(n), 通過 length 來遍歷整個列表需要 O(n^2).

    var paragraphs = document.getElementsByTagName('p'); for (var i = 0; i < paragraphs.length; i++) {   doSomething(paragraphs[i]); }

    這樣做會更好:

    var paragraphs = document.getElementsByTagName('p'); for (var i = 0, paragraph; paragraph = paragraphs[i]; i++) {   doSomething(paragraph); }

    這種方法對所有的 collections 和數(shù)組(只要數(shù)組不包含 falsy 值) 都適用.

    在上面的例子中, 也可以通過 firstChild 和 nextSibling 來遍歷孩子節(jié)點.

    var parentNode = document.getElementById('foo'); for (var child = parentNode.firstChild; child; child = child.nextSibling) {   doSomething(child); }

     

    Parting Words

    保持一致性.

    當(dāng)你在編輯代碼之前, 先花一些時間查看一下現(xiàn)有代碼的風(fēng)格. 如果他們給算術(shù)運算符添加了空格, 你也應(yīng)該添加. 如果他們的注釋使用一個個星號盒子, 那么也請你使用這種方式.

    代碼風(fēng)格中一個關(guān)鍵點是整理一份常用詞匯表, 開發(fā)者認(rèn)同它并且遵循, 這樣在代碼中就能統(tǒng)一表述. 我們在這提出了一些全局上的風(fēng)格規(guī)則, 但也要考慮自身情況形成自己的代碼風(fēng)格. 但如果你添加的代碼和現(xiàn)有的代碼有很大的區(qū)別, 這就讓閱讀者感到很不和諧. 所以, 避免這種情況的發(fā)生.

    修訂版 2.9

    Aaron Whyte
    Bob Jervis
    Dan Pupius
    Erik Arvidsson
    Fritz Schneider
    Robby Walker

    譯者注: Google JavaScript 編碼風(fēng)格原文

    posted on 2013-03-11 15:24 brock 閱讀(3068) 評論(0)  編輯  收藏 所屬分類: css
    主站蜘蛛池模板: 东北美女野外bbwbbw免费| 国产jizzjizz视频免费看| 亚洲免费网站在线观看| 成全高清视频免费观看| 又黄又大的激情视频在线观看免费视频社区在线 | 蜜桃AV无码免费看永久| 亚洲熟妇AV乱码在线观看| AV在线亚洲男人的天堂| 午夜福利不卡片在线播放免费| 国产亚洲精品美女久久久久| 亚洲av日韩综合一区在线观看| 成人性生交视频免费观看| 伊人免费在线观看| 亚洲va中文字幕| 日产亚洲一区二区三区| 免费国产真实迷j在线观看| 99久久精品免费精品国产| 高潮内射免费看片| 亚洲成年人免费网站| 日本亚洲国产一区二区三区| 久久综合AV免费观看| 日本在线免费观看| 一级毛片不卡免费看老司机| 亚洲精品456人成在线| 亚洲国产精品婷婷久久| 亚洲精品黄色视频在线观看免费资源| 四虎永久在线观看免费网站网址 | 日韩亚洲国产高清免费视频| 一级做a爱过程免费视| 亚洲精品动漫免费二区| 亚洲精品在线播放| 亚洲va久久久噜噜噜久久 | 亚洲福利一区二区精品秒拍| 亚洲无线码在线一区观看| 国产网站免费观看| 男男AV纯肉无码免费播放无码| 99热精品在线免费观看| 中国一级全黄的免费观看| 免费精品视频在线| 成人婷婷网色偷偷亚洲男人的天堂| 亚洲国产综合在线|