解讀優化 JavaScript 代碼_Xml教程

      編輯Tag賺U幣
      教程Tag:暫無Tag,歡迎添加,賺取U幣!

      推薦:如何使初學者開發出一個高質量的J2EE系統
      J2EE學習者越來越多,J2EE本身技術不斷在發展,涌現出各種概念,本文章試圖從一種容易理解的角度對這些概念向初學者進行解釋,以便掌握學習J2EE學習方向。 首先我們需要知道Java和J2EE是兩個不同概念,Java不只是指一種語言,已經代表與微軟不同的另外一個巨

      作者: Gregory Baker, GMail 軟件工程師 和 Erik Arvidsson, Google Chrome 軟件工程師

      需要的經驗: JavaScript 相關工作知識

      客戶端腳本能讓你的應用更加地動態和活躍, 但是瀏覽器對代碼的解析可能造成效率問題, 而這種性能差異在客戶端之間也不盡相同。 這里我們討論和給出一些優化你的 JavaScript 代碼的提示和最佳實踐。

      使用字符串

      字符串連接操作會對 Internet Explorer 6 和 7 的垃圾收集帶來很大的影響。 盡管這個問題在 Internet Explorer 8 里面得到解決 -- 字符串連接在 IE8 和其它非 IE 瀏覽器(如 Chrome)中稍微更有效率一點 -- 如果你的用戶中有很大一部分在使用 Internet Explorer 6 或 7, 你就需要非常注意你構建字符串的方式了。

      有如下示例代碼:

      以下為引用的內容:

      var veryLongMessage =
      'This is a long string that due to our strict line length limit of'
      maxCharsPerLine
      ' characters per line must be wrapped. '
      percentWhoDislike
      '% of engineers dislike this rule. The line length limit is for '
      ' style purposes, but we don't want it to have a performance impact.'
      ' So the question is how should we do the wrapping?';

      比起用連接的方式, 嘗試使用 join():

      以下為引用的內容:

      var veryLongMessage =
      ['This is a long string that due to our strict line length limit of',
      maxCharsPerLine,
      ' characters per line must be wrapped. ',
      percentWhoDislike,
      '% of engineers dislike this rule. The line length limit is for ',
      ' style purposes, but we don't want it to have a performance impact.',
      ' So the question is how should we do the wrapping?'
      ].join();

      相似的, 用連接的方式在條件語句和循環中構建字符串是很低效的. 錯誤的方式:

      var fibonacciStr = '前 20 個斐波那契數 ';
      for (var i = 0; i < 20; i++) {
      fibonacciStr += i + ' = ' + fibonacci(i) + '
      ';
      }

      正確的方法:

      var strBuilder = ['前 20 個斐波那契數:'];
      for (var i = 0; i < 20; i++) {
        strBuilder.push(i, ' = ', fibonacci(i));
      }
      var fibonacciStr = strBuilder.join('');

      構建通過輔助函數生成的字符串

      通過傳遞字符串構建器(可以是數組或者輔助類)到函數中構建長字符串, 以避免出現存放臨時結果的字符串.

      例如, 假定 buildMenuItemHtml_ 需要用文字串和變量構建一個字符串, 并且會在內部使用一個字符串構建器, 與其使用:

      var strBuilder = [];
      for (var i = 0; i < menuItems.length; i++) {
        strBuilder.push(this.buildMenuItemHtml_(menuItems[i]));
      }
      var menuHtml = strBuilder.join();

      不如用:

      var strBuilder = [];
      for (var i = 0; i < menuItems.length; i++) {
        this.buildMenuItem_(menuItems[i], strBuilder);
      }
      var menuHtml = strBuilder.join();

      定義類的方法

      下面的代碼效率不高, 因為每次構造 baz.Bar 的實例時, 都會為 foo 創建一個新函數和閉包(closure):

      baz.Bar = function() {
        // 構造函數代碼
        this.foo = function() {
        // 方法代碼
        };
      }

      推薦的方式為:

      baz.Bar = function() {
        // 構造函數代碼
      };

      baz.Bar.prototype.foo = function() {
        // 方法代碼
      };

      用這種方式, 無論構造了多少個 baz.Bar 實例, 只會創建一個函數給 foo, 同時不會創建任何閉包.

      初始化實例變量

      將帶有值類型(非引用的)的初始化值(例如類型為數字, 布爾值, null, undefined 或字符串的值)的變量聲明/初始化代碼直接放在 prototype 原型中. 這可以避免每次調用構造函數時不必要地運行初始化代碼. (這個方法無法應用到初始化值由構造器參數決定或構造時狀態不確定的實例變量上.)

      例如, 比起寫:

      foo.Bar = function() {
        this.prop1_ = 4;
        this.prop2_ = true;
        this.prop3_ = [];
        this.prop4_ = 'blah';
      };

      不如寫:

      foo.Bar = function() {
        this.prop3_ = [];
      };

      foo.Bar.prototype.prop1_ = 4;

      foo.Bar.prototype.prop2_ = true;

      foo.Bar.prototype.prop4_ = 'blah';

      謹慎地使用閉包(closure)

      閉包是 JavaScript 中一個強大而有用的特性; 但是, 它們也有不好的地方, 包括:

      • 它們是最常見的內存泄漏源頭.
      • 創建一個閉包比創建一個沒有閉包的內聯函數明顯要慢, 比起重用一個靜態函數則更慢. 例如:

        function setupAlertTimeout() {
          var msg = '要顯示的消息';
          window.setTimeout(function() { alert(msg); }, 100);
        }

        比下面的代碼慢:

        function setupAlertTimeout() {
          window.setTimeout(function() {
            var msg = '要顯示的消息';
            alert(msg);
          }, 100);
        }

        更比下面的代碼慢:

        function alertMsg() {
          var msg = '要顯示的消息';
          alert(msg);
        }

        function setupAlertTimeout() {
          window.setTimeout(alertMsg, 100);
        }
      • 他們增加了作用域鏈(scope chain)的層級. 當瀏覽器解析屬性時, 作用域鏈的每一個層級都必須被檢查一次. 在下面的例子中:

        var a = 'a';

        function createFunctionWithClosure() {
          var b = 'b';
          return function () {
            var c = 'c';
            a;
            b;
            c;
          };
        }

        var f = createFunctionWithClosure();
        f();

        f 被調用時, 引用 a 比引用 b 慢, 它們都比引用 c 要慢.

      查看 IE+JScript Performance Recommendations Part 3: JavaScript Code inefficiencies 獲得更多有關在 IE 中使用閉包的信息.

      避免使用 with

      在你的代碼中避免使用 with. 它對性能有非常壞的影響, 因為它修改了作用域鏈, 讓查找在其它作用域的變量變得代價高昂.

      避免瀏覽器內存泄漏

      內存泄漏對 Web 應用而言是個很普遍的問題, 它會帶來嚴重的性能問題. 當瀏覽器的內存使用上升時, 你的 Web 應用, 連同用戶系統的其他部分, 都會變慢. Web 應用最常見的內存泄漏原因是: 在 JavaScript 腳本引擎和瀏覽器 DOM 的 C++ 對象實現間的循環引用(例如, 在 JavaScript 腳本引擎和 Internet Explorer 的 COM 基礎架構間, 或者 JavaScript 引擎和 Firefox 的 XPCOM 基礎架構間).

      下面是避免內存泄漏的一些經驗法則:

      使用一個事件系統來附加事件處理函數

      最常見的循環引用模式 [ DOM 元素 --> 事件處理函數 --> 閉包作用域 --> DOM ] 在 這篇 MSDN 的 Blog 文章中討論過了. 為避免這個問題, 可以使用一個經過嚴格測試的事件系統來附件事件處理函數, 例如 Google doctype, Dojo, or JQuery.

      另外, 在 IE 中使用內聯(inline)的事件處理函數會導致另外一類泄漏. 這不是通常的循環引用泄漏, 而是內存中臨時匿名腳本對象的泄漏. 詳情請查看 理解和解決 IE 泄漏模式(Understanding and Solving Internet Explorer Leak Patterns) 的 "DOM 插入順序泄漏模型(DOM Insertion Order Leak Model)" 一節, 另外在 JavaScript Kit 教程 中還有一個例子.

      避免使用擴展(expando)屬性

      擴展屬性是附加到 DOM 元素上的任意 JavaScript 屬性, 也是循環引用的常見原因. 你能夠在使用擴展屬性時不導致內存泄漏, 但是很容易不小心就引入一個泄漏. 這個泄漏的模式是 [ DOM 元素 --> 擴展屬性 --> 中間對象 --> DOM 元素 ]. 最好的方法就是避免使用它們. 如果你要使用它們, 就只使用簡單的值類型. 如果你要非簡單的類型, 那么在不再需要擴展屬性時將它設為空(null). 參見 理解和解決 IE 泄漏模式(Understanding and Solving Internet Explorer Leak Patterns) 中的 "循環引用(Circular References)" 一節.

      分享:解讀自動更新程序的設計框架
      概要說明: 自動更新程序主要負責從服務器中獲取相應的更新文件,并且把這些文件下載到本地,替換現有的文件。達到修復Bug,更新功能的目的。 本文作為更新程序的一個框架性設計,主要側重介紹功能和流程。也許在若干年后,我再回顧當初自己的設計,會有另外

      來源:模板無憂//所屬分類:Xml教程/更新時間:2009-08-13
      相關Xml教程