摘要:簡(jiǎn)化終止條件上述情況,我們也可以不使用減值迭代,即像上文提到過(guò)的,在初始化時(shí)即將迭代長(zhǎng)度賦值給一個(gè)局部變量。優(yōu)化操作操作是最拖累性能的一方面,優(yōu)化操作可以顯著提高性能。
Javascript最初是解釋型語(yǔ)言,現(xiàn)在,主流瀏覽器內(nèi)置的Javascript引擎基本上都實(shí)現(xiàn)了Javascript的編譯執(zhí)行,即使如此,我們?nèi)孕枰獌?yōu)化自己寫的Javascript代碼,以獲得最佳性能。
注意作用域 避免全局作用域在之前的文章Javascript 變量、作用域和內(nèi)存問題提到過(guò),由于訪問變量需要在作用域鏈上進(jìn)行查找,相比于局部變量,訪問全局變量的開銷更大,因此以下代碼:
var person = { name: "Sue", hobbies: ["Yoga", "Jogging"] }; function hobby() { for(let i=0; i可以進(jìn)行如下優(yōu)化:
function hobby() { let hobbies = person.hobbies; for(let i=0; i把需要頻繁訪問的全局變量賦值到局部變量中,可以減小查找深度,進(jìn)而優(yōu)化性能。
避免使用with
當(dāng)然,上述優(yōu)化過(guò)的代碼仍然有不足的地方,后面的部分會(huì)提到。為什么避免使用with?
with并不是必須的,使用局部變量可以達(dá)到同樣的目的
with創(chuàng)建了自己的作用域,相當(dāng)于增加了作用域內(nèi)部查找變量的深度
舉一個(gè)例子:
function test() { var innerW = ""; var outerW = ""; with(window) { innerW = innerWidth; outerW = outerWidth; } return "Inner W: " + innerW + ", Outer W: " + outerW; } test() // "Inner W: 780, Outer W: 795"上述代碼中,with作用域減小了對(duì)全局變量window的查找深度,不過(guò)與此同時(shí),也增加了作用域中局部變量innerW和outerW的查找深度,功過(guò)相抵。
因此我們不如使用局部變量替代with:function test() { var w = window; var innerW = w.innerWidth; var outerW = w.outerWidth; return "Inner W: " + innerW + ", Outer W: " + outerW; }上述代碼仍然不是最優(yōu)的。
算法復(fù)雜度一下表格列出了幾種算法復(fù)雜度:
O(1)
復(fù)雜度 名稱 描述 O(1) 常數(shù) 無(wú)論多少值,執(zhí)行時(shí)間恒定,比如使用簡(jiǎn)單值或訪問存貯在變量中的值 O(lg n) 對(duì)數(shù) 總執(zhí)行時(shí)間與值的數(shù)量相關(guān),但不一定需要遍歷每一個(gè)值 O(n) 線性 總執(zhí)行時(shí)間與值的數(shù)量線性相關(guān) O(n2) 平方 總執(zhí)行時(shí)間與值的數(shù)量相關(guān),每個(gè)值要獲取n次 如果我們直接使用字面量,或者訪問保存在變量中的值,時(shí)間復(fù)雜度為O(1),比如:
var value = 5; var sum = 10 + value;上述代碼進(jìn)行了三次常量查找,分別是5,10,value,這段代碼整體復(fù)雜度為O(1)
訪問數(shù)組也是時(shí)間復(fù)雜度為O(1)的操作,以下代碼整體復(fù)雜度為O(1):var values = [1, 2]; var sum = values[0] + values[1];避免不必要的屬性查找在對(duì)象上訪問屬性是一個(gè)O(n)的操作,Javascript 面向?qū)ο蟮某绦蛟O(shè)計(jì)(原型鏈與繼承)文中提到過(guò),訪問對(duì)象中的屬性時(shí),需要沿著原型鏈追溯查找,屬性查找越多,執(zhí)行時(shí)間越長(zhǎng),比如:
var persons = ["Sue", "Jane", "Ben"]; for(let i=0; i上述代碼中,每次循環(huán)都會(huì)比較i
,為了避免頻繁的屬性查找,可以進(jìn)行如下優(yōu)化: var persons = ["Sue", "Jane", "Ben"]; for(let i=0, len = persons.length; i即如果循環(huán)長(zhǎng)度在循環(huán)開始時(shí)即可確定,就將要循環(huán)的長(zhǎng)度在初始化的時(shí)候聲明為一個(gè)局部變量。
優(yōu)化循環(huán)由于循環(huán)時(shí)反復(fù)執(zhí)行的代碼,動(dòng)輒上百次,因此優(yōu)化循環(huán)時(shí)性能優(yōu)化中很重要的部分。
減值迭代
為什么要進(jìn)行減值迭代,我們比較如下兩個(gè)循環(huán):
var nums = [1, 2, 3, 4]; for(let i=0; i for(let i=nums.length-1; i>-1; i--) { console.log(nums[i]); }二者有如下區(qū)別:
迭代順序不同
前者支持動(dòng)態(tài)增減數(shù)組元素,后者不支持
后者性能優(yōu)于前者,前者每次循環(huán)都會(huì)計(jì)算nums.length,頻繁的屬性查找降低性能
因此,出于性能的考慮,如果不在乎順序,迭代長(zhǎng)度初始即可確定,使用減值迭代更優(yōu)。
簡(jiǎn)化終止條件
上述情況,我們也可以不使用減值迭代,即像上文提到過(guò)的,在初始化時(shí)即將迭代長(zhǎng)度賦值給一個(gè)局部變量。
簡(jiǎn)化循環(huán)體
循環(huán)體應(yīng)最大程度地被優(yōu)化,避免進(jìn)行不必要的密集的計(jì)算
使用while循環(huán)
為什么使用while循環(huán),我們可以比較如下兩個(gè)循環(huán):
var len = nums.length; for(let i=0; i整合冒泡事件處理 var i = nums.length ; while(--len > -1) { console.log(nums[len]); }以上兩個(gè)循環(huán)有一個(gè)很明顯的不同點(diǎn):while循環(huán)將每次循環(huán)終止條件的判斷和index的自增合并為一個(gè)語(yǔ)句,在后續(xù)部分會(huì)講解語(yǔ)句數(shù)量與性能優(yōu)化的關(guān)系。
展開循環(huán)由于建立循環(huán)和處理終止條件需要額外的開銷,因此如果循環(huán)次數(shù)比較少,而且可以確定,我們可以將其展開,比如:
process(nums[0]); process(nums[1]);如果迭代次數(shù)不能事先確定,可以使用Duff裝置,其中比較著名的是Andrew B. King提出的一種Duff技術(shù),通過(guò)計(jì)算迭代次數(shù)是否為8的倍數(shù)將循環(huán)展開,將“零頭”與“整數(shù)”分成兩個(gè)多帶帶的do-while循環(huán),在處理大數(shù)據(jù)集時(shí)優(yōu)化效果顯著:
var iterations = Math.floor(values.length / 8); var leftover = values.length % 8; var i = 0; if (leftover > 0){ do { process(values[i++]); } while (--leftover > 0); } do { process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); } while (--iterations > 0);避免雙重解釋eval() Function() setTimeout()可以傳入字符串,Javascript引擎會(huì)將其解析成可以執(zhí)行的代碼,意味著,Javascript執(zhí)行到這里需要額外開一個(gè)解釋器來(lái)解析字符串,會(huì)明顯降低性能,因此:
盡量避免使用eval()
避免使用Function構(gòu)造函數(shù),用一般function來(lái)代替
setTimeout()傳入函數(shù)作為參數(shù)
其他使用原生方法
原生方法都是用C/C++之類的編譯語(yǔ)言寫出來(lái)的,比Javascript快得多。
使用switch語(yǔ)句
多個(gè)if-else可以轉(zhuǎn)換為switch語(yǔ)句,還可以按照最可能到最不可能排序case
使用位運(yùn)算符
當(dāng)進(jìn)行數(shù)學(xué)運(yùn)算的時(shí)候,位運(yùn)算操作要比任何布爾運(yùn)算或者算數(shù)運(yùn)算快。選擇性地用位運(yùn)算替換算數(shù)運(yùn)算可以極大提升復(fù)雜計(jì)算的性能。諸如取模,邏輯與和邏輯或都可
以考慮用位運(yùn)算來(lái)替換。書中的這段話筆者表示不能理解,由于使用&& ||做邏輯判斷時(shí),有的時(shí)候只需要求得第一個(gè)表達(dá)式的結(jié)果便可以結(jié)束運(yùn)算,而& |無(wú)論如何都要求得兩個(gè)表達(dá)式的結(jié)果才可以結(jié)束運(yùn)算,因此后者的性能沒有占太大優(yōu)勢(shì)。
這里,補(bǔ)充一下位運(yùn)算符如何發(fā)揮邏輯運(yùn)算符的功能,首先看幾個(gè)例子:7 === 7 & 6 === 6 1 7 === 7 & 5 === 4 0 7 === 7 | 6 ===6 1 7 === 7 | 7 ===6 1 7 === 6 | 6 === 5 0也許你會(huì)恍然大悟,位運(yùn)算符并沒有產(chǎn)生true 或 false,它只是利用了Number(true) === 1 Number(false) === 0 Boolean(1) === true Boolean(0) === false。
最小化語(yǔ)句數(shù)Javascript代碼中的語(yǔ)句數(shù)量會(huì)影響執(zhí)行的速度,盡量組合語(yǔ)句,可以減少腳本的執(zhí)行時(shí)間。
多個(gè)變量聲明當(dāng)我們需要聲明多個(gè)變量,比如:
var name = ""; var age = 18; var hobbies = [];可以做如下優(yōu)化:
var name = "", age = 18, hobbies = [];合并迭代值上文中我們提到一個(gè)例子,使用while循環(huán)可以合并自減和判斷終止條件,我們還可以換一種寫法:
var i = nums.length ; while(len > -1) { console.log(nums[len--]); }即將自減與使用index取值合并為一個(gè)語(yǔ)句。
使用字面量創(chuàng)建數(shù)組和對(duì)象即將如下代碼:
var array = new Array(); array[0] = 1; array[1] = 2; var person = new Object(); person.name = "Sue"; person.age = 18;替換成:
var array = [1, 2]; var person = { name:"Sue", age:18 };省了4行代碼。
優(yōu)化DOM操作DOM操作是最拖累性能的一方面,優(yōu)化DOM操作可以顯著提高性能。
最小化現(xiàn)場(chǎng)更新的次數(shù)如果我們要修改的DOM已經(jīng)顯示在頁(yè)面,那么我們就是在做現(xiàn)場(chǎng)更新,由于每次更新瀏覽器都要重新計(jì)算,重新渲染,非常消耗性能,因此我們應(yīng)該最小化現(xiàn)場(chǎng)更新的次數(shù),比如我們要向頁(yè)面添加一個(gè)列表:
var body = document.getElementsByTagName("body")[0]; for(let i=0; i<10; i++) { item = document.createElement("span"); body.appendChild(item); item.appendChild(document.createTextNode("Item" + i)); }每次循環(huán)時(shí)都會(huì)進(jìn)行兩次現(xiàn)場(chǎng)更新,添加div,為div添加文字,總共需要20次現(xiàn)場(chǎng)更新,頁(yè)面要重繪20次。
現(xiàn)場(chǎng)更新的性能瓶頸不在于更新的大小,而在于更新的次數(shù),因此,我們可以將所有的更新一次繪制到頁(yè)面上,有以下兩個(gè)方法:文檔片段
可以使用文檔片段先收集好要添加的元素,最后在父節(jié)點(diǎn)上調(diào)用appendChild()將片段的子節(jié)點(diǎn)添加到父節(jié)點(diǎn)中,注意,片段本身不會(huì)被添加。
thisinnerHTML
使用innerHTML與使用諸如createElement() appendChild()方法有一個(gè)顯著的區(qū)別,前者使用內(nèi)部的DOM來(lái)創(chuàng)建DOM結(jié)構(gòu),后者使用JavaScript的DOM來(lái)創(chuàng)建DOM結(jié)構(gòu),前者要快得多,之前的例子用innerHTML改寫為:
var ul = document.getElementById("ul"), innerHTML = ""; for(let i=0; i<10; i++) { innerHTML += "Item " + i + " "; } ul.innerHTML = innerHTML;頁(yè)面上的事件處理程序數(shù)量與頁(yè)面相應(yīng)用戶交互的速度之間存在負(fù)相關(guān),具體原因有多方面:
創(chuàng)建函數(shù)會(huì)占用內(nèi)存
綁定事件處理方法時(shí),需要訪問DOM
因此對(duì)于冒泡事件,盡可能由父元素甚至祖先元素代子元素處理,這樣一個(gè)事件處理方法可以負(fù)責(zé)多個(gè)目標(biāo)的事件處理,比如:
注意HTMLCollectionthis訪問HTMLCollection的代價(jià)非常昂貴。
下面的每個(gè)項(xiàng)目(以及它們指定的屬性)都返回 HTMLCollection:Document (images, applets, links, forms, anchors)
form (elements)
map (areas)
select (options)
table (rows, tBodies)
tableSection (rows)
row (cells)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/52305.html
摘要:本文是圖說(shuō)系列文章的第五篇。這樣的話,使用的開發(fā)者也不需要做任何適配,但是它們卻能獲得更高性能。該圖并不是用來(lái)準(zhǔn)確的衡量其性能的。運(yùn)行編寫出高性能的代碼是可能的。這種清理工作由引擎自動(dòng)進(jìn)行,稱為垃圾回收。 本文是圖說(shuō) WebAssembly 系列文章的第五篇。如果您還未閱讀之前的文章,建議您從第一篇入手。 在上一篇文章中,我們說(shuō)到了使用 WebAssembly 和 JavaScript...
摘要:下面我們撇開網(wǎng)絡(luò)方面的優(yōu)化,只分析靜態(tài)資源方面的優(yōu)化。不過(guò),也會(huì)阻止的構(gòu)建和延緩網(wǎng)頁(yè)渲染。未優(yōu)化正常加載優(yōu)化后異步加載根據(jù)上面的分析,我們可以清楚的認(rèn)識(shí)到,非必要優(yōu)先加載的,選擇異步加載是最優(yōu)選擇。 為什么做優(yōu)化 經(jīng)典問題:白屏?xí)r間過(guò)長(zhǎng),用戶體驗(yàn)差產(chǎn)生的原因:網(wǎng)絡(luò)問題、關(guān)鍵渲染路徑(CRP)問題 怎么做優(yōu)化 如何做好優(yōu)化呢,網(wǎng)上隨便一搜,就有很多優(yōu)化總結(jié),無(wú)非就是網(wǎng)絡(luò)優(yōu)化、靜態(tài)資源(h...
摘要:下面我們撇開網(wǎng)絡(luò)方面的優(yōu)化,只分析靜態(tài)資源方面的優(yōu)化。不過(guò),也會(huì)阻止的構(gòu)建和延緩網(wǎng)頁(yè)渲染。未優(yōu)化正常加載優(yōu)化后異步加載根據(jù)上面的分析,我們可以清楚的認(rèn)識(shí)到,非必要優(yōu)先加載的,選擇異步加載是最優(yōu)選擇。 為什么做優(yōu)化 經(jīng)典問題:白屏?xí)r間過(guò)長(zhǎng),用戶體驗(yàn)差產(chǎn)生的原因:網(wǎng)絡(luò)問題、關(guān)鍵渲染路徑(CRP)問題 怎么做優(yōu)化 如何做好優(yōu)化呢,網(wǎng)上隨便一搜,就有很多優(yōu)化總結(jié),無(wú)非就是網(wǎng)絡(luò)優(yōu)化、靜態(tài)資源(h...
摘要:下面我們撇開網(wǎng)絡(luò)方面的優(yōu)化,只分析靜態(tài)資源方面的優(yōu)化。不過(guò),也會(huì)阻止的構(gòu)建和延緩網(wǎng)頁(yè)渲染。未優(yōu)化正常加載優(yōu)化后異步加載根據(jù)上面的分析,我們可以清楚的認(rèn)識(shí)到,非必要優(yōu)先加載的,選擇異步加載是最優(yōu)選擇。 為什么做優(yōu)化 經(jīng)典問題:白屏?xí)r間過(guò)長(zhǎng),用戶體驗(yàn)差產(chǎn)生的原因:網(wǎng)絡(luò)問題、關(guān)鍵渲染路徑(CRP)問題 怎么做優(yōu)化 如何做好優(yōu)化呢,網(wǎng)上隨便一搜,就有很多優(yōu)化總結(jié),無(wú)非就是網(wǎng)絡(luò)優(yōu)化、靜態(tài)資源(h...
摘要:插件性能優(yōu)化及個(gè)人常用優(yōu)化方法經(jīng)常會(huì)觸發(fā)視覺變化。作用域鏈指的是當(dāng)前作用于下可用變量的集合,它在各種主流瀏覽器中至少包含兩個(gè)部分局部變量的集合和全局變量的集合。在考慮優(yōu)化時(shí),數(shù)值和變量的性能差不多,并且速度顯著優(yōu)于對(duì)象屬性和數(shù)組元素。 JavaScript 插件性能優(yōu)化及個(gè)人react常用優(yōu)化方法 JavaScript 經(jīng)常會(huì)觸發(fā)視覺變化。有時(shí)是直接通過(guò)樣式操作,有時(shí)是會(huì)產(chǎn)生視覺變化...
閱讀 3633·2021-11-24 10:22
閱讀 3701·2021-11-22 09:34
閱讀 2503·2021-11-15 11:39
閱讀 1537·2021-10-14 09:42
閱讀 3672·2021-10-08 10:04
閱讀 1566·2019-08-30 15:52
閱讀 858·2019-08-30 13:49
閱讀 3028·2019-08-30 11:21