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