Rising Sun

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

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

          修訂版: 2.9

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

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

           展開全部
          目錄

          重要注意事項(xiàng)

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

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

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

          背景

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

          JavaScript 語言規(guī)范

          變量

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

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

          常量

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

          Decision:

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

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

          對(duì)于非基本類型, 使用 @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, 因?yàn)?IE 不能識(shí)別, 所以不要使用.

           

          分號(hào)

          link
          總是使用分號(hào).

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

          而且有些情況下, 漏掉分號(hào)會(huì)很危險(xiǎn):

          // 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();

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

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

           

          為什么?

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

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

           

          嵌套函數(shù)

          link
          可以使用

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

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

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

          不要寫成:

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

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

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

          異常

          link
          可以

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

          自定義異常

          link
          可以

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

          標(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)險(xiǎn):

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

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

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

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

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

          link
          不是首選

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

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

          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 中最有用的特性了. 有一份比較好的介紹閉包原理的文檔.

          有一點(diǎn)需要牢記, 閉包保留了一個(gè)指向它封閉作用域的指針, 所以, 在給 DOM 元素附加閉包時(shí), 很可能會(huì)產(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 也保留了對(duì)閉包的引用, 這就產(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() 會(huì)讓程序執(zhí)行的比較混亂, 當(dāng) eval() 里面包含用戶輸入的話就更加危險(xiǎn). 可以用其他更佳的, 更清晰, 更安全的方式寫你的代碼, 所以一般情況下請(qǐng)不要使用 eval(). 當(dāng)碰到一些需要解析序列化串的情況下(如, 計(jì)算 RPC 響應(yīng)), 使用 eval 很容易實(shí)現(xiàn).

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

          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() 對(duì) RPC 響應(yīng)值進(jìn)行解碼. 例如, 你在使用 XMLHttpRequest 發(fā)出一個(gè) RPC 請(qǐng)求后, 通過 eval () 將服務(wù)端的響應(yīng)文本轉(zhuǎn)成 JavaScript 對(duì)象:

          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 讓你的代碼在語義上變得不清晰. 因?yàn)?nbsp;with 的對(duì)象, 可能會(huì)與局部變量產(chǎn)生沖突, 從而改變你程序原本的用義. 下面的代碼是干嘛的?

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

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

          this

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

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

          使用時(shí)很容易出錯(cuò), 所以只有在下面兩個(gè)情況時(shí)才能使用:

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

          for-in 循環(huán)

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

          對(duì) Array 用 for-in 循環(huán)有時(shí)會(huì)出錯(cuò). 因?yàn)樗⒉皇菑?nbsp;0 到 length - 1 進(jìn)行遍歷, 而是所有出現(xiàn)在對(duì)象及其原型鏈的鍵值. 下面就是一些失敗的使用案例:

          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 對(duì)象. Array 僅僅是擴(kuò)展自 Object (類似于其他 JS 中的對(duì)象, 就像 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.';

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

          Array 和 Object 直接量

          link
          使用

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

          使用 Array 構(gòu)造器很容易因?yàn)閭鲄⒉磺‘?dāng)導(dǎo)致錯(cuò)誤.

          // 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();

          如果傳入一個(gè)參數(shù)而不是2個(gè)參數(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)置對(duì)象的原型

          link
          不要

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

          IE下的條件注釋

          link
          不要使用

          不要這樣子寫:

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

          條件注釋妨礙自動(dòng)化工具的執(zhí)行, 因?yàn)樵谶\(yùn)行時(shí), 它們會(huì)改變 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ù)個(gè)數(shù)不固定時(shí), 應(yīng)該添加最后一個(gè)參數(shù) var_args 為參數(shù)的個(gè)數(shù). 你也可以不設(shè)置 var_args而取代使用 arguments.

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

           

          Getters 和 Setters

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

           

          命名空間

          JavaScript 不支持包和命名空間.

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

          為全局代碼使用命名空間

          在全局作用域上, 使用一個(gè)唯一的, 與工程/庫相關(guān)的名字作為前綴標(biāo)識(shí). 比如, 你的工程是 "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)選擇了一個(gè)子命名空間, 請(qǐng)確保父命名空間的負(fù)責(zé)人知道你在用哪個(gè)子命名空間, 比如說, 你為工程 'sloths' 創(chuàng)建一個(gè) 'hats' 子命名空間, 那確保 Sloth 團(tuán)隊(duì)人員知道你在使用 sloth.hats.

           

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

          "外部代碼" 是指來自于你代碼體系的外部, 可以獨(dú)立編譯. 內(nèi)外部命名應(yīng)該嚴(yán)格保持獨(dú)立. 如果你使用了外部庫, 他的所有對(duì)象都在 foo.hats.* 下, 那么你自己的代碼不能在 foo.hats.*下命名, 因?yàn)楹苡锌赡芷渌麍F(tuán)隊(duì)也在其中命名.

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

          不要對(duì)命名空間創(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)平臺(tái)上不識(shí)別大小寫的命名方式. 文件名以.js結(jié)尾, 不要包含除 - 和 _ 外的標(biāo)點(diǎn)符號(hào)(使用 - 優(yōu)于 _).

           

          自定義 toString() 方法

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

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

          延遲初始化

          link
          可以

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

          明確作用域

          link
          任何時(shí)候都需要

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

          代碼格式化

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

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

          大括號(hào)

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

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

           

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

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

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

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

          // 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í)符或者數(shù)值, 不要為了讓代碼好看些而手工對(duì)齊. 如:

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

          不要這樣做:

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

           

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

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

          // 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個(gè)空格, 而不是從 function 這個(gè)關(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ù)組和對(duì)象 , 和傳遞匿名函數(shù)外, 所有被拆開的多行文本要么選擇與之前的表達(dá)式左對(duì)齊, 要么以4個(gè)(而不是2個(gè))空格作為一縮進(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);

           

          二元和三元操作符

          操作符始終跟隨著前行, 這樣就不用顧慮分號(hào)的隱式插入問題. 如果一行實(shí)在放不下, 還是按照上述的縮進(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;

           

          括號(hào)

          link
          只在需要的時(shí)候使用

          不要濫用括號(hào), 只在必要的時(shí)候使用它.

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

          字符串

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

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

          var msg = 'This is some HTML';

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

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

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

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

          標(biāo)記為 @private 的構(gòu)造器, 表示該類只能在當(dāng)前文件或是其靜態(tài)/普通成員中實(shí)例化; 私有構(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)限即可訪問, 而不是只能在同一個(gè)類或類層次上. 而 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
          強(qiáng)烈建議你去使用編譯器.

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

          JavaScript 類型語言

          JS2 提議中包含了一種描述 JavaScript 類型的規(guī)范語法, 這里我們?cè)?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ù)的對(duì)象類型.
          參數(shù)化類型, 即指定了該類型中包含的一系列"類型參數(shù)". 類似于 Java 中的泛型.
          聯(lián)合類型{(number|boolean)}
          一個(gè)整數(shù)或者布爾值.
          表示其值可能是 A 類型, 也可能是 B 類型{(number,boolean)},{number|boolean},{(number||boolean)}
          記錄類型{{myNum: number, myObject}} 
          由現(xiàn)有類型組成的類型.

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

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

          可為空類型{?number}
          一個(gè)整型數(shù)或者為 NULL
          表示一個(gè)值可能是 A 類型或者 null. 默認(rèn), 每個(gè)對(duì)象都是可為空的. 注意: 函數(shù)類型不可為空.{number?}
          非空類型{!Object}
          一個(gè)對(duì)象, 但絕不會(huì)是 null 值.
          說明一個(gè)值是類型 A 且肯定不是 null. 默認(rèn)情況下, 所有值類型 (boolean, number, string, 和 undefined) 不可為空.{Object!}
          函數(shù)類型{function(string, boolean)}
          具有兩個(gè)參數(shù) ( string 和 boolean) 的函數(shù)類型, 返回值未知.
          說明一個(gè)函數(shù).
          函數(shù)返回類型{function(): number}
          函數(shù)返回一個(gè)整數(shù).
          說明函數(shù)的返回類型.
          函數(shù)的 this類型{function(this:goog.ui.Menu, string)}
          函數(shù)只帶一個(gè)參數(shù) (string), 并且在上下文 goog.ui.Menu 中執(zhí)行.
          說明函數(shù)類型的上下文類型.
          可變參數(shù){function(string, ...[number]): number}
          帶一個(gè)參數(shù) (字符類型) 的函數(shù)類型, 并且函數(shù)的參數(shù)個(gè)數(shù)可變, 但參數(shù)類型必須為 number.
          說明函數(shù)的可變長參數(shù).
          可變長的參數(shù) (使用@param 標(biāo)記)@param {...number} var_args
          函數(shù)參數(shù)個(gè)數(shù)可變.
          使用標(biāo)記, 說明函數(shù)具有不定長參數(shù).
          函數(shù)的 缺省參數(shù){function(?string=, number=)}
          函數(shù)帶一個(gè)可空且可選的字符串型參數(shù), 一個(gè)可選整型參數(shù). = 語法只針對(duì)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 對(duì)象
          string
          'Hello' "World" String(42)
          字符串值
          String
          new String('Hello') new String(42)
          字符串對(duì)象
          boolean
          true false Boolean(0)
          布爾值
          Boolean
          new Boolean(true)
          布爾對(duì)象
          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'}
          值為字符串的對(duì)象.
          Object.<number, string>
          var obj = {}; obj[1] = 'bar';
          鍵為整數(shù), 值為字符串的對(duì)象.

           

          注意, JavaScript 中, 鍵總是被轉(zhuǎn)換成字符串, 所以obj['1'] == obj[1]. 也所以, 鍵在 for...in 循環(huán)中是字符串類型. 但在編譯器中會(huì)明確根據(jù)鍵的類型來查找對(duì)象.
          Function
          function(x, y) {   return x * y; }
          函數(shù)對(duì)象
          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é)點(diǎn).
          HTMLInputElement
          htmlDocument.getElementsByTagName('input')[0]
          DOM 中, 特定類型的元素.

           

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

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

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

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

          告訴編譯器 myValue_ 屬性為一對(duì)象或 null. 如果 myValue_ 永遠(yuǎn)都不會(huì)為 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í), 就會(huì)給出警告.

          函數(shù)的可選參數(shù)可能在運(yùn)行時(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_ 可能是一個(gè)對(duì)象, 或 null, 或 undefined.

          注意: 可選參數(shù) opt_value 被聲明成 {Object=}, 而不是 {Object|undefined}. 這是因?yàn)榭蛇x參數(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)點(diǎn)和拼寫.

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

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

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

          頂層/文件注釋

          頂層注釋用于告訴不熟悉這段代碼的讀者這個(gè)文件中包含哪些東西. 應(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è)類的定義都要附帶一份注釋, 描述類的功能和用法. 也需要說明構(gòu)造器參數(shù). 如果該類繼承自其它類, 應(yīng)該使用 @extends 標(biāo)記. 如果該類是對(duì)接口的實(shí)現(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) {   // ... }

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

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

           

          屬性注釋

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

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

           

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

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

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

           

          JSDoc 縮進(jìn)

          如果你在 @param@return@supported@this 或 @deprecated 中斷行, 需要像在代碼中一樣, 使用4個(gè)空格作為一個(gè)縮進(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).

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

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

          有時(shí)類型會(huì)很復(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)記來定義個(gè)常用的類型表達(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ù)時(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)  */
          表明文件的作者, 通常僅會(huì)在 @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)造器.會(huì)檢查. 如果省略了, 編譯器將禁止實(shí)例化.
          @interface@interface

          如:

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

          如:

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

          如:

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

          如:

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

          如:

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

           

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

           

          更多有關(guān)此標(biāo)記的內(nèi)容見 JSDoc Toolkit docs.
          會(huì)檢查
          @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)用這個(gè)方法時(shí), 需要在哪個(gè)上下文中. 當(dāng) this 指向的不是原型方法的函數(shù)時(shí)必須使用這個(gè)標(biāo)記.會(huì)檢查
          @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 };
          用于枚舉類型.完全支持. 如果省略, 會(huì)認(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í), 不要再使用. 同時(shí)也會(huì)給出替代方法或函數(shù).不需要
          @override@override

          如:

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

          如:

          /** @inheritDoc */ project.SubClass.prototype.toString() {   // ... };
          指明子類的方法和屬性是故意隱藏了父類的方法和屬性, 它們具有相同的文檔. 注意: 使用 @inheritDoc 意味著也同時(shí)使用了 @override.會(huì)檢查
          @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 的, 會(huì)被編譯器保留不做任何修改而直接輸出到最終文擋中. 這個(gè)標(biāo)記讓一些重要的信息(如法律許可或版權(quán)信息)原樣保留, 同樣, 文本中的換行也會(huì)被保留.不需要.
          @noalias@noalias

          如:

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

          如:

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

          如:

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

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

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

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

          1. 包含 //javascript/closure/base.js, 或者
          2. 在代碼庫中自定義 goog.exportSymbol 和 goog.exportProperty 兩個(gè)方法, 并保證有相同的調(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';

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

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

          支持.
          @nosideeffects@nosideeffects

          如:

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

          如:

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

          如:

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

          指明一個(gè)外部文件.

          不會(huì)檢查

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

          • @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 不會(huì)完全依照純文本中書寫的格式. 所以, 不要在 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ù)組
          • {} 空對(duì)象

          下面段比較糟糕的代碼:

          while (x != null) {

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

          while (x) {

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

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

          但這樣會(huì)更好:

          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 代碼時(shí)也是很有用的:

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

           

          && 和 ||

          二元布爾操作符是可短路的, 只有在必要時(shí)才會(huì)計(jì)算到最后一項(xiàng).

          "||" 被稱作為 'default' 操作符, 因?yàn)榭梢赃@樣:

          /** @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); }

          不過這樣就有點(diǎn)兒過頭了:

          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é)點(diǎn)迭代器加一個(gè)過濾器來實(shí)現(xiàn)的. 這表示獲取他的屬性, 如 length 的時(shí)間復(fù)雜度為 O(n), 通過 length 來遍歷整個(gè)列表需要 O(n^2).

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

          這樣做會(huì)更好:

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

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

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

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

           

          Parting Words

          保持一致性.

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

          代碼風(fēng)格中一個(gè)關(guān)鍵點(diǎn)是整理一份常用詞匯表, 開發(fā)者認(rèn)同它并且遵循, 這樣在代碼中就能統(tǒng)一表述. 我們?cè)谶@提出了一些全局上的風(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 閱讀(3066) 評(píng)論(0)  編輯  收藏 所屬分類: css
          主站蜘蛛池模板: 工布江达县| 即墨市| 崇仁县| 安义县| 吉木萨尔县| 旺苍县| 马鞍山市| 贡觉县| 嘉定区| 晋宁县| 台南县| 泉州市| 汉寿县| 平原县| 庆云县| 会同县| 嘉鱼县| 祁东县| 阿克苏市| 平泉县| 湾仔区| 铜梁县| 江都市| 东丰县| 南江县| 宜城市| 彩票| 灵川县| 昔阳县| 双柏县| 射阳县| 巴南区| 安达市| 蓬溪县| 铜陵市| 内黄县| 仁化县| 忻城县| 通海县| 麟游县| 晋宁县|