#性能優化
---
###使用最新版本的 JQuery、zepto
最新版本的 JQuery、zepto、zepto 會改進性能和增加新功能,若不是為了兼容舊瀏覽器,建議使用最新版本的 JQuery、zepto。以下是三條常見的 JQuery 語句,版本越新,性能越好:

分別使用 1.4.2、1.4.4、1.6.2 三個版本測試瀏覽器在一秒內能夠執行多少次,結果 1.6.2 版執行次數遠超兩個老版本。
>附上官網:[zepto官網](http://zeptojs.com/)、[JQuery全部版本](https://code.jquery.com/jquery/)
###JQuery 變量
1. 存放 jQuery 對象的變量以 $ 開頭;
2. 將 jQuery 選擇器返回的對象緩存到本地變量中復用;
3. 使用駝峰命名變量;
~~~javascript
var $myDiv = $("#myDiv");
$myDiv.click(function(){...});
~~~
###選擇器
1. 盡可能的使用 ID 選擇器,因為它會調用瀏覽器原生方法 document.getElementById 查找元素。當然直接使用原生 document.getElementById 方法性能會更好;
2. 在父元素中選擇子元素使用 .find() 方法性能會更好, 因為 ID 選擇器沒有使用到 Sizzle 選擇器引擎來查找元素;
~~~javascript
// not good
var $productIds = $("#products .class");
// good
var $productIds = $("#products").find(".class");
~~~
###DOM 操作
1. 當要操作 DOM 元素的時候,盡量將其分離節點,操作結束后,再插入節點;
2. 使用字符串連接或 array.join 要比 .append()性能更好;
~~~javascript
var $myList = $("#list-container > ul").detach();
//...a lot of complicated things on $myList
$myList.appendTo("#list-container");
// not good
var $myList = $("#list");
for(var i = 0; i < 10000; i++){
$myList.append("<li>"+i+"</li>");
}
// good
var $myList = $("#list");
var list = "";
for(var i = 0; i < 10000; i++){
list += "<li>"+i+"</li>";
}
$myList.html(list);
// much to good
var array = [];
for(var i = 0; i < 10000; i++){
array[i] = "<li>"+i+"</li>";
}
$myList.html(array.join(''));
~~~
###事件
1. 如果需要,對事件使用自定義的 namespace,這樣容易解綁特定的事件,而不會影響到此 DOM 元素的其他事件監聽;
2. 對 Ajax 加載的 DOM 元素綁定事件時盡量使用事件委托。事件委托允許在父元素綁定事件,子代元素可以響應事件,也包括<font color=red> Ajax 加載后添加的子代元素</font>;
~~~javascript
$("#myLink").on("click.mySpecialClick", myEventHandler);
$("#myLink").unbind("click.mySpecialClick");
// not good
$("#list a").on("click", myClickHandler);
// good
$("#list").on("click", "a", myClickHandler);
~~~
###鏈式寫法
1. 盡量使用鏈式寫法而不是用變量緩存或者多次調用選擇器方法;
2. 當鏈式寫法超過三次或者因為事件綁定變得復雜后,使用換行和縮進保持代碼可讀性;
~~~javascript
$("#myDiv").addClass("error").show();
$("#myLink")
.addClass("bold")
.on("click", myClickHandler)
.on("mouseover", myMouseOverHandler)
.show();
~~~
###JQuery、zepto其他
1. 多個參數使用對象字面量存儲;
2. 不要將 CSS 寫在 jQuery 里面;
3. 正則表達式僅準用 .test() 和 .exec() 。不準用 "string".match() ;
###避免不必要的 DOM 操作
瀏覽器遍歷 DOM 元素的代價是昂貴的。最簡單優化 DOM 樹查詢的方案是,當一個元素出現多次時,將它保存在一個變量中,就避免多次查詢 DOM 樹了。
~~~javascript
// good
var myList = "";
var myListHTML = document.getElementById("myList").innerHTML;
for (var i = 0; i < 100; i++) {
myList += "<span>" + i + "</span>";
}
myListHTML = myList;
// not good
for (var i = 0; i < 100; i++) {
document.getElementById("myList").innerHTML += "<span>" + i + "</span>";
}
~~~
###緩存數組長度
循環無疑是和 JavaScript 性能非常相關的一部分。通過存儲數組的長度,可以有效避免每次循環重新計算。
注: 雖然現代瀏覽器引擎會自動優化這個過程,但是不要忘記還有舊的瀏覽器。
~~~javascript
var arr = new Array(1000),
len, i;
// good - size is calculated only 1 time and then stored
for (i = 0, len = arr.length; i < len; i++) {
}
// not good - size needs to be recalculated 1000 times
for (i = 0; i < arr.length; i++) {
}
~~~
###異步加載第三方內容
當你無法保證嵌入第三方內容比如 Youtube 視頻或者一個 like/tweet 按鈕可以正常工作的時候,你需要考慮用異步加載這些代碼,避免阻塞整個頁面加載。
~~~javascript
/* url為js的鏈接,callBack為url的js中的函數(該函數調用應該寫到匿名函數中,如function(){console.log(div.getScrollOffset())})*/
function asyncLoaded(url,callBack){
var script = document.createElement('script');
script.type = 'text/javascript';
/* if else 這幾句話必須要寫到這位置處,不能放最后,因為if中js加載中script.readyState存在好幾種狀態,
只有狀態改變‘readystatechange’事件才會觸發,但現在瀏覽器加載速度很快,當解析到該事件時JS有可能以加載完,
所以事件根本不會觸發,所以要寫到前面*/
if(script.readystate){//兼容IE
script.onreadystatechange = function() {//狀態改變事件才觸發
if(script.readyState == 'loaded' || script.readyState == 'complete'){
callBack();
script.onreadystatechange = null;
}
}
}else{
script.onload = function(e){
callBack();
}
}
script.src = url;
document.body.appendChild(script);
}
~~~
###避免使用 jQuery 實現動畫
1. 禁止使用 slideUp/Down() fadeIn/fadeOut() 等方法;
2. 盡量不使用 animate() 方法;
3. 對于移動端瀏覽器,盡量使用CSS3動畫或者canvas