<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      深入理解JavaScript系列(1):編寫高質(zhì)量JavaScript代碼的基本要點

      2011-12-28 23:00  湯姆大叔  閱讀(224103)  評論(127)    收藏  舉報

      才華橫溢的Stoyan Stefanov,在他寫的由O’Reilly初版的新書《JavaScript Patterns》(JavaScript模式)中,我想要是為我們的讀者貢獻其摘要,那會是件很美妙的事情。具體一點就是編寫高質(zhì)量JavaScript的一些要素,例如避免全局變量,使用單變量聲明,在循環(huán)中預緩存length(長度),遵循代碼閱讀,以及更多。

      此摘要也包括一些與代碼不太相關(guān)的習慣,但對整體代碼的創(chuàng)建息息相關(guān),包括撰寫API文檔、執(zhí)行同行評審以及運行JSLint。這些習慣和最佳做法可以幫助你寫出更好的,更易于理解和維護的代碼,這些代碼在幾個月或是幾年之后再回過頭看看也是會覺得很自豪的。

      書寫可維護的代碼(Writing Maintainable Code )

      軟件bug的修復是昂貴的,并且隨著時間的推移,這些bug的成本也會增加,尤其當這些bug潛伏并慢慢出現(xiàn)在已經(jīng)發(fā)布的軟件中時。當你發(fā)現(xiàn)bug 的時候就立即修復它是最好的,此時你代碼要解決的問題在你腦中還是很清晰的。否則,你轉(zhuǎn)移到其他任務(wù),忘了那個特定的代碼,一段時間后再去查看這些代碼就 需要:

      • 花時間學習和理解這個問題
      • 化時間是了解應(yīng)該解決的問題代碼

      還有問題,特別對于大的項目或是公司,修復bug的這位伙計不是寫代碼的那個人(且發(fā)現(xiàn)bug和修復bug的不是同一個人)。因此,必須降低理解代 碼花費的時間,無論是一段時間前你自己寫的代碼還是團隊中的其他成員寫的代碼。這關(guān)系到底線(營業(yè)收入)和開發(fā)人員的幸福,因為我們更應(yīng)該去開發(fā)新的激動 人心的事物而不是花幾小時幾天的時間去維護遺留代碼。

      另一個相關(guān)軟件開發(fā)生命的事實是,讀代碼花費的時間要比寫來得多。有時候,當你專注并深入思考某個問題的時候,你可以坐下來,一個下午寫大量的代碼。

      你的代碼很能很快就工作了,但是,隨著應(yīng)用的成熟,還會有很多其他的事情發(fā)生,這就要求你的進行進行審查,修改,和調(diào)整。例如:

      • bug是暴露的
      • 新功能被添加到應(yīng)用程序
      • 程序在新的環(huán)境下工作(例如,市場上出現(xiàn)新想瀏覽器)
      • 代碼改變用途
      • 代碼得完全從頭重新,或移植到另一個架構(gòu)上或者甚至使用另一種語言

      由于這些變化,很少人力數(shù)小時寫的代碼最終演變成花數(shù)周來閱讀這些代碼。這就是為什么創(chuàng)建可維護的代碼對應(yīng)用程序的成功至關(guān)重要。

      可維護的代碼意味著:

      • 可讀的
      • 一致的
      • 可預測的
      • 看上去就像是同一個人寫的
      • 已記錄

      最小全局變量(Minimizing Globals)

      JavaScript通過函數(shù)管理作用域。在函數(shù)內(nèi)部聲明的變量只在這個函數(shù)內(nèi)部,函數(shù)外面不可用。另一方面,全局變量就是在任何函數(shù)外面聲明的或是未聲明直接簡單使用的。

      每個JavaScript環(huán)境有一個全局對象,當你在任意的函數(shù)外面使用this的時候可以訪問到。你創(chuàng)建的每一個全部變量都成了這個全局對象的屬 性。在瀏覽器中,方便起見,該全局對象有個附加屬性叫做window,此window(通常)指向該全局對象本身。下面的代碼片段顯示了如何在瀏覽器環(huán)境 中創(chuàng)建和訪問的全局變量:

      myglobal = "hello"; // 不推薦寫法
      console.log(myglobal); // "hello"
      console.log(window.myglobal); // "hello"
      console.log(window["myglobal"]); // "hello"
      console.log(this.myglobal); // "hello"

       

      全局變量的問題

      全局變量的問題在于,你的JavaScript應(yīng)用程序和web頁面上的所有代碼都共享了這些全局變量,他們住在同一個全局命名空間,所以當程序的兩個不同部分定義同名但不同作用的全局變量的時候,命名沖突在所難免。

      web頁面包含不是該頁面開發(fā)者所寫的代碼也是比較常見的,例如:

      • 第三方的JavaScript庫
      • 廣告方的腳本代碼
      • 第三方用戶跟蹤和分析腳本代碼
      • 不同類型的小組件,標志和按鈕

      比方說,該第三方腳本定義了一個全局變量,叫做result;接著,在你的函數(shù)中也定義一個名為result的全局變量。其結(jié)果就是后面的變量覆蓋前面的,第三方腳本就一下子嗝屁啦!

      因此,要想和其他腳本成為好鄰居的話,盡可能少的使用全局變量是很重要的。在書中后面提到的一些減少全局變量的策略,例如命名空間模式或是函數(shù)立即自動執(zhí)行,但是要想讓全局變量少最重要的還是始終使用var來聲明變量。

      由于JavaScript的兩個特征,不自覺地創(chuàng)建出全局變量是出乎意料的容易。首先,你可以甚至不需要聲明就可以使用變量;第二,JavaScript有隱含的全局概念,意味著你不聲明的任何變量都會成為一個全局對象屬性。參考下面的代碼:

      function sum(x, y) {
      // 不推薦寫法: 隱式全局變量
      result = x + y;
      return result;
      }

      此段代碼中的result沒有聲明。代碼照樣運作正常,但在調(diào)用函數(shù)后你最后的結(jié)果就多一個全局命名空間,這可以是一個問題的根源。

      經(jīng)驗法則是始終使用var聲明變量,正如改進版的sum()函數(shù)所演示的:

      function sum(x, y) {
      var result = x + y;
      return result;
      }

      另一個創(chuàng)建隱式全局變量的反例就是使用任務(wù)鏈進行部分var聲明。下面的片段中,a是本地變量但是b確實全局變量,這可能不是你希望發(fā)生的:

      // 反例,勿使用 
      function foo() {
      var a = b = 0;
      // ...
      }

      此現(xiàn)象發(fā)生的原因在于這個從右到左的賦值,首先,是賦值表達式b = 0,此情況下b是未聲明的。這個表達式的返回值是0,然后這個0就分配給了通過var定義的這個局部變量a。換句話說,就好比你輸入了:

      var a = (b = 0);

      如果你已經(jīng)準備好聲明變量,使用鏈分配是比較好的做法,不會產(chǎn)生任何意料之外的全局變量,如:

      function foo() {
      var a, b;
      // ... a = b = 0; // 兩個均局部變量
      }

       

      然而,另外一個避免全局變量的原因是可移植性。如果你想你的代碼在不同的環(huán)境下(主機下)運行,使用全局變量如履薄冰,因為你會無意中覆蓋你最初環(huán)境下不存在的主機對象(所以你原以為名稱可以放心大膽地使用,實際上對于有些情況并不適用)。

      忘記var的副作用(Side Effects When Forgetting var)

      隱式全局變量和明確定義的全局變量間有些小的差異,就是通過delete操作符讓變量未定義的能力。

      • 通過var創(chuàng)建的全局變量(任何函數(shù)之外的程序中創(chuàng)建)是不能被刪除的。
      • 無var創(chuàng)建的隱式全局變量(無視是否在函數(shù)中創(chuàng)建)是能被刪除的。

      這表明,在技術(shù)上,隱式全局變量并不是真正的全局變量,但它們是全局對象的屬性。屬性是可以通過delete操作符刪除的,而變量是不能的:

      // 定義三個全局變量
      var global_var = 1;
      global_novar = 2; // 反面教材
      (function () {
      global_fromfunc = 3; // 反面教材
      }());

      // 試圖刪除
      delete global_var; // false
      delete global_novar; // true
      delete global_fromfunc; // true

      // 測試該刪除
      typeof global_var; // "number"
      typeof global_novar; // "undefined"
      typeof global_fromfunc; // "undefined"

      在ES5嚴格模式下,未聲明的變量(如在前面的代碼片段中的兩個反面教材)工作時會拋出一個錯誤。

      訪問全局對象(Access to the Global Object)

      在瀏覽器中,全局對象可以通過window屬性在代碼的任何位置訪問(除非你做了些比較出格的事情,像是聲明了一個名為window的局部變量)。但是在其他環(huán)境下,這個方便的屬性可能被叫做其他什么東西(甚至在程序中不可用)。如果你需要在沒有硬編碼的window標識符下訪問全局對象,你可以在任何層級的函數(shù)作用域中做如下操作:

      var global = (function () {
      return this;
      }());

       

      這種方法可以隨時獲得全局對象,因為其在函數(shù)中被當做函數(shù)調(diào)用了(不是通過new構(gòu)造),this總 是指向全局對象。實際上這個病不適用于ECMAScript 5嚴格模式,所以,在嚴格模式下時,你必須采取不同的形式。例如,你正在開發(fā)一個JavaScript庫,你可以將你的代碼包裹在一個即時函數(shù)中,然后從 全局作用域中,傳遞一個引用指向this作為你即時函數(shù)的參數(shù)。

      單var形式(Single var Pattern)

      在函數(shù)頂部使用單var語句是比較有用的一種形式,其好處在于:

      • 提供了一個單一的地方去尋找功能所需要的所有局部變量
      • 防止變量在定義之前使用的邏輯錯誤
      • 幫助你記住聲明的全局變量,因此較少了全局變量//zxx:此處我自己是有點暈乎的…
      • 少代碼(類型啊傳值啊單線完成)

      單var形式長得就像下面這個樣子:

      function func() {
      var a = 1,
      b = 2,
      sum = a + b,
      myobject = {},
      i,
      j;
      // function body...
      }

       

      您可以使用一個var語句聲明多個變量,并以逗號分隔。像這種初始化變量同時初始化值的做法是很好的。這樣子可以防止邏輯錯誤(所有未初始化但聲明的變量的初始值是undefined)和增加代碼的可讀性。在你看到代碼后,你可以根據(jù)初始化的值知道這些變量大致的用途,例如是要當作對象呢還是當作整數(shù)來使。

      你也可以在聲明的時候做一些實際的工作,例如前面代碼中的sum = a + b這個情況,另外一個例子就是當你使用DOM(文檔對象模型)引用時,你可以使用單一的var把DOM引用一起指定為局部變量,就如下面代碼所示的:

      function updateElement() {
      var el = document.getElementById("result"),
      style = el.style;
      // 使用el和style干點其他什么事...
      }

       

      預解析:var散布的問題(Hoisting: A Problem with Scattered vars)

      JavaScript中,你可以在函數(shù)的任何位置聲明多個var語句,并且它們就好像是在函數(shù)頂部聲明一樣發(fā)揮作用,這種行為稱為 hoisting(懸置/置頂解析/預解析)。當你使用了一個變量,然后不久在函數(shù)中又重新聲明的話,就可能產(chǎn)生邏輯錯誤。對于JavaScript,只 要你的變量是在同一個作用域中(同一函數(shù)),它都被當做是聲明的,即使是它在var聲明前使用的時候。看下面這個例子:

      // 反例
      myname = "global"; // 全局變量
      function func() {
      alert(myname); // "undefined"
      var myname = "local";
      alert(myname); // "local"
      }
      func();


      在這個例子中,你可能會以為第一個alert彈出的是”global”,第二個彈出”loacl”。這種期許是可以理解的,因為在第一個alert 的時候,myname未聲明,此時函數(shù)肯定很自然而然地看全局變量myname,但是,實際上并不是這么工作的。第一個alert會彈 出”undefined”是因為myname被當做了函數(shù)的局部變量(盡管是之后聲明的),所有的變量聲明當被懸置到函數(shù)的頂部了。因此,為了避免這種混 亂,最好是預先聲明你想使用的全部變量。

      上面的代碼片段執(zhí)行的行為可能就像下面這樣:

      myname = "global"; // global variable
      function func() {
      var myname; // 等同于 -> var myname = undefined;
      alert(myname); // "undefined"
      myname = "local";
      alert(myname); // "local"}
      func();

       

      為了完整,我們再提一提執(zhí)行層面的稍微復雜點的東西。代碼處理分兩個階段,第一階段是變量,函數(shù)聲明,以及正常格式的參數(shù)創(chuàng)建,這是一個解析和進入上下文 的階段。第二個階段是代碼執(zhí)行,函數(shù)表達式和不合格的標識符(為聲明的變量)被創(chuàng)建。但是,出于實用的目的,我們就采用了”hoisting”這個概念, 這種ECMAScript標準中并未定義,通常用來描述行為。

      for循環(huán)(for Loops)

      for循環(huán)中,你可以循環(huán)取得數(shù)組或是數(shù)組類似對象的值,譬如argumentsHTMLCollection對象。通常的循環(huán)形式如下:

      // 次佳的循環(huán)
      for (var i = 0; i < myarray.length; i++) {
      // 使用myarray[i]做點什么
      }


      這種形式的循環(huán)的不足在于每次循環(huán)的時候數(shù)組的長度都要去獲取下。這回降低你的代碼,尤其當myarray不是數(shù)組,而是一個HTMLCollection對象的時候。

      HTMLCollections指的是DOM方法返回的對象,例如:

      document.getElementsByName()
      document.getElementsByClassName()
      document.getElementsByTagName()

       

      還有其他一些HTMLCollections,這些是在DOM標準之前引進并且現(xiàn)在還在使用的。有:

      document.images: 頁面上所有的圖片元素
      document.links : 所有a標簽元素
      document.forms : 所有表單
      document.forms[0].elements : 頁面上第一個表單中的所有域

       

      集合的麻煩在于它們實時查詢基本文檔(HTML頁面)。這意味著每次你訪問任何集合的長度,你要實時查詢DOM,而DOM操作一般都是比較昂貴的。

      這就是為什么當你循環(huán)獲取值時,緩存數(shù)組(或集合)的長度是比較好的形式,正如下面代碼顯示的:

      for (var i = 0, max = myarray.length; i < max; i++) {
      // 使用myarray[i]做點什么
      }

       

      這樣,在這個循環(huán)過程中,你只檢索了一次長度值。

      在所有瀏覽器下,循環(huán)獲取內(nèi)容時緩存HTMLCollections的長度是更快的,2倍(Safari3)到190倍(IE7)之間。//zxx:此數(shù)據(jù)貌似很老,僅供參考

      注意到,當你明確想要修改循環(huán)中的集合的時候(例如,添加更多的DOM元素),你可能更喜歡長度更新而不是常量。

      伴隨著單var形式,你可以把變量從循環(huán)中提出來,就像下面這樣:

      function looper() {
      var i = 0,
      max,
      myarray = [];
      // ...
      for (i = 0, max = myarray.length; i < max; i++) {
      // 使用myarray[i]做點什么
      }
      }

       

      這種形式具有一致性的好處,因為你堅持了單一var形式。不足在于當重構(gòu)代碼的時候,復制和粘貼整個循環(huán)有點困難。例如,你從一個函數(shù)復制了一個循環(huán)到另一個函數(shù),你不得不去確定你能夠把imax引入新的函數(shù)(如果在這里沒有用的話,很有可能你要從原函數(shù)中把它們刪掉)。

      最后一個需要對循環(huán)進行調(diào)整的是使用下面表達式之一來替換i++

      i = i + 1
      i += 1

       

      JSLint提示您這樣做,原因是++–-促進了“過分棘手(excessive trickiness)”。//zxx:這里比較難翻譯,我想本意應(yīng)該是讓代碼變得更加的棘手
      如果你直接無視它,JSLint的plusplus選項會是false(默認是default)。

      還有兩種變化的形式,其又有了些微改進,因為:

      • 少了一個變量(無max)
      • 向下數(shù)到0,通常更快,因為和0做比較要比和數(shù)組長度或是其他不是0的東西作比較更有效率

       

      //第一種變化的形式:

      var i, myarray = [];
      for (i = myarray.length; i–-;) {
      // 使用myarray[i]做點什么
      }

      //第二種使用while循環(huán):

      var myarray = [],
      i = myarray.length;
      while (i–-) {
      // 使用myarray[i]做點什么
      }

      這些小的改進只體現(xiàn)在性能上,此外JSLint會對使用i–-加以抱怨。

      for-in循環(huán)(for-in Loops)

      for-in循環(huán)應(yīng)該用在非數(shù)組對象的遍歷上,使用for-in進行循環(huán)也被稱為“枚舉”。

      從技術(shù)上將,你可以使用for-in循環(huán)數(shù)組(因為JavaScript中數(shù)組也是對象),但這是不推薦的。因為如果數(shù)組對象已被自定義的功能增強,就可能發(fā)生邏輯錯誤。另外,在for-in中,屬性列表的順序(序列)是不能保證的。所以最好數(shù)組使用正常的for循環(huán),對象使用for-in循環(huán)。

      有個很重要的hasOwnProperty()方法,當遍歷對象屬性的時候可以過濾掉從原型鏈上下來的屬性。

      思考下面一段代碼:

      // 對象
      var man = {
      hands: 2,
      legs: 2,
      heads: 1
      };

      // 在代碼的某個地方
      //
      一個方法添加給了所有對象
      if (typeof Object.prototype.clone === "undefined") {
      Object.prototype.clone = function () {};
      }


      在這個例子中,我們有一個使用對象字面量定義的名叫man的對象。在man定義完成后的某個地方,在對象原型上增加了一個很有用的名叫 clone()的方法。此原型鏈是實時的,這就意味著所有的對象自動可以訪問新的方法。為了避免枚舉man的時候出現(xiàn)clone()方法,你需要應(yīng)用hasOwnProperty()方法過濾原型屬性。如果不做過濾,會導致clone()函數(shù)顯示出來,在大多數(shù)情況下這是不希望出現(xiàn)的。

      // 1.
      //
      for-in 循環(huán)
      for (var i in man) {
      if (man.hasOwnProperty(i)) { // 過濾
      console.log(i, ":", man[i]);
      }
      }
      /* 控制臺顯示結(jié)果
      hands : 2
      legs : 2
      heads : 1
      */
      // 2.
      //
      反面例子:
      //
      for-in loop without checking hasOwnProperty()
      for (var i in man) {
      console.log(i, ":", man[i]);
      }
      /*
      控制臺顯示結(jié)果
      hands : 2
      legs : 2
      heads : 1
      clone: function()
      */


      另外一種使用hasOwnProperty()的形式是取消Object.prototype上的方法。像是:

      for (var i in man) {
      if (Object.prototype.hasOwnProperty.call(man, i)) { // 過濾
      console.log(i, ":", man[i]);
      }
      }

       

      其好處在于在man對象重新定義hasOwnProperty情況下避免命名沖突。也避免了長屬性查找對象的所有方法,你可以使用局部變量“緩存”它。

      var i, hasOwn = Object.prototype.hasOwnProperty;
      for (i in man) {
      if (hasOwn.call(man, i)) { // 過濾
      console.log(i, ":", man[i]);
      }
      }

       

      嚴格來說,不使用hasOwnProperty()并不是一個錯誤。根據(jù)任務(wù)以及你對代碼的自信程度,你可以跳過它以提高些許的循環(huán)速度。但是當你對當前對象內(nèi)容(和其原型鏈)不確定的時候,添加hasOwnProperty()更加保險些。

      格式化的變化(通不過JSLint)會直接忽略掉花括號,把if語句放到同一行上。其優(yōu)點在于循環(huán)語句讀起來就像一個完整的想法(每個元素都有一個自己的屬性”X”,使用”X”干點什么):

      // 警告: 通不過JSLint檢測
      var i, hasOwn = Object.prototype.hasOwnProperty;
      for (i in man) if (hasOwn.call(man, i)) { // 過濾
      console.log(i, ":", man[i]);
      }


      (不)擴展內(nèi)置原型((Not) Augmenting Built-in Prototypes)

      擴增構(gòu)造函數(shù)的prototype屬性是個很強大的增加功能的方法,但有時候它太強大了。

      增加內(nèi)置的構(gòu)造函數(shù)原型(如Object(), Array(), 或Function())挺誘人的,但是這嚴重降低了可維護性,因為它讓你的代碼變得難以預測。使用你代碼的其他開發(fā)人員很可能更期望使用內(nèi)置的 JavaScript方法來持續(xù)不斷地工作,而不是你另加的方法。

      另外,屬性添加到原型中,可能會導致不使用hasOwnProperty屬性時在循環(huán)中顯示出來,這會造成混亂。

      因此,不增加內(nèi)置原型是最好的。你可以指定一個規(guī)則,僅當下面的條件均滿足時例外:

      • 可以預期將來的ECMAScript版本或是JavaScript實現(xiàn)將一直將此功能當作內(nèi)置方法來實現(xiàn)。例如,你可以添加ECMAScript 5中描述的方法,一直到各個瀏覽器都迎頭趕上。這種情況下,你只是提前定義了有用的方法。
      • 如果您檢查您的自定義屬性或方法已不存在——也許已經(jīng)在代碼的其他地方實現(xiàn)或已經(jīng)是你支持的瀏覽器JavaScript引擎部分。
      • 你清楚地文檔記錄并和團隊交流了變化。

      如果這三個條件得到滿足,你可以給原型進行自定義的添加,形式如下:

      if (typeof Object.protoype.myMethod !== "function") {
      Object.protoype.myMethod = function () {
      // 實現(xiàn)...
      };
      }

       

      switch模式(switch Pattern)

      你可以通過類似下面形式的switch語句增強可讀性和健壯性:

      var inspect_me = 0,
      result = '';
      switch (inspect_me) {
      case 0:
      result = "zero";
      break;
      case 1:
      result = "one";
      break;
      default:
      result = "unknown";
      }

       

      這個簡單的例子中所遵循的風格約定如下:

      • 每個case和switch對齊(花括號縮進規(guī)則除外)
      • 每個case中代碼縮進
      • 每個case以break清除結(jié)束
      • 避免貫穿(故意忽略break)。如果你非常確信貫穿是最好的方法,務(wù)必記錄此情況,因為對于有些閱讀人而言,它們可能看起來是錯誤的。
      • 以default結(jié)束switch:確??傆薪∪慕Y(jié)果,即使無情況匹配。

      避免隱式類型轉(zhuǎn)換(Avoiding Implied Typecasting )

      JavaScript的變量在比較的時候會隱式類型轉(zhuǎn)換。這就是為什么一些諸如:false == 0 或 “” == 0 返回的結(jié)果是true。為避免引起混亂的隱含類型轉(zhuǎn)換,在你比較值和表達式類型的時候始終使用===和!==操作符。

      var zero = 0;
      if (zero === false) {
      // 不執(zhí)行,因為zero為0, 而不是false
      }

      // 反面示例
      if (zero == false) {
      // 執(zhí)行了...
      }

       

      還有另外一種思想觀點認為==就足夠了===是多余的。例如,當你使用typeof你就知道它會返回一個字符串,所以沒有使用嚴格相等的理由。然而,JSLint要求嚴格相等,它使代碼看上去更有一致性,可以降低代碼閱讀時的精力消耗。(“==是故意的還是一個疏漏?”)

      避免(Avoiding) eval()

      如果你現(xiàn)在的代碼中使用了eval(),記住該咒語“eval()是魔鬼”。此方法接受任意的字符串,并當作JavaScript代碼來處理。當有 問題的代碼是事先知道的(不是運行時確定的),沒有理由使用eval()。如果代碼是在運行時動態(tài)生成,有一個更好的方式不使用eval而達到同樣的目 標。例如,用方括號表示法來訪問動態(tài)屬性會更好更簡單:

      // 反面示例
      var property = "name";
      alert(eval("obj." + property));

      // 更好的
      var property = "name";
      alert(obj[property]);


      使用eval()也帶來了安全隱患,因為被執(zhí)行的代碼(例如從網(wǎng)絡(luò)來)可能已被篡改。這是個很常見的反面教材,當處理Ajax請求得到的JSON 相應(yīng)的時候。在這些情況下,最好使用JavaScript內(nèi)置方法來解析JSON相應(yīng),以確保安全和有效。若瀏覽器不支持JSON.parse(),你可 以使用來自JSON.org的庫。

      同樣重要的是要記住,給setInterval(), setTimeout()和Function()構(gòu)造函數(shù)傳遞字符串,大部分情況下,與使用eval()是類似的,因此要避免。在幕后,JavaScript仍需要評估和執(zhí)行你給程序傳遞的字符串:

      // 反面示例
      setTimeout("myFunc()", 1000);
      setTimeout("myFunc(1, 2, 3)", 1000);

      // 更好的
      setTimeout(myFunc, 1000);
      setTimeout(function () {
      myFunc(1, 2, 3);
      }, 1000);


      使用新的Function()構(gòu)造就類似于eval(),應(yīng)小心接近。這可能是一個強大的構(gòu)造,但往往被誤用。如果你絕對必須使用eval(),你 可以考慮使用new Function()代替。有一個小的潛在好處,因為在新Function()中作代碼評估是在局部函數(shù)作用域中運行,所以代碼中任何被評估的通過var 定義的變量都不會自動變成全局變量。另一種方法來阻止自動全局變量是封裝eval()調(diào)用到一個即時函數(shù)中。

      考慮下面這個例子,這里僅un作為全局變量污染了命名空間。

      console.log(typeof un);    // "undefined"
      console.log(typeof deux); // "undefined"
      console.log(typeof trois); // "undefined"

      var jsstring = "var un = 1; console.log(un);";
      eval(jsstring); // logs "1"

      jsstring = "var deux = 2; console.log(deux);";
      new Function(jsstring)(); // logs "2"

      jsstring = "var trois = 3; console.log(trois);";
      (function () {
      eval(jsstring);
      }()); // logs "3"

      console.log(typeof un); // number
      console.log(typeof deux); // "undefined"
      console.log(typeof trois); // "undefined"

       

      另一間eval()和Function構(gòu)造不同的是eval()可以干擾作用域鏈,而Function()更安分守己些。不管你在哪里執(zhí)行 Function(),它只看到全局作用域。所以其能很好的避免本地變量污染。在下面這個例子中,eval()可以訪問和修改它外部作用域中的變量,這是 Function做不來的(注意到使用Function和new Function是相同的)。

      (function () {
      var local = 1;
      eval("local = 3; console.log(local)"); // logs "3"
      console.log(local); // logs "3"
      }());

      (function () {
      var local = 1;
      Function("console.log(typeof local);")(); // logs undefined
      }());

       

      parseInt()下的數(shù)值轉(zhuǎn)換(Number Conversions with parseInt())

      使用parseInt()你可以從字符串中獲取數(shù)值,該方法接受另一個基數(shù)參數(shù),這經(jīng)常省略,但不應(yīng)該。當字符串以”0″開頭的時候就有可能會出問 題,例如,部分時間進入表單域,在ECMAScript 3中,開頭為”0″的字符串被當做8進制處理了,但這已在ECMAScript 5中改變了。為了避免矛盾和意外的結(jié)果,總是指定基數(shù)參數(shù)。

      var month = "06",
      year = "09";
      month = parseInt(month, 10);
      year = parseInt(year, 10);

       

      此例中,如果你忽略了基數(shù)參數(shù),如parseInt(year),返回的值將是0,因為“09”被當做8進制(好比執(zhí)行 parseInt( year, 8 )),而09在8進制中不是個有效數(shù)字。

      替換方法是將字符串轉(zhuǎn)換成數(shù)字,包括:

      +"08" // 結(jié)果是 8
      Number("08") // 8

       

      這些通??煊趐arseInt(),因為parseInt()方法,顧名思意,不是簡單地解析與轉(zhuǎn)換。但是,如果你想輸入例如“08 hello”,parseInt()將返回數(shù)字,而其它以NaN告終。

      編碼規(guī)范(Coding Conventions)

      建立和遵循編碼規(guī)范是很重要的,這讓你的代碼保持一致性,可預測,更易于閱讀和理解。一個新的開發(fā)者加入這個團隊可以通讀規(guī)范,理解其它團隊成員書寫的代碼,更快上手干活。

      許多激烈的爭論發(fā)生會議上或是郵件列表上,問題往往針對某些代碼規(guī)范的特定方面(例如代碼縮進,是Tab制表符鍵還是space空格鍵)。如果你是 你組織中建議采用規(guī)范的,準備好面對各種反對的或是聽起來不同但很強烈的觀點。要記住,建立和堅定不移地遵循規(guī)范要比糾結(jié)于規(guī)范的細節(jié)重要的多。

      縮進(Indentation)

      代碼沒有縮進基本上就不能讀了。唯一糟糕的事情就是不一致的縮進,因為它看上去像是遵循了規(guī)范,但是可能一路上伴隨著混亂和驚奇。重要的是規(guī)范地使用縮進。

      一些開發(fā)人員更喜歡用tab制表符縮進,因為任何人都可以調(diào)整他們的編輯器以自己喜歡的空格數(shù)來顯示Tab。有些人喜歡空格——通常四個,這都無所謂,只要團隊每個人都遵循同一個規(guī)范就好了。這本書,例如,使用四個空格縮進,這也是JSLint中默認的縮進。

      什么應(yīng)該縮進呢?規(guī)則很簡單——花括號里面的東西。這就意味著函數(shù)體,循環(huán) (do, while, for, for-in),if,switch,以及對象字面量中的對象屬性。下面的代碼就是使用縮進的示例:

      function outer(a, b) {
      var c = 1,
      d = 2,
      inner;
      if (a > b) {
      inner = function () {
      return {
      r: c - d
      };
      };
      } else {
      inner = function () {
      return {
      r: c + d
      };
      };
      }
      return inner;
      }

       

      花括號{}(Curly Braces)

      花括號(亦稱大括號,下同)應(yīng)總被使用,即使在它們?yōu)榭蛇x的時候。技術(shù)上將,在in或是for中如果語句僅一條,花括號是不需要的,但是你還是應(yīng)該總是使用它們,這會讓代碼更有持續(xù)性和易于更新。

      想象下你有一個只有一條語句的for循環(huán),你可以忽略花括號,而沒有解析的錯誤。

      // 糟糕的實例
      for (var i = 0; i < 10; i += 1)
      alert(i);

       

      但是,如果,后來,主體循環(huán)部分又增加了行代碼?

      // 糟糕的實例
      for (var i = 0; i < 10; i += 1)
      alert(i);
      alert(i + " is " + (i % 2 ? "odd" : "even"));


      第二個alert已經(jīng)在循環(huán)之外,縮進可能欺騙了你。為了長遠打算,最好總是使用花括號,即時值一行代碼:

      // 好的實例
      for (var i = 0; i < 10; i += 1) {
      alert(i);
      }


      if條件類似:

      //
      if (true)
      alert(1);
      else
      alert(2);

      //
      if (true) {
      alert(1);
      } else {
      alert(2);
      }


      左花括號的位置(Opening Brace Location)

      開發(fā)人員對于左大括號的位置有著不同的偏好——在同一行或是下一行。

      if (true) {
      alert("It's TRUE!");
      }

      //

      if (true)
      {
      alert("It's TRUE!");
      }



      這個實例中,仁者見仁智者見智,但也有個案,括號位置不同會有不同的行為表現(xiàn)。這是因為分號插入機制(semicolon insertion mechanism)——JavaScript是不挑剔的,當你選擇不使用分號結(jié)束一行代碼時JavaScript會自己幫你補上。這種行為可能會導致麻 煩,如當你返回對象字面量,而左括號卻在下一行的時候:

      // 警告: 意外的返回值
      function func() {
      return
      // 下面代碼不執(zhí)行
      {
      name : "Batman"
      }
      }

       

      如果你希望函數(shù)返回一個含有name屬性的對象,你會驚訝。由于隱含分號,函數(shù)返回undefined。前面的代碼等價于:

      // 警告: 意外的返回值
      function func() {
      return undefined;
      // 下面代碼不執(zhí)行
      {
      name : "Batman"
      }
      }

       

      總之,總是使用花括號,并始終把在與之前的語句放在同一行:

      function func() {
      return {
      name : "Batman"
      };
      }

       

      關(guān)于分號注:就像使用花括號,你應(yīng)該總是使用分號,即使他們可由JavaScript解析器隱式創(chuàng)建。這不僅促進更科學和更嚴格的代碼,而且有助于解決存有疑惑的地方,就如前面的例子顯示。

      空格(White Space)

      空格的使用同樣有助于改善代碼的可讀性和一致性。在寫英文句子的時候,在逗號和句號后面會使用間隔。在JavaScript中,你可以按照同樣的邏輯在列表模樣表達式(相當于逗號)和結(jié)束語句(相對于完成了“想法”)后面添加間隔。

      適合使用空格的地方包括:

      • for循環(huán)分號分開后的的部分:如for (var i = 0; i < 10; i += 1) {...}
      • for循環(huán)中初始化的多變量(i和max):for (var i = 0, max = 10; i < max; i += 1) {...}
      • 分隔數(shù)組項的逗號的后面:var a = [1, 2, 3];
      • 對象屬性逗號的后面以及分隔屬性名和屬性值的冒號的后面:var o = {a: 1, b: 2};
      • 限定函數(shù)參數(shù):myFunc(a, b, c)
      • 函數(shù)聲明的花括號的前面:function myFunc() {}
      • 匿名函數(shù)表達式function的后面:var myFunc = function () {};

      使用空格分開所有的操作符和操作對象是另一個不錯的使用,這意味著在+, -, *, =, <, >, <=, >=, ===, !==, &&, ||, +=等前后都需要空格。

      // 寬松一致的間距
      //
      使代碼更易讀
      //
      使得更加“透氣”
      var d = 0,
      a = b + 1;
      if (a && b && c) {
      d = a % c;
      a += d;
      }

      // 反面例子
      //
      缺失或間距不一
      //
      使代碼變得疑惑
      var d = 0,
      a = b + 1;
      if (a&&b&&c) {
      d=a % c;
      a+= d;
      }

       

      最后需要注意的一個空格——花括號間距。最好使用空格:

      • 函數(shù)、if-else語句、循環(huán)、對象字面量的左花括號的前面({)
      • else或while之間的右花括號(})

      空格使用的一點不足就是增加了文件的大小,但是壓縮無此問題。

      有一個經(jīng)常被忽略的代碼可讀性方面是垂直空格的使用。你可以使用空行來分隔代碼單元,就像是文學作品中使用段落分隔一樣。

      命名規(guī)范(Naming Conventions)

      另一種方法讓你的代碼更具可預測性和可維護性是采用命名規(guī)范。這就意味著你需要用同一種形式給你的變量和函數(shù)命名。

      下面是建議的一些命名規(guī)范,你可以原樣采用,也可以根據(jù)自己的喜好作調(diào)整。同樣,遵循規(guī)范要比規(guī)范是什么更重要。

      以大寫字母寫構(gòu)造函數(shù)(Capitalizing Constructors)

      JavaScript并沒有類,但有new調(diào)用的構(gòu)造函數(shù):

      var adam = new Person();  

       

      因為構(gòu)造函數(shù)仍僅僅是函數(shù),僅看函數(shù)名就可以幫助告訴你這應(yīng)該是一個構(gòu)造函數(shù)還是一個正常的函數(shù)。

      命名構(gòu)造函數(shù)時首字母大寫具有暗示作用,使用小寫命名的函數(shù)和方法不應(yīng)該使用new調(diào)用:

      function MyConstructor() {...}
      function myFunction() {...}

       

      分隔單詞(Separating Words)

      當你的變量或是函數(shù)名有多個單詞的時候,最好單詞的分離遵循統(tǒng)一的規(guī)范,有一個常見的做法被稱作“駝峰(Camel)命名法”,就是單詞小寫,每個單詞的首字母大寫。

      對于構(gòu)造函數(shù),可以使用大駝峰式命名法(upper camel case),如MyConstructor()。對于函數(shù)和方法名稱,你可以使用小駝峰式命名法(lower camel case),像是myFunction(), calculateArea()getFirstName()。

      要是變量不是函數(shù)呢?開發(fā)者通常使用小駝峰式命名法,但還有另外一種做法就是所有單詞小寫以下劃線連接:例如,first_name, favorite_bands, old_company_name,這種標記法幫你直觀地區(qū)分函數(shù)和其他標識——原型和對象。

      ECMAScript的屬性和方法均使用Camel標記法,盡管多字的屬性名稱是罕見的(正則表達式對象的lastIndex和ignoreCase屬性)。

      其它命名形式(Other Naming Patterns)

      有時,開發(fā)人員使用命名規(guī)范來彌補或替代語言特性。

      例如,JavaScript中沒有定義常量的方法(盡管有些內(nèi)置的像Number, MAX_VALUE),所以開發(fā)者都采用全部單詞大寫的規(guī)范來命名這個程序生命周期中都不會改變的變量,如:

      // 珍貴常數(shù),只可遠觀
      var PI = 3.14,
      MAX_WIDTH = 800;


      還有另外一個完全大寫的慣例:全局變量名字全部大寫。全部大寫命名全局變量可以加強減小全局變量數(shù)量的實踐,同時讓它們易于區(qū)分。

      另外一種使用規(guī)范來模擬功能的是私有成員。雖然可以在JavaScript中實現(xiàn)真正的私有,但是開發(fā)者發(fā)現(xiàn)僅僅使用一個下劃線前綴來表示一個私有屬性或方法會更容易些。考慮下面的例子:

      var person = {
      getName: function () {
      return this._getFirst() + ' ' + this._getLast();
      },

      _getFirst: function () {
      // ...
      },
      _getLast: function () {
      // ...
      }
      };

       

      在此例中,getName()就表示公共方法,部分穩(wěn)定的API。而_getFirst()_getLast()則表明了私有。它們?nèi)匀皇钦5墓卜椒?,但是使用下劃線前綴來警告person對象的使用者這些方法在下一個版本中時不能保證工作的,是不能直接使用的。注意,JSLint有些不鳥下劃線前綴,除非你設(shè)置了noman選項為:false。

      下面是一些常見的_private規(guī)范:

      • 使用尾下劃線表示私有,如name_getElements_()
      • 使用一個下劃線前綴表_protected(保護)屬性,兩個下劃線前綴表示__private (私有)屬性
      • Firefox中一些內(nèi)置的變量屬性不屬于該語言的技術(shù)部分,使用兩個前下劃線和兩個后下劃線表示,如:__proto____parent__

      注釋(Writing Comments)

      你必須注釋你的代碼,即使不會有其他人向你一樣接觸它。通常,當你深入研究一個問題,你會很清楚的知道這個代碼是干嘛用的,但是,當你一周之后再回來看的時候,想必也要耗掉不少腦細胞去搞明白到底怎么工作的。

      很顯然,注釋不能走極端:每個單獨變量或是單獨一行。但是,你通常應(yīng)該記錄所有的函數(shù),它們的參數(shù)和返回值,或是任何不尋常的技術(shù)和方法。要想到注 釋可以給你代碼未來的閱讀者以諸多提示;閱讀者需要的是(不要讀太多的東西)僅注釋和函數(shù)屬性名來理解你的代碼。例如,當你有五六行程序執(zhí)行特定的任務(wù), 如果你提供了一行代碼目的以及為什么在這里的描述的話,閱讀者就可以直接跳過這段細節(jié)。沒有硬性規(guī)定注釋代碼比,代碼的某些部分(如正則表達式)可能注釋 要比代碼多。

      最重要的習慣,然而也是最難遵守的,就是保持注釋的及時更新,因為過時的注釋比沒有注釋更加的誤導人。

      關(guān)于作者(About the Author )

      Stoyan Stefanov是Yahoo!web開發(fā)人員,多個O'Reilly書籍的作者、投稿者和技術(shù)評審。他經(jīng)常在會議和他的博客www.phpied.com上發(fā)表web開發(fā)主題的演講。Stoyan還是smush.it圖片優(yōu)化工具的創(chuàng)造者,YUI貢獻者,雅虎性能優(yōu)化工具YSlow 2.0的架構(gòu)設(shè)計師。

       

      本文轉(zhuǎn)自:http://www.zhangxinxu.com/wordpress/?p=1173

      英文原文:http://net.tutsplus.com/tutorials/javascript-ajax/the-essentials-of-writing-high-quality-javascript/

      同步與結(jié)束語

      本文已同步至目錄索引:深入理解JavaScript系列

      深入理解JavaScript系列文章,包括了原創(chuàng),翻譯,轉(zhuǎn)載等各類型的文章,如果對你有用,請推薦支持一把,給大叔寫作的動力。

      主站蜘蛛池模板: 一区二区三区在线色视频| 久久一日本综合色鬼综合色 | 大桥未久亚洲无av码在线| 免费看久久妇女高潮a| 欧美精品一产区二产区| 国产亚洲色视频在线| 大香伊蕉在人线国产av| 中文字幕有码在线第十页| 国产色无码精品视频免费| 影音先锋亚洲成aⅴ人在| 麻豆成人精品国产免费| 在线播放免费人成毛片| 亚洲高清日韩专区精品| 亚洲欧洲日韩国内精品| 欧美人与zoxxxx另类| 成全我在线观看免费第二季| 国产内射性高湖| 亚洲欧洲精品日韩av| 大冶市| 日本一区午夜艳熟免费| 欧洲精品色在线观看| 亚洲 一区二区 在线| 被灌满精子的少妇视频| 国产精品自在线拍国产手青青机版| 国产精品中文字幕免费| 性一交一乱一乱一视频| 欧美精品V欧洲精品| 国产AV影片麻豆精品传媒| 美日韩精品一区二区三区| 人妻在线无码一区二区三区| 蜜臀91精品高清国产福利| 在线日韩日本国产亚洲| 国产免费毛卡片| 部精品久久久久久久久| 久久不见久久见免费视频观看| 四虎影院176| 国产69精品久久久久99尤物 | 免费无码毛片一区二三区| av在线播放观看国产| 无码国产欧美一区二区三区不卡| 中文字幕国产在线精品|