核心導(dǎo)讀:在服裝管理軟件開發(fā)應(yīng)用過程中,特別對(duì)于使用B/S架構(gòu)開發(fā)POS系統(tǒng),提高系統(tǒng)運(yùn)行速度是關(guān)鍵。使用javascript是提高性能方式之一
很久就想總結(jié)一下關(guān)于javascript性能優(yōu)化方面的一些東西,平時(shí)也有注意收集這方面的資料。把del.icio.us里的收藏的東西翻出來看一遍,才驚奇地發(fā)現(xiàn),這些所謂的優(yōu)化方法大多出自《javascript高級(jí)程序設(shè)計(jì)》一書,當(dāng)然也有個(gè)別
不一樣的。總之這本書上關(guān)于javascript性能優(yōu)化的內(nèi)容足足用了近20頁(yè)來進(jìn)行闡述,所以今天我也照本宣科地來介紹一下,同時(shí)收錄其他方法。
javascript的性能優(yōu)化應(yīng)分為兩部分:下載時(shí)間和執(zhí)行時(shí)間。
下載時(shí)間
javascript作為一種解釋性語言不同于其他編程語言。在諸如java,c,c++的語言中,開發(fā)人員根本無需考慮變量名的長(zhǎng)度以及長(zhǎng)篇大論的注釋,因?yàn)檫@些在編譯時(shí)都會(huì)被刪除。
但是web瀏覽器下載的是javascript的源代碼,你編寫的javascript程序文件會(huì)原模原樣地下載到客戶端。而這些長(zhǎng)變量名和注釋就會(huì)影響腳本的下載時(shí)間。單個(gè)TCP-IP包中能放入的字節(jié)數(shù)是1160,所以最好將每個(gè)javascript文件的大小控制在1160字節(jié)以內(nèi)以獲取最優(yōu)的下載時(shí)間。
javascript文件中的任何字節(jié),不管是空格,還是換行都會(huì)影響javascript文件的下載時(shí)間,所以在部署任何javascript文件之前,都應(yīng)該盡可能地優(yōu)化腳本文件的體積。以下是一些常用的優(yōu)化javascript文件大小的方法:
1、刪除注釋
腳本中的任何注釋都應(yīng)該在部署之前被刪除。雖然注釋對(duì)于開發(fā)人員來說意義重大,但在部署時(shí)這些注釋對(duì)客戶端用戶是沒有任何實(shí)質(zhì)意義的,而更可怕的是很多javascript源代碼中的注釋會(huì)比代碼多得多。因此刪除注釋是所見javascript文件大小最為方便有效的途徑。
2、刪除制表符和空格
又規(guī)律地縮進(jìn)代碼有增強(qiáng)代碼的可閱讀性,但是瀏覽器/客戶端用戶并不需要這些額外的制表符和可個(gè),所以最好刪除它們,包括函數(shù)參數(shù),賦值語句和比較操作符之間的空格:
和前一條規(guī)則一樣,只要你在程序的每行結(jié)尾都正確地添加了分號(hào),那么就不需要再添加換行符了。
4、替換變量名
此方法很無聊,大致做法就是將所有變量名替換成無意義的簡(jiǎn)短變量名。
要手動(dòng)地完成以上四步,在實(shí)際中可能會(huì)有一定的難度,那么下面的一些鏈接可能對(duì)你有所幫助。
ECMAScript Cruncher:http://saltstorm.net/depo/esc/
JSMin(The JavaScript Minifier): http://www.crockford.com/javascript/jsmin.html
Online JavaScript Compressor:http://dean.edwards.name/packer/
5、替換布爾值
對(duì)于比較來說,true等譯1,false等于0。因此可以將字面量的true都用1來替換,而false用0來替換,進(jìn)而較少一定的字節(jié)數(shù)。
6、縮短否定檢測(cè)
代碼中常常會(huì)出現(xiàn)檢測(cè)某個(gè)值時(shí)候有效的語句。而大部分否定測(cè)試所做的就是判斷某個(gè)變量是否為undefined、null或者false,如下:
執(zhí)行時(shí)間
javascript是一種解釋性語言,它的執(zhí)行速度要大大慢于編譯型語言。據(jù)測(cè)試,javascript的執(zhí)行效率要比編譯型的C程序慢5000倍;比解釋型的Java慢100倍;比解釋型的Perl慢10倍。但是我們?nèi)匀豢梢酝ㄟ^一些簡(jiǎn)單的事情來提高javascript的效率,同時(shí)這也顯得更加重要。
1、使用局部變量
在函數(shù)中,總是使用var來定義變量。無論何時(shí)使用var都會(huì)在當(dāng)前的范圍類創(chuàng)建一個(gè)局部變量。如果不使用var來定義變量,那么變量會(huì)被創(chuàng)建在window范圍內(nèi),那么每次使用這個(gè)變量的時(shí)候,解釋程序都會(huì)搜索整個(gè)范圍樹。同時(shí)全局變量要在頁(yè)面從瀏覽器中卸載后才銷毀,而局部變量在函數(shù)執(zhí)行完畢即可銷毀,過多的全局變量增加了不必要的內(nèi)存消耗。
2、避免使用with語句
使用with語句能夠減少一定的代碼長(zhǎng)度,但是在使用with語句時(shí),要強(qiáng)制解釋程序不僅在范圍樹內(nèi)查找局部變量,還強(qiáng)制檢測(cè)每個(gè)變量及指定的對(duì)象,看其是否為特性。因?yàn)椋覀円部梢栽诤瘮?shù)中定義同明的變量。
3、選擇正確的算法
只要有可能就應(yīng)該用局部變量或者數(shù)字索引的數(shù)組來替代命名特性。如果命名特性要多次使用,就先將它的值存儲(chǔ)在局部變量中,避免多次使用線性算法請(qǐng)求命名特性的值。
循環(huán)在各種編程語言中得到大量應(yīng)用,所以保持循環(huán)的高效性尤為重要。按照反向的順序進(jìn)行循環(huán)迭代是一種有效的方法。
5、翻轉(zhuǎn)循環(huán)
用do..while循環(huán)來替代while循環(huán)以進(jìn)一步減少執(zhí)行時(shí)間。假設(shè)有如下while循環(huán):
這段代碼比用while循環(huán)更快,因?yàn)樗醚h(huán)反轉(zhuǎn)來進(jìn)一步地優(yōu)化:
6、展開循環(huán)
可以考慮將循環(huán)展開,一次執(zhí)行多個(gè)語句?紤]如下for循環(huán)例子:
循環(huán)總共要執(zhí)行20次,每次都是對(duì)變量sum進(jìn)行增量操作,但是可以這樣寫:
在循環(huán)體內(nèi)做了五次增量。每次增量后,都對(duì)變量i加1,所以多數(shù)組的遍歷與原來是一致的,但是控制語句只執(zhí)行了四次,減少了執(zhí)行時(shí)間。當(dāng)然還可以繼續(xù)優(yōu)化:
7、優(yōu)化if語句
使用if語句和多個(gè)else語句時(shí),一定要把最有可能的情況放在第一個(gè),然后是第二可能出現(xiàn)的情況,如此排列,這樣就減少了要進(jìn)行多次測(cè)試才能遇到正確條件的情況。
同時(shí)也要盡量減少else if語句的數(shù)量,并且將條件按照二叉樹的方式進(jìn)行排列。例如:
8、switch和if
一般來說超過兩種情況時(shí),最好使用switch語句。常用switch來代替if語句,最高可令執(zhí)行快10倍。在javascript中就更加可以從中獲益,因?yàn)閏ase語句可以使用任何類型的值。
9、避免字符串連接
一旦一次要使用多個(gè)字符串的連接(比如,大于五個(gè)),最好使用如下方式:
只要可能,就應(yīng)該考慮優(yōu)先使用內(nèi)置方法。因?yàn)閮?nèi)置方法是用C++或者C之類的語言編譯的,運(yùn)行起來比必須實(shí)時(shí)編譯的javascript要高效的多。比如你可能像要自己編寫一個(gè)求階乘的函數(shù),但是實(shí)際上你應(yīng)該使用javascript內(nèi)置的Math.pow()方法。
11、存儲(chǔ)常用的值
當(dāng)多次使用到一個(gè)值得時(shí)候,可先將其存儲(chǔ)在局部變量中以便快速訪問。尤其對(duì)于通常使用對(duì)象的特性來進(jìn)行訪問的值更加重要。如:
document.body.clientWidth在該例中被使用了兩次,但它是使用命名特性(屬于極其昂貴的操作)來獲取的。可以使用局部變量來重寫這段代碼:
12、最小化語句數(shù)量
我們有理由相信,腳本中語句越少,執(zhí)行所需要的時(shí)間越短。很多方法可以將javascript中的語句數(shù)量減到最少,比如定義變量時(shí),處理迭代數(shù)字時(shí),使用數(shù)組和對(duì)象字面量時(shí)。
多個(gè)變量的定義可用一個(gè)var語句來替代:
插入迭代子
使用迭代子(在不同位置上加減的值)時(shí),盡可能合并語句?紤]下面代碼:
13、節(jié)約使用DOM
不管是添加、刪除或者是其他對(duì)頁(yè)面DOM內(nèi)部結(jié)構(gòu)的更改,都會(huì)導(dǎo)致整個(gè)頁(yè)面的重新渲染,帶來的是明顯的時(shí)間消耗。解決這個(gè)問題的方法是盡可能地對(duì)不在DOM文檔中的元素節(jié)點(diǎn)進(jìn)行操作。如下例子:
每次執(zhí)行I都會(huì)對(duì)整個(gè)頁(yè)面重新渲染一次,執(zhí)行II又會(huì)對(duì)整個(gè)頁(yè)面重新渲染一次,總共會(huì)有20次的對(duì)頁(yè)面的渲染。我們大可使用文檔碎片來保存所有列表項(xiàng),最后再一起添加到文檔中。
其他一些優(yōu)化方法
1、不要使用eval
使用eval相當(dāng)于在運(yùn)行時(shí)再次調(diào)用解釋引擎對(duì)內(nèi)容進(jìn)行運(yùn)行,需要消耗大量時(shí)間。這時(shí)候使用JavaScript所支持的閉包可以實(shí)現(xiàn)函數(shù)模版(關(guān)于閉包的內(nèi)容請(qǐng)參考函數(shù)式編程的有關(guān)內(nèi)容)
2、類型轉(zhuǎn)換
類型轉(zhuǎn)換是大家常犯的錯(cuò)誤,因?yàn)镴avaScript是動(dòng)態(tài)類型語言,你不能指定變量的類型。
對(duì)字符串進(jìn)行循環(huán)操作,譬如替換、查找,應(yīng)使用正則表達(dá)式,因?yàn)楸旧鞪avaScript的循環(huán)速度就比較慢,而正則表達(dá)式的操作是用C寫成的語言的API,性能很好。
4、DOM相關(guān)
不一樣的。總之這本書上關(guān)于javascript性能優(yōu)化的內(nèi)容足足用了近20頁(yè)來進(jìn)行闡述,所以今天我也照本宣科地來介紹一下,同時(shí)收錄其他方法。
javascript的性能優(yōu)化應(yīng)分為兩部分:下載時(shí)間和執(zhí)行時(shí)間。
下載時(shí)間
javascript作為一種解釋性語言不同于其他編程語言。在諸如java,c,c++的語言中,開發(fā)人員根本無需考慮變量名的長(zhǎng)度以及長(zhǎng)篇大論的注釋,因?yàn)檫@些在編譯時(shí)都會(huì)被刪除。
但是web瀏覽器下載的是javascript的源代碼,你編寫的javascript程序文件會(huì)原模原樣地下載到客戶端。而這些長(zhǎng)變量名和注釋就會(huì)影響腳本的下載時(shí)間。單個(gè)TCP-IP包中能放入的字節(jié)數(shù)是1160,所以最好將每個(gè)javascript文件的大小控制在1160字節(jié)以內(nèi)以獲取最優(yōu)的下載時(shí)間。
javascript文件中的任何字節(jié),不管是空格,還是換行都會(huì)影響javascript文件的下載時(shí)間,所以在部署任何javascript文件之前,都應(yīng)該盡可能地優(yōu)化腳本文件的體積。以下是一些常用的優(yōu)化javascript文件大小的方法:
1、刪除注釋
腳本中的任何注釋都應(yīng)該在部署之前被刪除。雖然注釋對(duì)于開發(fā)人員來說意義重大,但在部署時(shí)這些注釋對(duì)客戶端用戶是沒有任何實(shí)質(zhì)意義的,而更可怕的是很多javascript源代碼中的注釋會(huì)比代碼多得多。因此刪除注釋是所見javascript文件大小最為方便有效的途徑。
2、刪除制表符和空格
又規(guī)律地縮進(jìn)代碼有增強(qiáng)代碼的可閱讀性,但是瀏覽器/客戶端用戶并不需要這些額外的制表符和可個(gè),所以最好刪除它們,包括函數(shù)參數(shù),賦值語句和比較操作符之間的空格:
function doSomething ( arg1, arg2, arg3 ){
alert(arg1 + arg2 + arg3);
}
function doSomething(arg1,arg2,arg3){alert(arg1+arg2+arg3);}
3、刪除所有換行alert(arg1 + arg2 + arg3);
}
function doSomething(arg1,arg2,arg3){alert(arg1+arg2+arg3);}
和前一條規(guī)則一樣,只要你在程序的每行結(jié)尾都正確地添加了分號(hào),那么就不需要再添加換行符了。
4、替換變量名
此方法很無聊,大致做法就是將所有變量名替換成無意義的簡(jiǎn)短變量名。
要手動(dòng)地完成以上四步,在實(shí)際中可能會(huì)有一定的難度,那么下面的一些鏈接可能對(duì)你有所幫助。
ECMAScript Cruncher:http://saltstorm.net/depo/esc/
JSMin(The JavaScript Minifier): http://www.crockford.com/javascript/jsmin.html
Online JavaScript Compressor:http://dean.edwards.name/packer/
5、替換布爾值
對(duì)于比較來說,true等譯1,false等于0。因此可以將字面量的true都用1來替換,而false用0來替換,進(jìn)而較少一定的字節(jié)數(shù)。
6、縮短否定檢測(cè)
代碼中常常會(huì)出現(xiàn)檢測(cè)某個(gè)值時(shí)候有效的語句。而大部分否定測(cè)試所做的就是判斷某個(gè)變量是否為undefined、null或者false,如下:
if(oTest != undefined){
//dosomething
}
if(oTest != null){
//dosomething
}
if(oTest != false){
//dosomething
}
//這樣寫更簡(jiǎn)潔
if(!oTest){
//dosomethin
}
7、使用數(shù)組和對(duì)象變量
//dosomething
}
if(oTest != null){
//dosomething
}
if(oTest != false){
//dosomething
}
//這樣寫更簡(jiǎn)潔
if(!oTest){
//dosomethin
}
var aTest = new Array;
var oTest = new Object;
oTest.pro1 = "pro1"
oTest.pro2 = "pro2";
和下面的代碼作用是完全相同的,但是下面的代碼可以節(jié)省更多的字節(jié)。
var oTest = new Object;
oTest.pro1 = "pro1"
oTest.pro2 = "pro2";
var aTest = [];
var oTest = {pro1:"pro1",pro2:"pro2"};
var oTest = {pro1:"pro1",pro2:"pro2"};
執(zhí)行時(shí)間
javascript是一種解釋性語言,它的執(zhí)行速度要大大慢于編譯型語言。據(jù)測(cè)試,javascript的執(zhí)行效率要比編譯型的C程序慢5000倍;比解釋型的Java慢100倍;比解釋型的Perl慢10倍。但是我們?nèi)匀豢梢酝ㄟ^一些簡(jiǎn)單的事情來提高javascript的效率,同時(shí)這也顯得更加重要。
1、使用局部變量
在函數(shù)中,總是使用var來定義變量。無論何時(shí)使用var都會(huì)在當(dāng)前的范圍類創(chuàng)建一個(gè)局部變量。如果不使用var來定義變量,那么變量會(huì)被創(chuàng)建在window范圍內(nèi),那么每次使用這個(gè)變量的時(shí)候,解釋程序都會(huì)搜索整個(gè)范圍樹。同時(shí)全局變量要在頁(yè)面從瀏覽器中卸載后才銷毀,而局部變量在函數(shù)執(zhí)行完畢即可銷毀,過多的全局變量增加了不必要的內(nèi)存消耗。
2、避免使用with語句
使用with語句能夠減少一定的代碼長(zhǎng)度,但是在使用with語句時(shí),要強(qiáng)制解釋程序不僅在范圍樹內(nèi)查找局部變量,還強(qiáng)制檢測(cè)每個(gè)變量及指定的對(duì)象,看其是否為特性。因?yàn)椋覀円部梢栽诤瘮?shù)中定義同明的變量。
3、選擇正確的算法
只要有可能就應(yīng)該用局部變量或者數(shù)字索引的數(shù)組來替代命名特性。如果命名特性要多次使用,就先將它的值存儲(chǔ)在局部變量中,避免多次使用線性算法請(qǐng)求命名特性的值。
var aValues = [1,2,3,4,5,6,7];
function testFunc(){
for(var i=0, iCount=aValues.length; i++){
alert(i + "/" + iCount + "=" + aValues[i]);
}
}
4、反轉(zhuǎn)循環(huán)function testFunc(){
for(var i=0, iCount=aValues.length; i++){
alert(i + "/" + iCount + "=" + aValues[i]);
}
}
循環(huán)在各種編程語言中得到大量應(yīng)用,所以保持循環(huán)的高效性尤為重要。按照反向的順序進(jìn)行循環(huán)迭代是一種有效的方法。
for(var i=aValues.length-1; i >= 0; i--){
//do something
}
反轉(zhuǎn)循環(huán)有利于減低算法的復(fù)雜度。它用常數(shù)0作為循環(huán)控制語句以減小執(zhí)行時(shí)間。//do something
}
5、翻轉(zhuǎn)循環(huán)
用do..while循環(huán)來替代while循環(huán)以進(jìn)一步減少執(zhí)行時(shí)間。假設(shè)有如下while循環(huán):
var i=0;
while(i < aValues.length){
//do something
i++;
}
可用do..while循環(huán)重寫上面的代碼而不改變行為:
while(i < aValues.length){
//do something
i++;
}
var i=0;
do{
//do something
i++;
}while(i < aValues.length)
do{
//do something
i++;
}while(i < aValues.length)
這段代碼比用while循環(huán)更快,因?yàn)樗醚h(huán)反轉(zhuǎn)來進(jìn)一步地優(yōu)化:
var i=aValues.length-1;
do{
//do something
i--;
}while(i>=0)
也可以將自減操作直接放進(jìn)控制語句中,以減少額外的語句。
do{
//do something
i--;
}while(i>=0)
var i=aValues.length-1;
do{
//do somethin
}while(--i>=0)
這個(gè)循環(huán)已經(jīng)被完全優(yōu)化了do{
//do somethin
}while(--i>=0)
6、展開循環(huán)
可以考慮將循環(huán)展開,一次執(zhí)行多個(gè)語句?紤]如下for循環(huán)例子:
var sum = 0;var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
for(var i=0; i<aValues.length; i++){
sum+=aValues[i];
}
for(var i=0; i<aValues.length; i++){
sum+=aValues[i];
}
循環(huán)總共要執(zhí)行20次,每次都是對(duì)變量sum進(jìn)行增量操作,但是可以這樣寫:
var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var sum = 0;
for(var i=0; i<aValues.length; i++){
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
}
var sum = 0;
for(var i=0; i<aValues.length; i++){
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
sum+=aValues[i];
i++;
}
在循環(huán)體內(nèi)做了五次增量。每次增量后,都對(duì)變量i加1,所以多數(shù)組的遍歷與原來是一致的,但是控制語句只執(zhí)行了四次,減少了執(zhí)行時(shí)間。當(dāng)然還可以繼續(xù)優(yōu)化:
var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var sum = 0;
for(var i=0; i<aValues.length; i++){
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
}
關(guān)于此算法的更多信息可以參考這里http://home.earthlink.net/~kendrasg/info/js_opt/var sum = 0;
for(var i=0; i<aValues.length; i++){
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
sum+=aValues[i++];
}
7、優(yōu)化if語句
使用if語句和多個(gè)else語句時(shí),一定要把最有可能的情況放在第一個(gè),然后是第二可能出現(xiàn)的情況,如此排列,這樣就減少了要進(jìn)行多次測(cè)試才能遇到正確條件的情況。
同時(shí)也要盡量減少else if語句的數(shù)量,并且將條件按照二叉樹的方式進(jìn)行排列。例如:
if(iNum>0){
if(iNum>10){
alert("Between 0 and 10");
}else{
if(iNum>20){
alert("Between 10 and 20");
}else{
if(iNum<30){
alert("Between 20 and 30");
}else{
alert("Greater than or equal to 30");
}
}
}
}else{
alert("Less than or equal to 0");
}
if(iNum>10){
alert("Between 0 and 10");
}else{
if(iNum>20){
alert("Between 10 and 20");
}else{
if(iNum<30){
alert("Between 20 and 30");
}else{
alert("Greater than or equal to 30");
}
}
}
}else{
alert("Less than or equal to 0");
}
8、switch和if
一般來說超過兩種情況時(shí),最好使用switch語句。常用switch來代替if語句,最高可令執(zhí)行快10倍。在javascript中就更加可以從中獲益,因?yàn)閏ase語句可以使用任何類型的值。
9、避免字符串連接
一旦一次要使用多個(gè)字符串的連接(比如,大于五個(gè)),最好使用如下方式:
var buf = new Array();
for(var i = 0; i < 100; i++){
buf.push(i.toString());
}
var all = buf.join("");
10、優(yōu)先使用內(nèi)置方法for(var i = 0; i < 100; i++){
buf.push(i.toString());
}
var all = buf.join("");
只要可能,就應(yīng)該考慮優(yōu)先使用內(nèi)置方法。因?yàn)閮?nèi)置方法是用C++或者C之類的語言編譯的,運(yùn)行起來比必須實(shí)時(shí)編譯的javascript要高效的多。比如你可能像要自己編寫一個(gè)求階乘的函數(shù),但是實(shí)際上你應(yīng)該使用javascript內(nèi)置的Math.pow()方法。
11、存儲(chǔ)常用的值
當(dāng)多次使用到一個(gè)值得時(shí)候,可先將其存儲(chǔ)在局部變量中以便快速訪問。尤其對(duì)于通常使用對(duì)象的特性來進(jìn)行訪問的值更加重要。如:
oDiv1.style.left = document.body.clientWidth;
oDiv2.style.left = document.body.clientWidth;
oDiv2.style.left = document.body.clientWidth;
document.body.clientWidth在該例中被使用了兩次,但它是使用命名特性(屬于極其昂貴的操作)來獲取的。可以使用局部變量來重寫這段代碼:
var iClientWidth = document.body.clientWidth;
oDiv1.style.left = iClientWidth;
oDiv2.style.left = iClientWidth;
oDiv1.style.left = iClientWidth;
oDiv2.style.left = iClientWidth;
12、最小化語句數(shù)量
我們有理由相信,腳本中語句越少,執(zhí)行所需要的時(shí)間越短。很多方法可以將javascript中的語句數(shù)量減到最少,比如定義變量時(shí),處理迭代數(shù)字時(shí),使用數(shù)組和對(duì)象字面量時(shí)。
多個(gè)變量的定義可用一個(gè)var語句來替代:
var iFive = 5, sColor = "red", aValues = [1,2,3], oDate = new Date();
插入迭代子
使用迭代子(在不同位置上加減的值)時(shí),盡可能合并語句?紤]下面代碼:
var sName = aValues[i];
i++;
這樣寫更簡(jiǎn)短:
i++;
var sName = aValues[i++];
13、節(jié)約使用DOM
不管是添加、刪除或者是其他對(duì)頁(yè)面DOM內(nèi)部結(jié)構(gòu)的更改,都會(huì)導(dǎo)致整個(gè)頁(yè)面的重新渲染,帶來的是明顯的時(shí)間消耗。解決這個(gè)問題的方法是盡可能地對(duì)不在DOM文檔中的元素節(jié)點(diǎn)進(jìn)行操作。如下例子:
var oUL = document.getElementById("ulItems");
for(var i=0; i<10; i++){
var oLI = document.createElement("li");
oUL.appendChild(oLI); //I
oLI.appendChild(document.createTextNode("Item "+i)); //II
}
for(var i=0; i<10; i++){
var oLI = document.createElement("li");
oUL.appendChild(oLI); //I
oLI.appendChild(document.createTextNode("Item "+i)); //II
}
每次執(zhí)行I都會(huì)對(duì)整個(gè)頁(yè)面重新渲染一次,執(zhí)行II又會(huì)對(duì)整個(gè)頁(yè)面重新渲染一次,總共會(huì)有20次的對(duì)頁(yè)面的渲染。我們大可使用文檔碎片來保存所有列表項(xiàng),最后再一起添加到文檔中。
var oUL = document.getElementById("ulItems");
var oFragment = document.createDocumentFragment();
for(var i=0; i<10; i++){
var oLI = document.createElement("li");
oLI.appendChild(document.createTextNode("Item "+i));
oFragment.appendChild(oLI);
}
oUL.appendChild(oFragment);
這樣對(duì)文檔中DOM樹的操作就只有一次。var oFragment = document.createDocumentFragment();
for(var i=0; i<10; i++){
var oLI = document.createElement("li");
oLI.appendChild(document.createTextNode("Item "+i));
oFragment.appendChild(oLI);
}
oUL.appendChild(oFragment);
其他一些優(yōu)化方法
1、不要使用eval
使用eval相當(dāng)于在運(yùn)行時(shí)再次調(diào)用解釋引擎對(duì)內(nèi)容進(jìn)行運(yùn)行,需要消耗大量時(shí)間。這時(shí)候使用JavaScript所支持的閉包可以實(shí)現(xiàn)函數(shù)模版(關(guān)于閉包的內(nèi)容請(qǐng)參考函數(shù)式編程的有關(guān)內(nèi)容)
2、類型轉(zhuǎn)換
類型轉(zhuǎn)換是大家常犯的錯(cuò)誤,因?yàn)镴avaScript是動(dòng)態(tài)類型語言,你不能指定變量的類型。
- 把數(shù)字轉(zhuǎn)換成字符串,應(yīng)用”" + 1,雖然看起來比較丑一點(diǎn),但事實(shí)上這個(gè)效率是最高的,性能上來說:(“” + ) > String() > .toString() > new String()這條其實(shí)和下面的“直接量”有點(diǎn)類似,盡量使用編譯時(shí)就能使用的內(nèi)部操作要比運(yùn)行時(shí)使用的用戶操作要快。String()屬于內(nèi)部函數(shù),所以速度很快,而.toString()要查詢?cè)椭械暮瘮?shù),所以速度遜色一些,new String()用于返回一個(gè)精確的副本。
- 浮點(diǎn)數(shù)轉(zhuǎn)換成整型,這個(gè)更容易出錯(cuò),很多人喜歡使用parseInt(),其實(shí)parseInt()是用于將字符串轉(zhuǎn)換成數(shù)字,而不是浮點(diǎn)數(shù)和整型之間的轉(zhuǎn)換,我們應(yīng)該使用Math.floor()或者M(jìn)ath.round()。另外,和第二節(jié)的對(duì)象查找中的問題不一樣,Math是內(nèi)部對(duì)象,所以Math.floor()其實(shí)并沒有多少查詢方法和調(diào)用的時(shí)間,速度是最快的。
- 對(duì)于自定義的對(duì)象,如果定義了toString()方法來進(jìn)行類型轉(zhuǎn)換的話,推薦顯式調(diào)用toString(),因?yàn)閮?nèi)部的操作在嘗試所有可能性之后,會(huì)嘗試對(duì)象的toString()方法嘗試能否轉(zhuǎn)化為String,所以直接調(diào)用這個(gè)方法效率會(huì)更高
對(duì)字符串進(jìn)行循環(huán)操作,譬如替換、查找,應(yīng)使用正則表達(dá)式,因?yàn)楸旧鞪avaScript的循環(huán)速度就比較慢,而正則表達(dá)式的操作是用C寫成的語言的API,性能很好。
4、DOM相關(guān)
- 插入HTML 很多人喜歡在JavaScript中使用document.write來給頁(yè)面生成內(nèi)容。事實(shí)上這樣的效率較低,如果需要直接插入HTML,可以找一個(gè)容器元素,比如指定一個(gè)div或者span,并設(shè)置他們的innerHTML來將自己的HTML代碼插入到頁(yè)面中。
- 創(chuàng)建節(jié)點(diǎn) 盡量避免只是使用html字符串來創(chuàng)建節(jié)點(diǎn)。因?yàn)檫@樣做既無法保證代碼的有效性,同時(shí)字符串的操作效率有極低。所以應(yīng)該是用document.createElement()方法,而如果文檔中存在現(xiàn)成的樣板節(jié)點(diǎn),應(yīng)該是用cloneNode()方法,因?yàn)槭褂胏reateElement()方法之后,你需要設(shè)置多次元素的屬性,使用cloneNode()則可以減少屬性的設(shè)置次數(shù)——同樣如果需要?jiǎng)?chuàng)建很多元素,應(yīng)該先準(zhǔn)備一個(gè)樣板節(jié)點(diǎn)。
- 定時(shí)器 如果針對(duì)的是不斷運(yùn)行的代碼,不應(yīng)該使用setTimeout,而應(yīng)該是用setInterval。setTimeout每次要重新設(shè)置一個(gè)定時(shí)器