摘要:這樣優化后我們最多進行次判斷即可,大大提高了代碼的性能。表達式的值具有離散性,
個人博客,點擊查看目錄,喜歡可以關注一下.
1.從[]==![]為true來剖析JavaScript各種蛋疼的類型轉換
2.吹毛求疵的追求優雅高性能JavaScript
李小龍說過:"天下武功,無堅不摧,唯快不破".(真的說過嗎?)
我想說的是:"世間網站,完美體驗,唯快不破".(這個我承認我說過.)
俗話說,時間就是生命,時間就是金錢,時間就是一切,人人都不想把時間白白浪費,一個網站,最重要的就是體驗,而網站好不好最直觀的感受就是這個網站打開速度快不快,卡不卡.
當打開一個購物網站卡出翔,慢的要死,是不是如同心塞一樣的感受,藍瘦香菇,想買個心愛的寶貝都不能買,心里想這尼瑪什么玩意.
那么如何讓我們的網站給用戶最佳的體驗呢?大環境我們不說,什么網絡啊,瀏覽器性能啊,這些我們無法改變,我們能改變的就是我們碼農能創造的,那就是代碼的性能.代碼精簡,執行速度快,嵌套層數少等等都是我們可以著手優化注意的地方.
恰巧最近剛看完《高性能JavaScript》收獲頗豐,今天就以點帶面從追求高性能JavaScript的目的書寫的代碼來感受一下速度提升帶來的體驗,從編程實踐,代碼優化的角度總結一下自己平時遇到、書中以及其他地方看到有關提高JavaScript性能的例子,其他關于加載和執行,數據存取,瀏覽器中的DOM,算法和流程控制,字符串和正則表達式,快速響應的用戶界面,Ajax這些大范圍的方向這里就不多加闡述.我們從代碼本身出發,用數據說話,挖掘那些細思極恐的效率.有的提升可能微不足道,但是所有的微不足道聚集在一起就是一個從量到質變.
比較寬泛的闡釋高性能JavaScript,從大體上了解提高JavaScript性能的有幾個大方面,可以閱讀這兩篇文章作詳細了解:
高性能JavaScript讀書筆記.
高性能javascript小結
本文只從代碼和數據上闡述具體說明如何一步步提高JavaScript的性能.
Javascript是一門非常靈活的語言,我們可以隨心所欲的書寫各種風格的代碼,不同風格的代碼也必然也會導致執行效率的差異,作用域鏈、閉包、原型繼承、eval等特性,在提供各種神奇功能的同時也帶來了各種效率問題,用之不慎就會導致執行效率低下,開發過程中零零散散地接觸到許多提高代碼性能的方法,整理一下平時比較常見并且容易規避的問題。
算法和流程控制的優化 循壞你一天(一周)內寫了多少個循環了?
我們先以最簡單的循環入手作為切入點,這里我們只考慮單層循環以及比較不同循環種類和不同流程控制的效率.
測試數據:[1,2,3,...,10000000] 測試依據:數組[1,2,3,""",10000000]的累加所需要的時間 測試環境:node版本v6.9.4環境的v8引擎
為什么不在瀏覽器控制臺測試?
首先不同瀏覽器的不同版本性能可能就不一樣,這里為了統一,我選擇了node環境,為什么不選擇瀏覽器而選擇了node環境測試,這是因為瀏覽器的一部分原因.
因為用控制臺是測不出性能的,因為控制臺本質上是個套了一大堆安全機制的eval,它的沙盒化程度很高。這里我們就一個簡單的例子來對比一下,瀏覽器和node環境同樣的代碼的執行效率.
測試的數組代碼:
var n = 10000000; // 準備待測數組 var arr = []; for(var count=0;count就想簡單測試一下這段生成待測數組所消耗時間的對比:
這是最新谷歌瀏覽器控制臺的執行結果:
時間大約在28ms左右.
我們再來在node環境下測試一下所需要的時間:
時間穩定在7ms左右,大約3倍的差距,同樣都是v8引擎,瀏覽器就存在很明顯的差距,這是由于瀏覽器的機制有關,瀏覽器要處理的事情遠遠比單純在node環境下執行代碼處理的事情多,所以用瀏覽器測試性能沒有在單純地node環境下靠譜.
具體細節和討論可以參看知乎上的這篇RednaxelaFX的回答:
為何瀏覽器控制臺的JavaScript引擎性能這么差?各個循環實現的測試代碼,每個方法都會多帶帶執行,一起執行會有所偏差.
// for 測試(for和while其實差不多,這里我們只測試for循環) console.time("for"); for (var i = 0; i < arr.length; i++) { arr[i]; } console.timeEnd("for"); // for loop測試 console.time("for"); var sum = 0; for (var i = 0; i < arr.length; i++) { sum += arr[i]; } console.timeEnd("for"); // for loop緩存測試 console.time("for cache"); var sum = 0; var len = arr.length; for (var i = 0; i < len; i++) { sum += arr[i]; } console.timeEnd("for cache"); // for loop倒序測試 console.time("for reverse"); var sum = 0; var len = arr.length; for (i = len-1;i>0; i--) { sum += arr[i]; } console.timeEnd("for reverse"); //forEach測試 console.time("forEach"); var sum = 0; arr.forEach(function(ele) { sum += ele; }) //這段代碼看起來更加簡潔,但這種方法也有一個小缺陷:你不能使用break語句中斷循環,也不能使用return語句返回 到外層函數。 console.timeEnd("forEach"); //ES6的for of測試 console.time("for of"); var sum = 0; for (let i of arr) { sum += i; } console.timeEnd("for of"); //這是最簡潔、最直接的遍歷數組元素的語法 //這個方法避開了for-in循環的所有缺陷 //與forEach()不同的是,它可以正確響應break、continue和return語句 // for in 測試 console.time("for in"); var sum=0; for(var i in arr){ sum+=arr[i]; } console.timeEnd("for in"); 這是最簡潔、最直接的遍歷數組元素的語法 這個方法避開了for-in循環的所有缺陷 與forEach()不同的是,它可以正確響應break、continue和return語句最后在node環境下各自所花費的不同時間:
循環類型 耗費時間(ms) for 約11.998 for cache 約10.866 for 倒序 約11.230 forEach 約400.245 for in 約2930.118 for of 約320.921 從上面的表格統計比較可以看出,前三種原始的for循壞一個檔次,然后forEach和for of也基本屬于一個檔次,for of的執行速度稍微高于forEach,最后最慢的就是for in循環了,差的不是幾十倍的關系了.
看起來好像是那么回事哦!
同樣是循壞為什么會有如此大的懸殊呢?我們來稍微分析一下其中的個別緣由導致這樣的差異.
for in 一般是用在對象屬性名的遍歷上的,由于每次迭代操作會同時搜索實例本身的屬性以及原型鏈上的屬性,所以效率肯定低下.
for...in 實際上效率是最低的。這是因為 for...in 有一些特殊的要求,具體包括:
1. 遍歷所有屬性,不僅是 ownproperties 也包括原型鏈上的所有屬性。 2. 忽略 enumerable 為 false 的屬性。 3. 必須按特定順序遍歷,先遍歷所有數字鍵,然后按照創建屬性的順序遍歷剩下的。這里既然扯到對象的遍歷屬性,就順便扯一扯幾種對象遍歷屬性不同區別,為什么for...in性能這么差,算是一個延伸吧,反正我發現寫一篇博客可以延伸很多東西,自己也可以學到很多,還可以鞏固自己之前學過但是遺忘的一些東西,算是溫故而知新.
遍歷數組屬性目前我知道的有:for-in循環、Object.keys()和Object.getOwnPropertyNames(),那么三種到底有啥區別呢?
for-in循環:會遍歷對象自身的屬性,以及原型屬性,包括enumerable 為 false(不可枚舉屬性); Object.keys():可以得到自身可枚舉的屬性,但得不到原型鏈上的屬性; Object.getOwnPropertyNames():可以得到自身所有的屬性(包括不可枚舉),但得不到原型鏈上的屬性,Symbols屬性 也得不到.Object.defineProperty顧名思義,就是用來定義對象屬性的,vue.js的雙向數據綁定主要在getter和setter函數里面插入一些處理方法,當對象被讀寫的時候處理方法就會被執行了。 關于這些方法和屬性的更具體解釋,可以看MDN上的解釋(戳我);
簡單看一個小demo例子加深理解,對于Object.defineProperty屬性不太明白,可以看看上面介紹的文檔學習補充一下.
"use strict"; class A { constructor() { this.name = "jawil"; } getName() {} } class B extends A { constructor() { super(); this.age = 22; } //getAge不可枚舉 getAge() {} [Symbol("fullName")]() { } } B.prototype.get = function() { } var b = new B(); //設置b對象的info屬性的enumerable: false,讓其不能枚舉. Object.defineProperty(b, "info", { value: 7, writable: true, configurable: true, enumerable: false }); //Object可以得到自身可枚舉的屬性,但得不到原型鏈上的屬性 console.log(Object.keys(b)); //[ "name", "age" ] //Object可A以得到自身所有的屬性(包括不可枚舉),但得不到原型鏈上的屬性,Symbols屬性也得不到 console.log(Object.getOwnPropertyNames(b)); //[ "name", "age", "info" ] for (var attr in b) { console.log(attr);//name,age,get } //in會遍歷對象自身的屬性,以及原型屬性 console.log("getName" in b); //true從這里也可以看出為什么for...in性能這么慢,因為它要遍歷自身的屬性和原型鏈上的屬性,這無疑就增加了所有不必要的額外開銷.
目前絕大部分開源軟件都會在for loop中緩存數組長度,因為普通觀點認為某些瀏覽器Array.length每次都會重新計算數組長度,因此通常用臨時變量來事先存儲數組長度,以此來提高性能.
而forEach是基于函數的迭代(需要特別注意的是所有版本的ie都不支持,如果需要可以用JQuery等庫),對每個數組項調用外部方法所帶來的開銷是速度慢的主要原因.
總結:
1.能用for緩存的方法循環就用for循壞,性能最高,寫起來繁雜; 2.不追求極致性能的情況下,建議使用forEach方法,干凈,簡單,易讀,短,沒有中間變量,沒有成堆的分號,簡單非常 優雅; 3.想嘗鮮使用ES6語法的話,不考慮兼容性情況下,推薦使用for of方法,這是最簡潔、最直接的遍歷數組元素的語法,該方 法避開了for-in;循環的所有缺陷與forEach()不同的是,它可以正確響應break、continue和return語句. 4.能避免for in循環盡量避免,太消費性能,太費時間,數組循環不推薦使用.條件語句常見的條件語句有if-else和switch-case,那么什么時候用if-else,什么時候用switch-case語句呢?
我們先來看個簡單的if-else語句的代碼:if (value == 0){ return result0; } else if (value == 1){ return result1; } else if (value == 2){ return result2; } else if (value == 3){ return result3; } else if (value == 4){ return result4; } else if (value == 5){ return result5; } else if (value == 6){ return result6; } else if (value == 7){ return result7; } else if (value == 8){ return result8; } else if (value == 9){ return result9; } else { return result10; }最壞的情況下(value=10)我們可能要做10次判斷才能返回正確的結果,那么我們怎么優化這段代碼呢?一個顯而易見的優化策略是將最可能的取值提前判斷,比如value最可能等于5或者10,那么將這兩條判斷提前。但是通常情況下我們并不知道(最可能的選擇),這時我們可以采取二叉樹查找策略進行性能優化。
if (value < 6){ if (value < 3){ if (value == 0){ return result0; } else if (value == 1){ return result1; } else { return result2; } } else { if (value == 3){ return result3; } else if (value == 4){ return result4; } else { return result5; } } } else { if (value < 8){ if (value == 6){ return result6; } else { return result7; } } else { if (value == 8){ return result8; } else if (value == 9){ return result9; } else { return result10; } } }這樣優化后我們最多進行4次判斷即可,大大提高了代碼的性能。這樣的優化思想有點類似二分查找,和二分查找相似的是,只有value值是連續的數字時才能進行這樣的優化。但是代碼這樣寫的話不利于維護,如果要增加一個條件,或者多個條件,就要重寫很多代碼,這時switch-case語句就有了用武之地。
將以上代碼用switch-case語句重寫:
switch(value){ case 0: return result0; case 1: return result1; case 2: return result2; case 3: return result3; case 4: return result4; case 5: return result5; case 6: return result6; case 7: return result7; case 8: return result8; case 9: return result9; default: return result10; }swtich-case語句讓代碼顯得可讀性更強,而且swtich-case語句還有一個好處是如果多個value值返回同一個結果,就不用重寫return那部分的代碼。一般來說,當case數達到一定數量時,swtich-case語句的效率是比if-else高的,因為switch-case采用了branch table(分支表)索引來進行優化,當然各瀏覽器的優化程度也不一樣。
相對來說,下面幾種情況更適合使用switch結構: 枚舉表達式的值。這種枚舉是可以期望的、平行邏輯關系的。 表達式的值具有離散性,不具有線性的非連續的區間值。 表達式的值是固定的,不是動態變化的。 表達式的值是有限的,而不是無限的,一般情況下表達式應該比較少。 表達式的值一般為整數、字符串等類型的數據。 而if結構則更適合下面的一些情況: 具有復雜的邏輯關系。 表達式的值具有線性特征,如對連續的區間值進行判斷。 表達式的值是動態的。 測試任意類型的數據。除了if-else和swtich-case外,我們還可以采用查找表。
var results = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10]; //return the correct result return results[value];當數據量很大的時候,查找表的效率通常要比if-else語句和swtich-case語句高,查找表能用數字和字符串作為索引,而如果是字符串的情況下,最好用對象來代替數組。當然查找表的使用是有局限性的,每個case對應的結果只能是一個取值而不能是一系列的操作。
從根源上分析if else 和 switch的效率,之前只知道if else 和switch算法實現不同,但具體怎么樣就不清楚了,為了刨根問底,翻閱了很多資料,但無奈找到了原因我還是不太懂,只知道就是這么回事,不懂匯編,懂底層匯編的大神還望多加指點.
想要深入從匯編的角度了解可以看看這篇文章:switch...case和if...else效率比較
學前端的我想問你匯編是啥?能吃嗎?
在選擇分支較多時,選用switch...case結構會提高程序的效率,但switch不足的地方在于只能處理字符或者數字類型的變量,if...else結構更加靈活一些,if...else結構可以用于判斷表達式是否成立,比如if(a+b>c),if...else的應用范圍更廣,switch...case結構在某些情況下可以替代if...else結構。
小結:1. 當只有兩個case或者case的value取值是一段連續的數字的時候,我們可以選擇if-else語句; 2. 當有3~10個case數并且case的value取值非線性的時候,我們可以選擇switch-case語句; 3. 當case數達到10個以上并且每次的結果只是一個取值而不是額外的JavaScript語句的時候,我們可以選擇查找表.事件委托減少循環綁定的事件什么是事件委托:通俗的講,事件就是onclick,onmouseover,onmouseout,等就是事件,委托呢,就是讓別人來做,這個事件本來是加在某些元素上的,然而你卻加到別人身上來做,完成這個事件。
也就是:利用冒泡的原理,把事件加到父級上,觸發執行效果。 好處呢: 1.提高性能。 2.新添加的元素還會有之前的事件。試想一下,一個頁面上ul的每一個li標簽添加一個事件,我們會不會給每一個標簽都添加一個onclick呢。 當頁面中存在大量元素都需要綁定同一個事件處理的時候,這種情況可能會影響性能,不僅消耗了內存,還多循環時間。每綁定一個事件都加重了頁面或者是運行期間的負擔。對于一個富前端的應用,交互重的頁面上,過多的綁定會占用過多內存。 一個簡單優雅的方式就是事件委托。它是基于事件的工作流:逐層捕獲,到達目標,逐層冒泡。既然事件存在冒泡機制,那么我們可以通過給外層綁定事件,來處理所有的子元素出發的事件。
一個事件委托的簡單實現:document.getElementById("ulId").onclick = function(e) { var e = e || window.event; var target = e.target || e.srcElement; //兼容舊版本IE和現代瀏覽器 if (target.nodeName.toLowerCase() !== "ul") { return; } console.log(target.innerHTML); }我們可以看一個例子:需要觸發每個li來改變他們的背景顏色。
- 1
- 2
- 3
首先想到最直接的實現:
window.onload = () => { let oUl = document.querySelector("#ul"); let aLi = oUl.querySelectorAll("li"); Array.from(aLi).forEach(ele => { ele.onmouseover = function() { this.style.background = "red"; } ele.onmouseout = function() { this.style.background = ""; } }) }
這樣我們就可以做到li上面添加鼠標事件。
但是如果說我們可能有很多個li用for循環的話就比較影響性能。
下面我們可以用事件委托的方式來實現這樣的效果。html不變
window.onload = () => { let oUl = document.querySelector("#ul"); /* 這里要用到事件源:event 對象,事件源,不管在哪個事件中,只要你操作的那個元素就是事件源。 ie:window.event.srcElement 標準下:event.target nodeName:找到元素的標簽名 */ //雖然習慣用ES6語法寫代碼,這里事件還是兼容一下IE吧 oUl.onmouseover = ev => { ev = ev || window.event; let target = ev.target || ev.srcElement; //console.log(target.innerHTML); if (target.nodeName.toLowerCase() === "li") { target.style.background = "red"; } } oUl.onmouseout = ev=> { ev = ev || window.event; let target = ev.target || ev.srcElement; //console.log(target.innerHTML); if (target.nodeName.toLowerCase() == "li") { target.style.background = ""; } } }
好處2,新添加的元素還會有之前的事件。
我們還拿這個例子看,但是我們要做動態的添加li。點擊button動態添加li
不用事件委托我們會這樣做:
window.onload = () => { let oUl = document.querySelector("#ul"); let aLi = oUl.querySelectorAll("li"); let oBtn = document.querySelector("#btn"); let iNow = 4; //剛才用forEach實現,現在就用性能最高的for實現,把剛才學的溫習一下 for (let i = 0, len = aLi.length; i < len; i++) { aLi[i].onmouseover = function() { this.style.background = "red"; } aLi[i].onmouseout = function() { this.style.background = ""; } } oBtn.onclick = function() { iNow++; let oLi = document.createElement("li"); oLi.innerHTML = 1* iNow; oUl.appendChild(oLi); } }
這樣做我們可以看到點擊按鈕新加的li上面沒有鼠標移入事件來改變他們的背景顏色。
因為點擊添加的時候for循環已經執行完畢。
那么我們用事件委托的方式來做。就是html不變
window.onload = () => { let oUl = document.querySelector("#ul"); let oBtn = document.querySelector("#btn"); let iNow = 4; oUl.onmouseover = ev => { ev = ev || window.event; let target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() === "li") { target.style.background = "red"; } } oUl.onmouseout = ev => { ev = ev || window.event; let target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == "li") { target.style.background = ""; } } oBtn.onclick = function() { iNow++; let oLi = document.createElement("li"); oLi.innerHTML = 1111 * iNow; oUl.appendChild(oLi); } }更快速的數據訪問
對于瀏覽器來說,一個標識符所處的位置越深,去讀寫他的速度也就越慢(對于這點,原型鏈亦是如此)。這個應該不難理解,簡單比喻就是:雜貨店離你家越遠,你去打醬油所花的時間就越長... 熊孩子,打個醬油那么久,菜早燒焦了 -.-~
我們在編碼過程中多多少少會使用到一些全局變量(window,document,自定義全局變量等等),了解javascript作用域鏈的人都知道,在局部作用域中訪問全局變量需要一層一層遍歷整個作用域鏈直至頂級作用域,而局部變量的訪問效率則會更快更高,因此在局部作用域中高頻率使用一些全局對象時可以將其導入到局部作用域中,例如:
對比看看:
//修改前 function showLi(){ var i = 0; for(;i再來看看兩個簡單的例子;
//1、作為參數傳入模塊 (function(window,$){ var xxx = window.xxx; $("#xxx1").xxx(); $("#xxx2").xxx(); })(window,jQuery); //2、暫存到局部變量 function(){ var doc = document; var global = window.global; }eval以及類eval問題我們都知道eval可以將一段字符串當做js代碼來執行處理,據說使用eval執行的代碼比不使用eval的代碼慢100倍以上(具體效率我沒有測試,有興趣同學可以測試一下),前面的瀏覽器控制臺效率低下也提到eval這個問題.
JavaScript 代碼在執行前會進行類似“預編譯”的操作:首先會創建一個當前執行環境下的活動對象,并將那些用 var 申明的變量設置為活動對象的屬性,但是此時這些變量的賦值都是 undefined,并將那些以 function 定義的函數也 添加為活動對象的屬性,而且它們的值正是函數的定義。但是,如果你使用了“eval”,則“eval”中的代碼(實際上為字 符串)無法預先識別其上下文,無法被提前解析和優化,即無法進行預編譯的操作。所以,其性能也會大幅度降低對上面js的預編譯,活動對象等一些列不太明白的童鞋.看完這篇文章惡補一下,我想你會收獲很多:前端基礎進階(三):變量對象詳解
其實現在大家一般都很少會用eval了,這里我想說的是兩個類eval的場景(new Function{},setTimeout,
setInterver)setTimtout("alert(1)",1000); setInterver("alert(1)",1000); (new Function("alert(1)"))();上述幾種類型代碼執行效率都會比較低,因此建議直接傳入匿名方法、或者方法的引用給setTimeout方法.
DOM操作的優化眾所周知的,DOM操作遠比javascript的執行耗性能,雖然我們避免不了對DOM進行操作,但我們可以盡量去減少該操作對性能的消耗。
為什么操作DOM這么耗費性能呢?
瀏覽器通常會把js和DOM分開來分別獨立實現。 舉個栗子冷知識,在IE中,js的實現名為JScript,位于jscript.dll文件中;DOM的實現則存在另一個庫中,名為mshtml.dll(Trident)。 Chrome中的DOM實現為webkit中的webCore,但js引擎是Google自己研發的V8。 Firefox中的js引擎是SpiderMonkey,渲染引擎(DOM)則是Gecko。DOM,天生就慢
前面的小知識中說過,瀏覽器把實現頁面渲染的部分和解析js的部分分開來實現,既然是分開的,一旦兩者需要產生連接,就要付出代價。
兩個例子:
小明和小紅是兩個不同學校的學生,兩個人家里經濟條件都不太好,買不起手機(好尷尬的設定Orz...),所以只能通過寫信來互相交流,這樣的過程肯定比他倆面對面交談時所需要花費的代價大(額外的事件、寫信的成本等)。
官方例子:把DOM和js(ECMAScript)各自想象為一座島嶼,它們之間用收費橋進行連接。ECMAScript每次訪問DOM,都要途徑這座橋,并交納“過橋費”。訪問DOM的次數越多,費用也就越高。
因此,推薦的做法是:盡可能的減少過橋的次數,努力待在ECMAScript島上。
讓我們通過一個最簡單的代碼解釋這個問題:
function innerLi_s(){ var i = 0; for(;i<20;i++){ document.getElementById("Num").innerHTML="A"; //進行了20次循環,每次又有2次DOM元素訪問:一次讀取innerHTML的值,一次寫入值 }; };針對以上方法進行一次改寫:
function innerLi_s(){ var content =""; var i = 0; for(;i<20;i++){ content += "A"; //這里只對js的變量循環了20次 }; document.getElementById("Num").innerHTML += content; //這里值進行了一次DOM操作,又分2次DOM訪問:一次讀取innerHTML的值,一次寫入值 };減少頁面的重排(Reflows)和重繪(Repaints)簡單說下什么是重排和重繪:
瀏覽器下載完HTMl,CSS,JS后會生成兩棵樹:DOM樹和渲染樹。 當Dom的幾何屬性發生變化時,比如Dom的寬高,或者顏色,position,瀏覽器需要重新計算元素的幾何屬性,并且重新構建渲染樹,這個過程稱之為重繪重排。元素布局的改變或內容的增刪改或者瀏覽器窗口尺寸改變都將會導致重排,而字體顏色或者背景色的修改則將導致重繪。
對于類似以下代碼的操作,據說現代瀏覽器大多進行了優化(將其優化成1次重排版)://修改前 var el = document.getElementById("div"); el.style.borderLeft = "1px"; //1次重排版 el.style.borderRight = "2px"; //又1次重排版 el.style.padding = "5px"; //還有1次重排版 //修改后 var el = document.getElementById("div"); el.style.cssText = "border-left:1px;border-right:2px;padding:5px"; //1次重排版針對多重操作,以下三種方法也可以減少重排版和重繪的次數:
Dom先隱藏,操作后再顯示 2次重排 (臨時的display:none);
document.createDocumentFragment() 創建文檔片段處理,操作后追加到頁面 1次重排;
var newDOM = oldDOM.cloneNode(true)創建Dom副本,修改副本后oldDOM.parentNode.replaceChild
(newDOM,oldDOM)覆蓋原DOM 2次重排如果是動畫元素的話,最好使用絕對定位以讓它不在文檔流中,這樣的話改變它的位置不會引起頁面其它元素重排
更多DOM優化的細節以及關于瀏覽器頁面的重排(Reflows)和重繪(Repaints)的概念和優化請參考:天生就慢的DOM如何優化?,花10+分鐘閱讀,你會受益匪淺.
盡量少去改變作用域鏈使用with
try catch
我了解到的JavaScript中改變作用域鏈的方式只有兩種1)使用with表達式 2)通過捕獲異常try catch來實現
但是with是大家都深惡痛絕的影響性能的表達式,因為我們完全可以通過使用一個局部變量的方式來取代它(因為with的原理是它的改變作用域鏈的同時需要保存很多信息以保證完成當前操作后恢復之前的作用域鏈,這些就明顯的影響到了性能)
try catch中的catch子句同樣可以改變作用域鏈。當try塊發生錯誤時,程序自動轉入catch塊并將異常對象推入作用域鏈前端的一個可變對象中,也就是說在catch塊中,函數所有的局部變量已經被放在第二個作用域鏈對象中,但是catch子句執行完成之后,作用域鏈就會返回到原來的狀態。應該最小化catch子句來保證代碼性能,如果知道錯誤的概念很高,我們應該盡量修正錯誤而不是使用try catch.
最后雖說現代瀏覽器都已經做的很好了,但是本獸覺得這是自己對代碼質量的一個追求。并且可能一個點或者兩個點不注意是不會產生多大性能影響,但是從多個點進行優化后,可能產生的就會質的飛躍了
參考文章:
JavaScript 總結的這幾個提高性能知識點,希望大家牢牢掌握。高性能JavaScript循環語句和流程控制
if else 和 switch的效率
switch...case和if...else效率比較
JavaScript提高性能知識點匯總
JavaScript執行效率小結
天生就慢的DOM如何優化?
編寫高性能的JavaScript代碼
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81742.html
摘要:下面圍繞的這樣的目的,即左右知乎網頁上屏幕截圖功能的實現前端掘金背景最近注意到知乎的屏幕截圖反饋功能,感覺非常不錯。正如你期望的,文中的闖關記之垃圾回收和內存管理前端掘金題圖來源,授權基于協議。 微信小程序實戰學習 起手式 DEMO 仿肯德基 - 前端 - 掘金小程序?大場景? 微信小程序本質上來說就是一個 HTML 5(移動網頁) 應用,用view、scoll-view代替了div標...
摘要:至簡阿里巴巴高級技術專家,是集團方向的重要參與者和推動者。作者將工程師思維分解為產品技術和工程三大思維。快速跟上技術的迭代步伐是每個有追求的工程師不斷提升自己專業素養的表現之一。對于工程師來說,機制是通過系統性的軟件設計去達成的。 摘要: 為什么想到寫這篇文章?作者是想通過對工程師思維的分析和解讀,讓工程師能正確對待那些在現實工作中看上去與本職崗位無關,卻對團隊效能影響極大的一些點和一...
閱讀 2425·2021-09-01 10:41
閱讀 1446·2019-08-30 14:12
閱讀 513·2019-08-29 12:32
閱讀 2863·2019-08-29 12:25
閱讀 2937·2019-08-28 18:30
閱讀 1710·2019-08-26 11:47
閱讀 984·2019-08-26 10:35
閱讀 2593·2019-08-23 18:06