JavaScript 性能優化小記

whywhom 8年前發布 | 6K 次閱讀 性能優化 JavaScript開發 JavaScript

加載與運行

  • 延期腳本

    <scripttype="text/javascript" src="file1.js"defer></script>
      js文件要在dom加載完成時才會被下載
  • 動態腳本元素

    var script= document.createElement ("script");
      script.type= "text/javascript";
      script.src= "file1.js";
      document.getElementsByTagName_r("head")[0].appendChild(script)

    無論在何處啟動下載,文件的下載和運行都不會阻塞其他頁面的處理過程</code></pre> </li>

  • XHR 腳本注入

    var xhr = newXMLHttpRequest();
      xhr.open("get", "file1.js", true);
      xhr.onreadystatechange = function() {
          if (xhr.readyState == 4) {
              if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
                  var script = document.createElement("script");
                  script.type = "text/javascript";
                  script.text = xhr.responseText;
                  document.body.appendChild(script);
              }
          }
      };
      xhr.send(null)

    在html頁面中產生內聯的js代碼,它下載后不會立即執行,所以可以控制它的執行狀態</code></pre> </li>

  • 推薦做法

    <script type = "text/javascript" >
          functionloadScript(url, callback) {
              var script = document.createElement("script")
              script.type = "text/javascript";
              if(script.readyState) { //IE
                  script.onreadystatechange = function() {
                      if (script.readyState == "loaded" ||
                          script.readyState == "complete") {
                          script.onreadystatechange = null;
                          callback();
                      }
                  };
              } else { //Others
                  script.onload = function() {
                      callback();
                  };
              }
              script.src = url;
              document.getElementsByTagName_r("head")[0].appendChild(script);
          }
      loadScript("the-rest.js", function() {
          Application.init();
      });
      </script>
  • </ul>

    數據訪問

    • 避免with

      當代碼流執行到一個 with 表達式時,運行期上下文的作用域鏈被臨時改變了。一個新的可變對象將被
        創建,它包含指定對象的所有屬性。此對象被插入到作用域鏈的前端,意味著現在函數的所有局部變量都
        被推入第二個作用域鏈對象中,所以訪問代價更高了(參見下圖)。

      通過將 document 對象傳遞給 with 表達式,一個新的可變對象容納了 document 對象的所有屬性,被插入到作用域鏈的前端。這使得訪問 document 的屬性非常快,但是訪問局部變量的速度卻變慢了,例如 bd 變量。正因為這個原因,最好不要使用 with 表達式。正如前面提到的,只要簡單地將 document 存儲在一個 局部變量中,就可以獲得性能上的提升。</code></pre> </li> </ul>

      此處輸入圖片的描述

      • 避免try-catch

        在 JavaScript 中不只是 with 表達式人為地改變運行期上下文的作用域鏈,try-catch 表達式的 catch 子句
          具有相同效果。當 try 塊發生錯誤時,程序流程自動轉入 catch 塊,并將異常對象推入作用域鏈前端的一個
          可變對象中。在 catch 塊中,函數的所有局部變量現在被放在第二個作用域鏈對象中。例如:

        try { methodThatMightCauseAnError(); } catch (ex){ alert(ex.message); //scope chain is augmented here }</code></pre> </li>

      • 如果需要多次訪問過深的作用域鏈和原型鏈,訪問的內容不會變化時,那應該把需要訪問的值賦給局部變量

        在 JavaScript 中,數據存儲位置可以對代碼整體性能產生重要影響。有四種數據訪問類型:直接量,變量,數組項,對象成員。它們有不同的性能考慮。

        直接量和局部變量訪問速度非常快,數組項和對象成員需要更長時間。

        局部變量比域外變量快,因為它位于作用域鏈的第一個對象中。變量在作用域鏈中的位置越深,訪問所需的時間就越長。全局變量總是最慢的,因為它們總是位于作用域鏈的最后一環。</code></pre> </li> </ul>

        Dom 編程

        這對性能意味著什么呢?簡單說來,兩個獨立的部分以功能接口連接就會帶來性能損耗。一個很形象的比喻是把 DOM 看成一個島嶼,把 JavaScript(ECMAScript)看成另一個島嶼,兩者之間以一座收費橋連

        接(參見 John Hrvatin,微軟,MIX09, http://videos.visitmix.com/MIX09/T53F)。每次 ECMAScript 需要訪問DOM時,你需要過橋,交一次“過橋費”。你操作 DOM次數越多,費用就越高。一般的建議是盡量減少過橋次數,努力停留在 ECMAScript 島上。

        • 最小化 DOM 訪問,在 JavaScript 端做盡可能多的事情。

        • 在反復訪問的地方使用局部變量存放 DOM 引用.

        • 小心地處理 HTML 集合,因為他們表現出“存在性”,總是對底層文檔重新查詢。將集合的 length 屬性緩存到一個變量中,在迭代中使用這個變量。如果經常操作這個集合,可以將集合拷貝到數組中

        • 如果可能的話,使用速度更快的 API,諸如 querySelectorAll()和 firstElementChild、querySelector()等css選擇器

        • 注意重繪和重排版;批量修改風格,離線操作 DOM 樹,緩存并減少對布局信息的訪問。

        • 動畫中使用絕對坐標,使用拖放代理。

        • 使用事件托管技術最小化事件句柄數量。

        算法與流程控制

        正如其他編程語言,代碼的寫法和算法選用影響 JavaScript 的運行時間。與其他編程語言不同的是,JavaScript 可用資源有限,所以優化技術更為重要。

        • for,while,do-while 循環的性能特性相似,誰也不比誰更快或更慢。除非你要迭代遍歷一個屬性未知的對象,否則不要使用 for-in 循環。

        • 當判斷條件較多時,查表法比 if-else 或者 switch 更快。

        • 如果你遇到一個棧溢出錯誤,將方法修改為一個迭代算法或者使用制表法可以避免重復工作。

        • 當遇到遞歸時,如果有一些重復性的返回,就應該用緩存cache存儲重復的返回

        字符串與正則表達式

        • 字符串

          當連接數量巨大或尺寸巨大的字符串時,數組聯合是 IE7 和它的早期版本上唯一具有合理性能的方法。如果你不關心 IE7 和它的早期版本,數組聯合是連接字符串最慢的方法之一。使用簡單的+和+=取而代之,可避免(產生)不必要的中間字符串。
        • 正則表達式

          正則表達式并不總是完成工作的最佳工具,尤其當你只是搜索一個文本字符串時。

        響應接口

        • UI 線程

          大多數瀏覽器有一個單獨的處理進程,它由兩個任務所
            共享:JavaScript 任務和用戶界面更新任務。每個時刻只有其中的一個操作得以執行,也就是說當 JavaScript
            代碼運行時用戶界面不能對輸入產生反應,反之亦然。或者說,當 JavaScript 運行時,用戶界面就被“鎖定”
            了。管理好 JavaScript 運行時間對網頁應用的性能很重要

          JavaScript 和 UI 更新共享的進程通常被稱作瀏覽器 UI 線程(雖然對所有瀏覽器來說“線程”一詞不一定準確)。此 UI 線程圍繞著一個簡單的隊列系統工作,任務被保存到隊列中直至進程空閑。一旦空閑,隊列中的下一個任務將被檢索和運行。這些任務不是運行 JavaScript 代碼,就是執行 UI更新,包括重繪和重排版(在第三章討論過)。此進程中最令人感興趣的部分是每次輸入均導致一個或多個任務被加入隊列。</code></pre> </li>

        • JS最佳運行時間是100ms以內

          如果該接口在 100毫秒內響應用戶輸入,用戶認為自己是“直接操作用戶界面中的對象。”超過 100毫秒意味著用戶認為自己與接口斷開了。由于 UI 在JavaScript 運行時無法更新,如果運行時間長于 100 毫秒,用戶就不能感受到對接口的控制
        • 定時器可用于安排代碼推遲執行,它使得你可以將長運行腳本分解成一系列較小的任務。

        • </ul>

          Ajax 異步JavaScript 和 XML

          Ajax 是高性能 JavaScript 的基石。它可以通過延遲下載大量資源使頁面加載更快。它通過在客戶端和服務器之間異步傳送數據,避免頁面集體加載。它還用于在一次 HTTP 請求中獲取整個頁面的資源。通過選擇正確的傳輸技術和最有效的數據格式,你可以顯著改善用戶與網站之間的互動。

          • 高性能 Ajax 包括:知道你項目的具體需求,選擇正確的數據格式和與之相配的傳輸技術。

            作為數據格式,純文本和 HTML 是高度限制的,但它們可節省客戶端的 CPU 周期。XML 被廣泛應用普遍支持,但它非常冗長且解析緩慢。JSON 是輕量級的,解析迅速(作為本地代碼而不是字符串),交互性與 XML 相當。字符分隔的自定義格式非常輕量,在大量數據集解析時速度最快,但需要編寫額外的程序在服務器端構造格式,并在客戶端解析。
          • 減少請求數量,可通過 JavaScript 和 CSS 文件打包,或者使用 MXHR。

          • 縮短頁面的加載時間,在頁面其它內容加載之后,使用 Ajax 獲取少量重要文件。

          編程實踐

          • 給 setTimeout()和 setInterval()傳遞函數參數而不是字符串參數

          • 創建新對象和數組時使用對象直接量和數組直接量。它們比非直接量形式創建和初始化更快

          • 避免重復進行相同工作。當需要檢測瀏覽器時,使用延遲加載或條件預加載

          • 原生方法總是比 JavaScript 寫的東西要快。盡量使用原生方法

          創建部署

          開發和部署過程對基于JavaScript的應用程序可以產生巨大影響,最重要的幾個步驟如下:

          • 合并 JavaScript 文件,減少 HTTP 請求的數量

          • 壓縮JavaScript 文件

          • 以壓縮形式提供 JavaScript 文件(gzip 編碼)

          • 通過設置 HTTP 響應報文頭使 JavaScript 文件可緩存,通過向文件名附加時間戳解決緩存問題

          • 使用內容傳遞網絡(CDN)提供 JavaScript 文件,CDN不僅可以提高性能,它還可以為你管理壓縮和緩存

           

          來自:http://www.jianshu.com/p/272f8c6c4c29

           

 本文由用戶 whywhom 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!