前端代碼優化
前端標準html、js,查這裏mozilla標準(w3c給的是紙面標準,這裏是業界實際使用的標準)
developer.mozilla.org/zh-CN/javascript
》做用域鏈越長,執行性能越差
當函數執行時,會造成本身的執行環境,執行環境會與函數的做用域鏈進行連接,並建立與之關聯的活動對象(activation object)。執行環境中定義的全部變量和函數都保存在這個對象中。當函數執行完成後,這個對象會被系統銷燬。css
》避免函數內部屢次逐級向上查找變量,使用局部變量存儲一下
例如在函數開頭var doc=documenthtml
》避免使用with
with關鍵字指定其內部未指明做用域的變量都使用它聲明的做用域
with(document){
var bd=body; // 至關於使用document.body
}
帶來的性能問題是,with會使做用域鏈變長,下降內部本地變量的查找(會先從with指定的做用域查找,而後再查找本地做用域),建議不要使用。前端
》減小DOM操做
瀏覽器執行分兩個引擎:ECMAScript引擎和DOM引擎,前者執行js代碼,很是快,後者操做UI,不管是讀仍是寫代價都很高。例如Chrome使用V8引擎來驅動ECMAScript,使用Webkit中的WebCore來驅動DOM。
顯著提升性能的策略就是增長JS操做,減小DOM操做。java
》DOM操做優化
>注意獲取DOM元素返回是類數組,其length是動態計算的
var alldivs = document.getElementsByTagName("div");
for (var i = 0; i < alldivs.length; i++){
document.body.appendChild(document.createElement("div"));
}
這會進入死循環,由於alldivs.length是動態的,建議的用本地變量存起來,包括查找出的結果,減小不避要的重複查找(例如若屢次訪問alldivs[i],可用變量存起來)
for (var i=0, len=divs.length; i<len; i++)
即:越怕麻煩越麻煩正則表達式
》getElementById getElementsByTagName querySelectorAll
現代瀏覽器對querySelectorAll() 都作了優化,複雜語句的查詢效率獲得提高,最重要的,它能直接接受多條CSS語句。對於相對複雜的查找,getElementById getElementsByTagName的效率並無querySelectorAll() 高。
var elements = document.querySelectorAll("#menu a");
var elements = document.getElementById("menu").getElementsByTagName("a");編程
》頁面的重排與重繪
>DOM樹和渲染樹:
DOM樹是整個頁面的骨架,每一個節點在渲染樹上都至少有一個對應節點。隱藏的DOM樹節點是沒有對應的渲染樹節點。
渲染樹上的每個節點均可稱之爲frame或者box,對應CSS的盒子模型,margin、 padding、border等。
當DOM樹和渲染樹都建立完成,頁面便開始繪製(paint)。數組
>重排與重繪
若是DOM樹的某些變化,致使某個元素的拓撲發生改變,例如邊框變粗、加入文字等,那麼瀏覽器就會從新計算該元素的拓撲結構,以及其它被影響到的元素的拓撲結構。同時瀏覽器會將這部分的拓撲改變,映射到渲染樹上,對渲染樹進行從新的構建,稱之爲重排(reflow)。
當重排完成以後,瀏覽器就會對這部分進行從新繪製,稱之爲重繪(repaint)。
並非全部的DOM樹改變都會影響拓撲結構。例如改變某個元素的背景色,就只會致使重繪,而不會致使重排。但重排必定會致使重繪。重排或者重繪都會大大消耗系統資源。瀏覽器
>致使重排的因素有:
一個可見的DOM元素被添加或者刪除
元素改變的位置
元素的尺寸發生了變化(margin、padding、border、width、height等等)
內容發生了改變(例如文字、圖片的增刪改)
頁面初始化加載
瀏覽器窗口發生了尺寸的改變緩存
>致使立刻重繪的因素有:
現代瀏覽器對重排與重繪操做,都作了很好的優化。即便頻繁操做,瀏覽器也會進行相應的管理,構建隊列等,最後再一次性進行重排或重繪。
但若是用戶讀取下面這些屬性,瀏覽器爲了得到正確的值,就會立刻重繪頁面,消耗大量資源:
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
getComputedStyle() (currentStyle in IE)
》減小重排與重繪
>避免DOM屬性的讀和寫操做交叉
var computed, tmp = [], bodystyle = document.body.style;
if (document.body.currentStyle) { // IE, Opera
computed = document.body.currentStyle;
} else { // W3C
computed = document.defaultView.getComputedStyle(document.body, "");
}
bodystyle.color = "red"; // 寫
tmp[0] = computed.backgroundColor; // 讀
bodystyle.color = "white"; // 寫
tmp[1] = computed.backgroundImage; // 讀
bodystyle.color = "green";
tmp[2] = computed.backgroundAttachment;
上面的操做會致使三次立刻重繪
bodystyle.color = "red";
bodystyle.color = "white";
bodystyle.color = "green";
tmp[0] = computed.backgroundColor;
tmp[1] = computed.backgroundImage;
tmp[2] = computed.backgroundAttachment;
這樣優化後只重繪一次
var el = document.getElementById("mydiv");
el.style.borderLeft = "1px";
el.style.borderRight = "2px";
el.style.padding = "5px";
儘管瀏覽器會對這三次操做進行排隊、合併,但仍是應該採用合理的方式(始終記住js操做效率遠高於dom操做):
el.style.cssText += "border-left:1px; border-right:2px; padding:5px;";
或者寫成一個外部css樣式
>充分利用DocumentFragment
在建立多個相同或者類似元素時,應該合理利用文檔碎片DocumentFragment來處理,瀏覽器有專門的優化。例如給頁面一次性添加一百萬個<div>元素:
function createDivs(){
var tFrag = document.createDocumentFragment();
for (var i=0; i<1000000; i++){
tFrag.appendChild(document.createElement("div"));
}
document.body.appendChild(tFrag);
}
>讓元素脫離文檔流
顯示或者隱藏頁面的某個部分,是常常遇到的需求。尤爲在製做動畫的時候。重排或者重繪有時只會影響頁面的某個區域,有時會影響整棵樹。
當一個元素從頁面頂部動畫到底部時,可能會影響整個頁面的DOM樹,所以能夠考慮將其移出文檔流。
->將元素設置爲絕對定位,讓其脫離文檔流。
->進行動畫操做,隻影響頁面局部。
->再根據狀況,從新修改position屬性,讓其迴歸文檔流。
》使用事件委託
利用事件冒泡機制,將事件綁定到父元素,子元素的事件委託給父元素處理,避免子元素頻繁的變化致使重複綁定。event delegation
var myul = sina.$("newslist");
var lis = myul.getElementsByTagName("li");
for(var i=0,l=lis.length;i<l;i++){
sina.addEvent(lis[i],"click",function(e){
e = e || window.event;
var target = e.target || e.srcElement;
alert(target.innerHTML);
});
}
可優化爲:
var myul = sina.$("newslist");
sina.addEvent(myul,"click",function(e){
e = e || window.event;
var target = e.target || e.srcElement;
if(target.tagName.toLowerCase()=="li"){
alert(target.innerHTML);
}
});
典型應用:地圖、Gmail、新聞列表翻頁等等。
》一些編程技巧
for-in是四種循環裏性能明顯低於其它三種的,應儘可能避免使用。只有在不知道對象屬性數量、名稱時才考慮;
減小對length屬性的查詢可以提升性能;
if-else與switch性能差很少,而lookup tables查表法對於鍵值對key-value類型的分支判斷的有效;
空間換時間,例如把遞歸結果緩存起來直接使用
》字符串操做
+ += join可大膽使用,減小concat使用
充分利用數組的方法處理字符口串,例如反轉字符串 str.split("").reverse().join("")足夠高效和簡潔
》正則表達式優化
全部瀏覽器執行正則表達式都很快,稍微複雜的字符串匹配,用正則校驗會比本身寫校驗邏輯更高效。
一個正則表達式在匹配字符串的時候,經歷了四個步驟:
編譯、設置開始點、匹配每個正則標識、成功或者失敗,步驟3可能有回溯,步驟2-4可能重複執行
>編譯
建立一個正則表達式時,瀏覽器會檢查是否符合規則。而後將該正則編譯到底層,用於以後的對比。所以正則的速度一般都較快,是在瀏覽器更底層來完成對比,能應對複雜查詢。
var myReg = "/h(ello|appy) hippo/g"; /g表示全局匹配,或儘量長匹配,/i表示不區分大小寫
正則表達式的優化主要是在減小回溯次數
var myReg = "/h(ello|appy) hippo/g";
var tStr = "hello there, happy hippo";
->從字符串的第一個位置開始匹配,h匹配成功,字符串這個位置被記錄。
->正則逐一日後走,ello 都匹配成功,但h與t匹配失敗。此時正則回溯(backtrack),回溯到以前成功的位置h,試圖匹配appy,匹配失敗。
->字符串返回到以前記錄的位置h後面的一個位置e,從新匹配整個正則。沒法匹配h,下一個位置l,也失敗。直到第14個字符,又是h,匹配成功。
->重複步驟2,ello 匹配失敗,此時正則回溯(backtrack)到以前成功的位置h,試圖匹配appy,成功匹配。繼續往下 hippo匹配也成功。
整個匹配過程正則表達式有兩次回溯,一次在字符串的第一個h,另外一次在字符串的第二個h。
var str = "<p>Para 1.</p><img src='s.jpg'><p>Para 2.</p><div>Div.</div>";
var reg1 = /<p>.*<\/p>/; //16次匹配出結果
var reg2 = /<p>.*?<\/p>/; //22次匹配出結果
對比貪婪模式與懶惰模式:
第一個正則表達式是貪婪匹配,在匹配<p>成功後,.* 會直接匹配到字符串的末端,而後往前逐一尋找<。
第二個正則表達式是懶惰匹配,在匹配<p>成功後,.*? 會從P字符開始,逐一日後尋找<。
每一次匹配不成功,正則表達式都會回溯到 .* 或 .*?,尋找下一個分支。.* 回溯後是減小一個字符,.*? 回溯後是增長一個字符。
每回退一個字符,總體匹配一下模式串看是否成功
因爲 | 很容易致使回溯,應當儘量的採用別的方式。
不要使用 使用
cat|bat [cb]at
red|read rea?d
red|raw r(?:ed|aw)
(.|\r|\n) [\s\S]
其餘分析參見PPT詳解
》Web Workers多線程
javascript是一門單線程的語言,在遇到複雜計算時,可能會阻斷整個頁面,HTML5經過workers的模式,在必定程度上給js開啓了多線程的大門。
外部的Worker是不能操做主線程DOM樹的,這樣也能保證在執行子線程的時候,主線程的UI不會被打斷。在子線程中,經過 self 對象來指向本身。與主線程的通信方式也是經過 message,接收、計算完成後 post 回去。
》其餘js變量提高:只是聲明提高,賦值動做不會,因此剛開始會是undefinedjs原型鏈:每個對象都有成員屬性_prop_指向類,類自己也有屬性_prop_指向父類(如object),對象經過this訪問成員屬性或方法且自己沒有命中時,會自動從_porp_中搜索原型鏈。假設有類(或函數)Book和對象book一、book2,當給類添加方法,即Book.protype.sayHi = function (){},則對象book一、book2能訪問到sayHi方法。