如何提升JavaScript Web效能的技巧總結
Javascript 效能最佳化絕不是一種書面的技術,Nicholas 的技術演進列出了10個建議,幫助你寫出高效的JS 程式碼
1. 定義局部變數
當一個變數被引用的時候,JavaScript會在作用域鏈中的不同成員中尋找這個變數。作用域鏈指的是目前作用於下可用變數的集合,它在各種主流瀏覽器中至少包含兩個部分:局部變數的集合和全域變數的集合。
簡單來說,如果JavaScript引擎在作用域鏈中搜尋的深度越大,那麼操作就會消耗更多的時間。引擎先從 this 開始尋找局部變量,然後是函數參數、本地定義的變量,最後遍歷所有的全域變數。
因為局部變數在這條鏈的起端,所以尋找局部變數總是比尋找全域變數要塊。所以當你想要不只一次地使用一個全域變數的時候,你應該將它定義成局部變量,就像這樣:
var blah = document.getElementById('myID'), blah2 = document.getElementById('myID2');
改寫成
var doc = document, blah = doc.getElementById('myID'), blah2 = doc.getElementById('myID2');
2. 不要使用with() 語句
這是因為with() 語句將會在作用域鏈的開始加入額外的變數。額外的變數意味著,當任何變數需要被存取的時候,JavaScript引擎都需要先掃描with()語句產生的變量,然後才是局部變量,最後是全域變數。
So with() essentially gives local variables all the performance drawbacks of global ones, and in turn derails Javascript optimization. 因此with()語句同時給局部變數和全域變數的效能帶來負面影響,最終使我們優化JavaScriptScript性能的計劃破產。
3. 小心使用閉包
#雖然你可能還不知道“閉包”,但你可能在不經意間經常使用這項技術。閉包基本上被認為是JavaScript中的new,當我們定義一個即時函數的時候,我們就使用了閉包,例如:
document.getElementById('foo').onclick = function(ev) { };
閉包的問題在於:根據定義,在它們的作用域鏈中至少有三個物件:閉包變數、局部變數和全域變數。這些額外的物件將會導致第1和第2個建議中提到的效能問題。
但我認為Nicholas並不是要我們因噎廢食,閉包對於提高程式碼可讀性等方面還是非常有用的,只是不要濫用它們(尤其在循環中)。
4. 物件屬性和陣列元素的速度都比變數慢
談到JavaScript的數據,一般來說有4種存取方式:數值、變數、物件屬性和數組元素。在考慮最佳化時,數值和變數的效能差不多,且速度顯著優於物件屬性和陣列元素。
因此當你多次引用一個物件屬性或陣列元素的時候,你可以透過定義一個變數來獲得效能提升。 (這一條在讀取、寫入資料時都有效)
雖然這條規則在絕大多數情況下是正確的,但是Firefox在優化數組索引上做了一些有意思的工作,能夠讓它的實際性能優於變數。但考慮到數組元素在其他瀏覽器上的效能弊端,還是應該盡量避免數組查找,除非你真的只針對於火狐瀏覽器的效能而進行開發。
5. 不要在陣列中挖得太深
另外,程式設計師應該避免在陣列中挖得太深,因為進入的層數越多,操作速度就越慢。
簡單地說,在嵌套很多層的數組中操作很慢是因為數組元素的查找速度很慢。試想如果操作嵌套三層的陣列元素,就要執行三次組元素查找,而不是一次。
因此如果你不斷地引用 foo.bar, 你可以透過定義 var bar = foo.bar 來提高效能。
6. 避免 for-in 迴圈(和基於函數的迭代)
這是另一個非常教條的建議:不要使用for-in迴圈。
這背後的邏輯非常直接:要遍歷一個集合內的元素,你可以使用諸如for循環、或者do-while循環來替代for-in循環,for-in循環不僅僅可能需要遍歷額外的數組項,還需要更多的時間。
為了遍歷這些元素,JavaScript需要為每個元素建立一個函數,而這個基於函數的迭代帶來了一系列效能問題:額外的函數引入了函數物件被建立和銷毀的上下文,將會在作用域鏈的頂端增加額外的元素。
7. 在迴圈時將控制條件和控制變數合併起來
提到效能,在迴圈中需要避免的工作一直是個熱門話題,因為迴圈會被重複執行很多次。所以如果有效能優化的需求,先對循環開刀有可能會得到最明顯的效能提升。
一種最佳化迴圈的方法是在定義迴圈的時候,將控制條件和控制變數合併起來,以下是一個沒有將他們合併起來的例子:
for ( var x = 0; x < 10; x++ ) { };
當我們要添加什麼東西到這個循環之前,我們發現有幾個操作在每次迭代都會出現。 JavaScript引擎需要:
#1:检查 x 是否存在
#2:检查 x 是否小于 0 (译者注:我猜这里是作者的笔误)
#3:使 x 增加 1
然而如果你只是迭代元素中的一些元素,那么你可以使用while循环进行轮转来替代上面这种操作:
var x = 9; do { } while( x-- );
如果你想更深入地了解循环的性能,Zakas提供了一种高级的循环优化技巧,使用异步进行循环(碉堡了!)
8. 为HTML集合对象定义数组
JavaScript使用了大量的HTML集合对象,比如 document.forms,document.images 等等。通常他们被诸如 getElementsByTagName、getElementByClassName 等方法调用。
由于大量的DOM selection操作,HTML集合对象相当的慢,而且还会带来很多额外的问题。正如DOM标准中所定义的那样:“HTML集合是一个虚拟存在,意味着当底层文档被改变时,它们将自动更新。”这太可怕了!
尽管集合对象看起来跟数组很像,他们在某些地方却区别很大,比如对于特定查询的结果。当对象被访问进行读写时,查询需要重新执行来更新所有与对象相关的组分,比如 length。
HTML集合对象也非常的慢,Nicholas说好像在看球的时候对一个小动作进行60倍速慢放。另外,集合对象也有可能造成死循环,比如下面的例子:
var ps = document.getElementsByTagName('p'); for (var i=0; i < ps.length; i++ ) { var p = document.createElement("p"); document.appendChild(p); }
这段代码造成了死循环,因为 ps 表示一个实时的HTML集合,并不是你所期望的数组。这种实时的集合在添加
标签时被更新,所以i < p.length 永远都不会结束。
解决这个问题的方法是将这些元素定义成数组,相比只设置 var ps = document.getElementsByTagName(‘p') 稍微有点麻烦,下面是Zakas提供的强制使用数组的代码:
function array(items) { try { return Array.prototype.concat.call(items); } catch (ex) { var i = 0, len = items.length, result = Array(len); while (i < len) { result[i] = items[i]; i++; } return result; } } var ps = array( document.getElementsByTagName('p') ); for (var i=0l i < ps.length; i++ ) { var p = document.createElement("p"); document.appendChild(p); }
9. 不要碰DOM!
不使用DOM是JavaScript优化中另一个很大的话题。经典的例子是添加一系列的列表项:如果你把每个列表项分别加到DOM中,肯定会比一次性加入所有列表项到DOM中要慢。这是因为DOM操作开销很大。
Zakas对这个进行了细致的讲解,解释了由于回流(reflow)的存在,DOM操作是非常消耗资源的。回流通常被理解为浏览器重新选渲染DOM树的处理过程。比如说,如果你用JavaScript语句改变了一个p的宽度,浏览器需要重绘页面来适应变化。
任何时候只要有元素被添加到DOM树或者从DOM树移除,都会引发回流。使用一个非常方便的JavaScript对象可以解决这个问题——documentFragment,我并没有使用过,但是在Steve Souders也表示同意这种做法之后我感觉更加肯定了。
DocumentFragment 基本上是一种浏览器以非可视方式实现的类似文档的片段,非可视化的表现形式带来了很多优点,最主要的是你可以在 documentFragment 中添加任何结点而不会引起浏览器回流。
10. 修改CSS类,而不是样式
你也许听说过:修改CSS类必直接修改样式会更高效。这归结于回流带来的另一个问题:当布局样式发生改变时,会引发回流。
布局样式意味着任何影响改变布局的变化都会强制引起浏览器回流。比如宽度、高度、字号、浮动等。
但是别误会我的意思,CSS类并不会避免回流,但是可以将它的影响最小化。相比每次修改样式都会引起回流,使用CSS类一次修改多个样式,只需要承担一次回流带来的消耗。
因此在修改多个布局样式的时候,使用CSS类来优化性能是明智的选择。另外如果你需要在运行时定义很多歌CSS类,在DOM上添加样式结点也是不错的选择。
以上是如何提升JavaScript Web效能的技巧總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

人臉偵測辨識技術已經是一個比較成熟且應用廣泛的技術。而目前最廣泛的網路應用語言非JS莫屬,在Web前端實現人臉偵測辨識相比後端的人臉辨識有優勢也有弱勢。優點包括減少網路互動、即時識別,大大縮短了使用者等待時間,提高了使用者體驗;弱勢是:受到模型大小限制,其中準確率也有限。如何在web端使用js實現人臉偵測呢?為了實現Web端人臉識別,需要熟悉相關的程式語言和技術,如JavaScript、HTML、CSS、WebRTC等。同時也需要掌握相關的電腦視覺和人工智慧技術。值得注意的是,由於Web端的計

隨著網路金融的快速發展,股票投資已經成為了越來越多人的選擇。而在股票交易中,蠟燭圖是常用的技術分析方法,它能夠顯示股票價格的變動趨勢,幫助投資人做出更精準的決策。本文將透過介紹PHP和JS的開發技巧,帶領讀者了解如何繪製股票蠟燭圖,並提供具體的程式碼範例。一、了解股票蠟燭圖在介紹如何繪製股票蠟燭圖之前,我們首先需要先了解什麼是蠟燭圖。蠟燭圖是由日本人

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

js和vue的關係:1、JS作為Web開發基石;2、Vue.js作為前端框架的崛起;3、JS與Vue的互補關係;4、JS與Vue的實踐應用。

Cockpit是一個面向Linux伺服器的基於Web的圖形介面。它主要是為了使新用戶/專家用戶更容易管理Linux伺服器。在本文中,我們將討論Cockpit存取模式以及如何從CockpitWebUI切換Cockpit的管理存取。內容主題:駕駛艙進入模式查找當前駕駛艙訪問模式從CockpitWebUI啟用Cockpit的管理訪問從CockpitWebUI禁用Cockpit的管理訪問結論駕駛艙進入模式駕駛艙有兩種訪問模式:受限訪問:這是駕駛艙的默認訪問模式。在這種存取模式下,您無法從駕駛艙Web用戶

web是全球廣域網,也稱為萬維網,是互聯網的一種應用形式。 Web 是一種基於超文本和超媒體的資訊系統,它允許使用者透過超連結在不同的網頁之間跳轉,從而瀏覽和獲取資訊。 Web 的基礎是互聯網,它使用統一、標準化的協定和語言,使得不同電腦之間能夠進行資料交換和資訊共享。

JavaScript中的HTTP狀態碼取得方法簡介:在進行前端開發中,我們常常需要處理與後端介面的交互,而HTTP狀態碼就是其中非常重要的一部分。了解並取得HTTP狀態碼有助於我們更好地處理介面傳回的資料。本文將介紹使用JavaScript取得HTTP狀態碼的方法,並提供具體程式碼範例。一、什麼是HTTP狀態碼HTTP狀態碼是指當瀏覽器向伺服器發起請求時,服務

PHP在Web開發中是屬於後端。 PHP是一種伺服器端腳本語言,主要用於處理伺服器端的邏輯,產生動態網頁內容。與前端技術相比,PHP更多地用於與資料庫互動、處理使用者請求以及生成頁面內容等後端操作。接下來透過具體的程式碼範例來說明PHP在後端開發中的應用。首先,我們來看一個簡單的PHP程式碼範例,用於連接資料庫並查詢資料:
