摘要:像也是類似的也不建議使用,會降低性能,通過包裹的代碼塊,作用域鏈將會額外增加一層,降低索引效率對象的優化緩存需要被使用的對象獲取數據的性能有如下順序從快到慢變量獲取數組下標獲取對象的整數索引獲取對象屬性獲取對象非整數索引獲取。
正巧看到在送書,于是乎找了找自己博客上記錄過的一些東西來及其無恥的蹭書了~~~
小廣告:更多內容可以看我的博客
優化循環如果現在有個一個data[]數組,需要對其進行遍歷,應當怎么做?最簡單的代碼是:
for (var i = 0; i < data.length; i++) { //do someting }
這里每次循環開始前都需要判斷i是否小于data.length,JavaScript并不會對data.length進行緩存,而是每次比較都會進行一次取值。如我們所知,JavaScript數組其實是一個對象,里面有個length屬性,所以這里實際上就是取得對象的屬性。如果直接使用變量的話就會少一次索引對象,如果數組的元素很多,效率提升還是很可觀的。所以我們通常將代碼改成如下所示:
for(var i = 0, m = data.length; i < m; i++) { //do someting }
這里多加了一個變量m用于存放data.length屬性,這樣就可以在每次循環時,減少一次索引對象,但是代價是增加了一個變量的空間,如果遍歷不要求順序,我們甚至可以不用m這個變量存儲長度,在不要求順序的時候可以使用如下代碼:
for(var i = data.length; i--; ) { //do someting }
當然我們可以使用while來替代:
var i = data.length; while(i--) { //do someting }
這樣就可只使用一個變量了
運算結果緩存由于JavaScript中的函數也是對象(JavaScript中一切都是對象),所以我們可以給函數添加任意的屬性。這也就為我們提供符合備忘錄模式的緩存運算結果的功能,比如我們有一個需要大量運算才能得出結果的函數如下:
function calculator(params) { //大量的耗時的計算 return result; }
如果其中不涉及隨機,參數一樣時所返回的結果一致,我們就可以將運算結果進行緩存從而避免重復的計算:
function calculator(params) { var cacheKey = JSON.stringify(params); var cache = calculator.cache = calculator.cache || {}; if(typeof cache[cacheKey] !== "undefined") { return cache[cacheKey]; } //大量耗時的計算 cache[cacheKey] = result; return result; }
這里將參數轉化為JSON字符串作為key,如果這個參數已經被計算過,那么就直接返回,否則進行計算。計算完畢后再添加入cache中,如果需要,可以直接查看cache的內容:calculator.cache
這是一種典型的空間換時間的方式,由于瀏覽器的頁面存活時間一般不會很長,占用的內存會很快被釋放(當然也有例外,比如一些WEB應用),所以可以通過這種空間換時間的方式來減少響應時間,提升用戶體驗。這種方式并不適用于如下場合:
1. 相同參數可能產生不同結果的情況(包含隨機數之類的)
2. 運算結果占用特別多內存的情況
這個很好理解,每創建一個函數對象是需要大批量空間的。所以在一個循環中創建函數是很不明智的,盡量將函數移動到循環之前創建,比如如下代碼:
for(var i = 0, m = data.length; i < m; i++) { handlerData(data[i], function(data){ //do something }); }
就可以修改為:
var handler = function(data){ //do something }; for(var i = 0, m = data.length; i < m; i++) { handlerData(data[i], handler); }讓垃圾回收器回收那些不再需要的對象
之前我曾在 淺談V8引擎中的垃圾回收機制 中講到了V8引擎如何進行垃圾回收。可以從中看到,如果長時間保存對象,老生代中占用的空間將增大,每次在老生代中的垃圾回收過程將會相當漫長。而垃圾回收器判斷一個對象為活對象還是死對象,是按照是否有活對象或根對象含有對它的引用來判定的。如果有根對象或者活對象引用了這個對象,它將被判定為活對象。所以我們需要通過手動消除這些引用來讓垃圾回收器對回收這些對象。
delete一種方式是通過delete方式來消除對象中的鍵值對,從而消除引用。但這種方式并不提倡,它會改變對象的結構,可能導致引擎中對對象的存儲方式變更,降級為字典方式進行存儲(詳細請見V8 之旅:對象表示),不利于JavaScript引擎的優化,所以盡量減少使用
null另一種方式是通過將值設為null來消除引用。通過將變量或對象的屬性設為null,可以消除引用,使原本引用的對象成為一個“孤島”,然后在垃圾回收的時候對其進行回收。這種方式不會改變對象的結構,比使用delete要好
全局對象另外需要注意的是,垃圾回收器認為根對象永遠是活對象,永遠不會對其進行垃圾回收。而全局對象就是根對象,所以全局作用域中的變量將會一直存在
事件處理器的回收在平常寫代碼的時候,我們經常會給一個DOM節點綁定事件處理器,但有時候我們不需要這些事件處理器后,就不管它們了,它們默默的在內存中保存著。所以在某些DOM節點綁定的事件處理器不需要后,我們應當銷毀它們。同時綁定的時候也盡量使用事件代理的方式進行綁定,以免造成多次重復的綁定導致內存空間的浪費,事件代理可見前端性能優化(DOM操作篇)
閉包導致的內存泄露JavaScript的閉包可以說即是“天使”又是“魔鬼”,它“天使”的一面是我們可以通過它突破作用域的限制,而其魔鬼的一面就是和容易導致內存泄露,比如如下情況:
var result = (function() { var small = {}; var big = new Array(10000000); //do something return function(){ if(big.indexOf("someValue") !== -1) { return null; } else { return small; } } })();
這里,創建了一個閉包。使得返回的函數存儲在result中,而result函數能夠訪問其作用域內的small對象和big對象。由于big對象和small對象都可能被訪問,所以垃圾回收器不會去碰這兩個對象,它們不會被回收。我們將上述代碼改成如下形式:
var result = (function() { var small = {}; var big = new Array(10000000); var hasSomeValue; //do something hasSomeValue = big.indexOf("someValue") !== -1; return function(){ if(hasSomeValue) { return null; } else { return small; } } })();
這樣,函數內部只能夠訪問到hasSomeValue變量和small變量了,big沒有辦法通過任何形式被訪問到,垃圾回收器將會對其進行回收,節省了大量的內存。
慎用eval和withDouglas Crockford將eval比作魔鬼,確實在很多方面我們可以找到更好地替代方式。使用它時需要在運行時調用解釋引擎對eval()函數內部的字符串進行解釋運行,這需要消耗大量的時間。像Function、setInterval、setTimeout也是類似的
Douglas Crockford也不建議使用with,with會降低性能,通過with包裹的代碼塊,作用域鏈將會額外增加一層,降低索引效率
對象的優化 緩存需要被使用的對象JavaScript獲取數據的性能有如下順序(從快到慢):變量獲取 > 數組下標獲取(對象的整數索引獲取) > 對象屬性獲取(對象非整數索引獲取)。我們可以通過最快的方式代替最慢的方式:
var body = document.body; var maxLength = someArray.length; //...
需要考慮,作用域鏈和原型鏈中的對象索引。如果作用域鏈和原型鏈較長,也需要對所需要的變量繼續緩存,否則沿著作用域鏈和原型鏈向上查找時也會額外消耗時間
緩存正則表達式對象需要注意,正則表達式對象的創建非常消耗時間,盡量不要在循環中創建正則表達式,盡可能多的對正則表達式對象進行復用
考慮對象和數組在JavaScript中我們可以使用兩種存放數據:對象和數組。由于JavaScript數組可以存放任意類型數據這樣的靈活性,導致我們經常需要考慮何時使用數組,何時使用對象。我們應當在如下情況下做出考慮:
1. 存儲一串相同類型的對象,應當使用數組
2. 存儲一堆鍵值對,值的類型多樣,應當使用對象
3. 所有值都是通過整數索引,應當使用數組
往數組中插入混合類型很容易降低數組使用的效率,盡量保持數組中元素的類型一致
如果使用稀疏數組,它的元素訪問將遠慢于滿數組的元素訪問。因為V8為了節省空間,會將稀疏數組通過字典方式保存在內存中,節約了空間,但增加了訪問時間
對象的拷貝需要注意的是,JavaScript遍歷對象和數組時,使用for...in的效率相當低,所以在拷貝對象時,如果已知需要被拷貝的對象的屬性,通過直接賦值的方式比使用for...in方式要來得快,我們可以通過定一個拷貝構造函數來實現,比如如下代碼:
function copy(source){ var result = {}; var item; for(item in source) { result[item] = source[item]; } return result; } var backup = copy(source);
可修改為:
function copy(source){ this.property1 = source.property1; this.property2 = source.property2; this.property3 = source.property3; //... } var backup = new copy(source);字面量代替構造函數
JavaScript可以通過字面量來構造對象,比如通過[]構造一個數組,{}構造一個對象,/regexp/構造一個正則表達式,我們應當盡力使用字面量來構造對象,因為字面量是引擎直接解釋執行的,而如果使用構造函數的話,需要調用一個內部構造器,所以字面量略微要快一點點。
緩存AJAX曾經聽過一個訪問時間比較(當然不精確):
* cpu cache ≈ 100 * 寄存器
* 內存 ≈ 100 * cpu cache
* 外存 ≈ 100 * 內存
* 網絡 ≈ 100 * 外存
可看到訪問網絡資源是相當慢的,而AJAX就是JavaScript訪問網絡資源的方式,所以對一些AJAX結果進行緩存,可以大大減少響應時間。那么如何緩存AJAX結果呢
函數緩存我們可以使用前面緩存復雜計算函數結果的方式進行緩存,通過在函數對象上構造cache對象,原理一樣,這里略過。這種方式是精確到函數,而不精確到請求
本地緩存HTML5提供了本地緩存sessionStorage和localStorage,區別就是前者在瀏覽器關閉后會自動釋放,而后者則是永久的,不會被釋放。它提供的緩存大小以MB為單位,比cookie(4KB)要大得多,所以我們可以根據AJAX數據的存活時間來判斷是存放在sessionStorage還是localStorage當中,在這里以存儲到sessionStorage中為例(localStorage只需把第一行的window.sessionStorage修改為window.localStorage):
function(data, url, type, callback){ var storage = window.sessionStorage; var key = JSON.stringify({ url : url, type : type, data : data }); var result = storage.getItem(key); var xhr; if (result) { callback.call(null, result); } else { xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status === 200){ storage.setItem(key, xhr.responseText); callback.call(null, xhr.responseText); } else { } } }; xhr.open(type, url, async); xhr.send(data); } };使用布爾表達式的短路
在很多語言中,如果bool表達式的值已經能通過前面的條件確定,那么后面的判斷條件將不再會執行,比如如下代碼
function calCondition(params) { var result; //do lots of work return !!result; } if(otherCondition && calCondition(someParams)) { console.log(true); } else { console.log(false); }
這里首先會計算otherCondition的值,如果它為false,那么整個正則表達式就為false了,后續的需要消耗大量時間的calCondition()函數就不會被調用和計算了,節省了時間
使用原生方法在JavaScript中,大多數原生方法是使用C++編寫的,比js寫的方法要快得多,所以盡量使用諸如Math之類的原生對象和方法
字符串拼接在IE和FF下,使用直接+=的方式或是+的方式進行字符串拼接,將會很慢。我們可以通過Array的join()方法進行字符串拼接。不過并不是所有瀏覽器都是這樣,現在很多瀏覽器使用+=比join()方法還要快
使用web workerweb worker是HTML5提出的一項新技術,通過多線程的方式為JavaScript提供并行計算的能力,通過message的方式進行相互之間的信息傳遞,我還沒有仔細研究過
JavaScript文件的優化 使用CDN在編寫JavaScript代碼中,我們經常會使用庫(jQuery等等),這些JS庫通常不會對其進行更改,我們可以將這些庫文件放在CDN(內容分發網絡上),這樣能大大減少響應時間
壓縮與合并JavaScript文件在網絡中傳輸JS文件,文件越長,需要的時間越多。所以在上線前,通常都會對JS文件進行壓縮,去掉其中的注釋、回車、不必要的空格等多余內容,如果通過uglify的算法,還可以縮減變量名和函數名,從而將JS代碼壓縮,節約傳輸時的帶寬。另外經常也會將JavaScript代碼合并,使所有代碼在一個文件之中,這樣就能夠減少HTTP的請求次數。合并的原理和sprite技術相同
使用Application Cache緩存這個在之前的文章前端性能優化(Application Cache篇)中已有描述,就不贅述了
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78069.html
摘要:特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入匯總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應和斧正,會及時更新,平時業務工作時也會不定期更...
摘要:感謝王下邀月熊分享的前端每周清單,為方便大家閱讀,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清單系列,并以年月為單位進行分類,具體內容看這里前端每周清單年度總結與盤點。 感謝 王下邀月熊_Chevalier 分享的前端每周清單,為方便大家閱讀,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清單系列,并以年/月為單位進行分類,具...
摘要:雖然有著各種各樣的不同,但是相同的是,他們前端優化不完全指南前端掘金篇幅可能有點長,我想先聊一聊閱讀的方式,我希望你閱讀的時候,能夠把我當作你的競爭對手,你的夢想是超越我。 如何提升頁面渲染效率 - 前端 - 掘金Web頁面的性能 我們每天都會瀏覽很多的Web頁面,使用很多基于Web的應用。這些站點看起來既不一樣,用途也都各有不同,有在線視頻,Social Media,新聞,郵件客戶端...
摘要:減少作用域鏈上的查找次數。盡量少用全局變量,盡量使用局部變量。全局變量如果不手動銷毀,會一直存在,造成全局變量污染,可能很產生一些意想不到的錯誤,而局部變量運行完成后,就被會被回收使用代替大量的內聯樣式修改。性能優化還需要繼續深入研究。 關于前端性能優化的討論一直都很多,包羅的知識也很多,可以說性能優化只有更好,沒有最好。前面我寫了一篇關于css優化的總結文章,今天再從javascri...
摘要:減少作用域鏈上的查找次數。盡量少用全局變量,盡量使用局部變量。全局變量如果不手動銷毀,會一直存在,造成全局變量污染,可能很產生一些意想不到的錯誤,而局部變量運行完成后,就被會被回收使用代替大量的內聯樣式修改。性能優化還需要繼續深入研究。 關于前端性能優化的討論一直都很多,包羅的知識也很多,可以說性能優化只有更好,沒有最好。前面我寫了一篇關于css優化的總結文章,今天再從javascri...
閱讀 3076·2023-04-25 20:43
閱讀 1719·2021-09-30 09:54
閱讀 1590·2021-09-24 09:47
閱讀 2874·2021-09-06 15:02
閱讀 3510·2021-02-22 17:09
閱讀 1233·2019-08-30 15:53
閱讀 1441·2019-08-29 17:04
閱讀 1956·2019-08-28 18:22