每種程序語言中都會涉及到字符竄連接,而這個小小的字符竄連接問題很可能會影響到系統的整體性能,本文主要探討JavaScript中字符串連接的性能問題。而JavaScript的核心是ECMAScript 。與其他語言類似,ECMAScript 的字符串是不可變的,即它們的值不能改變。請考慮下面的代碼:
var str = "hello "; str += "world";
實際上,這段代碼在幕后執行的步驟如下:
- 創建存儲 "hello " 的字符串。
- 創建存儲 "world" 的字符串。
- 創建存儲連接結果的字符串。
- 把 str 的當前內容復制到結果中。
- 把 "world" 復制到結果中。
- 更新 str,使它指向結果。
每次完成字符串連接都會執行步驟 2 到 6,使得這種操作非常消耗資源。如果重復這一過程幾百次,甚至幾千次,就會造成性能問題。解決方法是用 Array 對象存儲字符串,然后用 join() 方法(參數是空字符串)創建最后的字符串。想象用下面的代碼代替前面的代碼:
var arr = new Array(); arr[0] = "hello "; arr[1] = "world"; var str = arr.join("");
這樣,無論數組中引入多少字符串都不成問題,因為只在調用 join() 方法時才會發生連接操作。此時,執行的步驟如下:
- 創建存儲結果的字符串
- 把每個字符串復制到結果中的合適位置
雖然這種解決方案很好,但還有更好的方法。問題是,這段代碼不能確切反映出它的意圖。要使它更容易理解,可以用 StringBuffer 類打包該功能:
function StringBuffer () { this._strings_ = new Array(); } StringBuffer.prototype.append = function(str) { this._strings_.push(str); }; StringBuffer.prototype.toString = function() { return this._strings_.join(""); };
這段代碼首先要注意的是 strings 屬性,本意是私有屬性。它只有兩個方法,即 append() 和 toString() 方法。append() 方法有一個參數,它把該參數附加到字符串數組中,toString() 方法調用數組的 join 方法,返回真正連接成的字符串。要用 StringBuffer 對象連接一組字符串,可以用下面的代碼:
var buffer = new StringBuffer (); buffer.append("hello "); buffer.append("world"); var result = buffer.toString();
基於上面的實現我們來進行一下運行時間對比,即以“+”逐個進行字符串連接和我們封裝的工具。可用下面的代碼測試 StringBuffer 對象和傳統的字符串連接方法的性能,在chrome控制台輸入一下代碼並運行:
var d1 = new Date(); var str = ""; for (var i=0; i < 10000; i++) { str += "text"; } var d2 = new Date(); console.log("Concatenation with plus: " + (d2.getTime() - d1.getTime()) + " milliseconds"); var buffer = new StringBuffer(); d1 = new Date(); for (var i=0; i < 10000; i++) { buffer.append("text"); } var result = buffer.toString(); d2 = new Date(); console.log("Concatenation with StringBuffer: " + (d2.getTime() - d1.getTime()) + " milliseconds");
這段代碼對字符串連接進行兩個測試,第一個使用加號,第二個使用 StringBuffer 類。每個操作都連接 10000 個字符串。日期值 d1 和 d2 用於判斷完成操作需要的時間。請注意,創建 Date 對象時,如果沒有參數,賦予對象的是當前的日期和時間。要計算連接操作歷經多少時間,把日期的毫秒表示(用 getTime() 方法的返回值)相減即可。這是衡量 JavaScript 性能的常見方法。該測試的結果可以幫助您比較使用 StringBuffer 類與使用加號的效率差異。上例運行結果如下:
那么有人也許會說JavaScript的String對象中不是也封裝一個concat()方法么,我們在下面也來用concat()方法來做同樣的事情,在consoel輸入以下代碼:
var d1 = new Date(); var str = ""; for (var i=0; i < 10000; i++) { str.concat("text"); } var d2 = new Date(); console.log("Concatenation with plus: " + (d2.getTime() - d1.getTime()) + " milliseconds");
我們可以看到做10000次字符竄連接它的耗時是:
由此可以得出結論,當涉及到一定數量的字符串連接時,我們在Javascript中封裝一個類似Java中的StringBuffer對象(函數)來進行操作會在性能上得到提升。