來自知乎的問題:JavaScript 怎樣高效拼接字符串?html
請把如下用於鏈接字符串的JavaScript代碼修改成更高效的方式:前端
var htmlString ='< div class=」container」 > ' + '< ul id=」news-list」 > '; for (var i = 0; i < NEWS.length; i++) { htmlString += '< li > < a href="' +NEWS[i].LINK +'" > +NEWS[i].TITLE + '< /a > < /li >'; } htmlString += '< /ul > < /div > ';
zhiyelee的回答:
java
JS優化已經討論了不少了,最近又看到 aimingoo的一篇。大致上,aimingoo的說法都是很是正確的。
除了像aimingoo作個案研究外,這裏我想從更通常的角度總結在瀏覽器編程中JS優化的幾個原則。
首先,與其餘語言不一樣,JS的效率很大程度是取決於JS engine的效率。除了引擎實現的優劣外,引擎本身也會爲一些特殊的代碼模式採起一些優化的策略。例如FF、Opera和Safari的JS引擎,都對字符串的拼接運算(+)作了特別優化。顯然,要得到最大效率,就必需要了解引擎的脾氣,儘可能迎合引擎的口味。因此對於不一樣的引擎,所做的優化極有多是背道而馳的。
而若是作跨瀏覽器的web編程,則最大的問題是在於IE6(JScript 5.6)!由於在不打 hotfix的狀況下,JScript引擎的垃圾回收的bug,會致使其在真實應用中的performance跟其餘瀏覽器根本不在一個數量級上。所以在這種場合作優化,實際上就是爲JScript作優化!
因此第一原則就是 只須要爲IE6(未打補丁的JScript 5.6或更早版本)作優化!
若是你的程序已經優化到在IE6下能夠接受的性能,那基本上在其餘瀏覽器上性能就徹底沒有問題。
所以,注意我下面講的許多問題在其餘引擎上可能徹底不一樣,例如在循環中進行字符串拼接,一般認爲須要用Array.join的方式,可是因爲SpiderMonkey等引擎對字符串的「+」運算作了優化,結果使用Array.join的效率反而不如直接用「+」!可是若是考慮IE6,則其餘瀏覽器上的這種效率的差異根本不值一提。
JS優化與其餘語言的優化也仍然有相同之處。好比說,不要一上來就急吼吼的作優化,那樣毫無心義。優化的關鍵,仍然是要把精力放在最關鍵的地方,也就是瓶頸上。通常來講,瓶頸老是出如今大規模循環的地方。這倒不是說循環自己有性能問題,而是循環會迅速放大可能存在的性能問題。
因此第二原則就是 以大規模循環體爲最主要優化對象。
如下的優化原則,只在大規模循環中才有意義,在循環體以外作此類優化基本上是沒有意義的。
目前絕大多數JS引擎都是解釋執行的,而解釋執行的狀況下,在全部操做中,函數調用的效率是較低的。此外,過深的prototype繼承鏈或者多級引用也會下降效率。JScript中,10級引用的開銷大致是一次空函數調用開銷的1/2。這二者的開銷都遠遠大於簡單操做(如四則運算)。
因此第三原則就是 儘可能避免過多的引用層級和沒必要要的屢次方法調用。
特別要注意的是,有些狀況下看似是屬性訪問,其實是方法調用。例如全部DOM的屬性,實際上都是方法。在遍歷一個NodeList的時候,循環條件對於nodes.length的訪問,看似屬性讀取,其實是等價於函數調用的。並且IE DOM的實現上,childNodes.length每次是要經過內部遍歷從新計數的。(My god,可是這是真的!由於我測過,childNodes.length的訪問時間與childNodes.length的值成正比!)這很是耗費。因此預先把nodes.length保存到js變量,固然能夠提升遍歷的性能。
一樣是函數調用,用戶自定義函數的效率又遠遠低於語言內建函數,由於後者是對引擎本地方法的包裝,而引擎一般是c,c++,java寫的。進一步,一樣的功能,語言內建構造的開銷一般又比內建函數調用要效率高,由於前者在JS代碼的parse階段就能夠肯定和優化。
因此第四原則就是 儘可能使用語言自己的構造和內建函數。
這裏有一個例子是 高性能的String.format方法。String.format傳統的實現方式是用String.replace(regex, func),在pattern包含n個佔位符(包括重複的)時,自定義函數func就被調用n次。而這個高性能實現中,每次format調用所做的只是一次Array.join而後一次String.replace(regex, string)的操做,二者都是引擎內建方法,而不會有任何自定義函數調用。兩次內建方法調用和n次的自定義方法調用,這就是性能上的差異。
一樣是內建特性,性能上也仍是有差異的。例如在JScript中對於arguments的訪問性能就不好,幾乎遇上一次函數調用了。所以若是一個可變參數的簡單函數成爲性能瓶頸的時候,能夠將其內部作一些改變,不要訪問arguments,而是經過對參數的顯式判斷來處理。
好比:function sum() { var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; }
這個sum一般調用的時候個數是較少的,咱們但願改進它在參數較少時的性能。若是改爲:
其實並不會有多少提升,可是若是改爲:function sum(a, b, c, d, e, f, g) { var r = a ? b ? c ? d ? e ? f ? a + b + c + d + e + f : a + b + c + d + e : a + b + c + d : a + b + c : a + b : a : 0; if (g === undefined) return r; for (var i = 6; i < arguments.length; i++) { r += arguments[i]; } return r; }就會提升不少(至少快1倍)。
最後是第五原則,也每每是真實應用中最重要的性能障礙,那就是 儘可能減小沒必要要的對象建立。
自己建立對象是有必定的代價的,可是這個代價其實並不大。最根本的問題是因爲 JScript愚蠢之極的垃圾回收調度算法,致使隨着對象個數的增長,性能嚴重降低(據微軟的人本身說複雜度是O(n^2))。 好比咱們常見的字符串拼接問題,通過個人測試驗證,單純的屢次建立字符串對象其實根本不是性能差的緣由。要命的是在對象建立期間的無謂的垃圾回收的開銷。而Array.join的方式,不會建立中間字符串對象,所以就減小了那該死的垃圾回收的開銷。 所以,若是咱們能把大規模對象建立轉化爲單一語句,則其性能會獲得極大的提升!例如經過構造代碼而後eval——實際上PIES項目中正在根據這個想法來作一個專門的大規模對象產生器…… 好了上面就是偶總結的JS優化五大原則。 除了這些原則之外,還有一些特殊狀況,如DOM的遍歷,之後有時間再作討論。