本文深入探讨JavaScript中字符串拼接的性能差异。早期ECMAScript规范中字符串不可变导致+运算符频繁创建对象,性能较低,而StringBuffer模式通过数组join优化了连接过程。然而,随着Chrome、Firefox等现代浏览器引擎的迭代优化,+运算符的性能已大幅提升,甚至反超传统StringBuffer方案。文章通过具体代码示例和不同浏览器版本的实测数据,揭示了技术演进对代码最佳实践的影响。
JavaScript字符串不可变性与性能基础
在ECMAScript规范中,字符串被定义为不可变类型,这意味着一旦创建,其值便无法更改。当我们执行类似 `var str = "Hello "; str += "world";` 这样的操作时,实际上发生了一系列复杂的底层过程。
Js代码
代码如下:
var str = "Hello ";
str += "world";上述操作的执行步骤如下:
首先,在内存中创建存储“Hello”的字符串对象。
接着,创建存储“world”的字符串对象。
然后,开辟新的内存空间以存储连接后的结果字符串。
将变量 `str` 当前的内容复制到新创建的结果字符串中。
将“world”的内容追加复制到结果字符串中。
最后,更新变量 `str` 的引用,使其指向新创建的结果字符串。
每一次字符串连接操作都会重复执行上述步骤2至6,这种频繁的内存分配和数据复制使得该操作非常消耗资源。如果重复执行几百次甚至几千次,性能损耗将显著增加。
二、那么再看看下面的代码,来解决这种窘况
基于数组的StringBuffer优化方案
Js代码
代码如下:
var arr = new Array;
arr[0] = "Hello ";
arr[1] = "world";
var str = arr.join("");这种基于数组的连接方式执行步骤如下:
首先创建存储最终结果的字符串对象。
随后,将数组中的每个字符串直接复制到结果字符串的相应位置。
这种方式的优势在于,无论数组中引入多少个字符串,连接操作仅在调用 `join()` 方法时一次性完成,从而避免了中间过程的资源消耗。
三、觉得操作很复杂?
如果上述代码未能清晰表达意图,我们可以使用对象封装的方式来提高可读性,例如实现一个 `StringBuffer` 类:
Js代码
代码如下:
function StringBuffer() {
this._strs = new Array;
}
StringBuffer.prototype.append = function (str) {
this._strs.push(str);
};
StringBuffer.prototype.toString = function() {
this._strs.join("");
}; 现在,让我们看看如何使用这个封装好的类来操作字符串:
Js代码
代码如下:
var sb = new StringBuffer();
sb.append("Hello ");
sb.append("world");
var result = sb.toString();四、似乎色香味俱全了,但是吃下去的功效如何?
不同浏览器下的性能实测对比
Js代码
代码如下:
var tStart = new Date();
var str = "";
for(var i=0;i<10000;i++)
{
str += "text"
}
var tEnd = new Date();
document.write("原始的方法加号 拼接10000个字符串 花费时间:"+(tEnd.getTime()-tStart.getTime())+"秒");
var oSB = new StringBuffer();
tStart = new Date();
for(var i=0;i<10000;i++)
{
oSB.append("text");
}
var sRst = oSB.toString();
tEnd = new Date();
document.write("<br/>StringBuffer 拼接10000个字符串 花费时间:"+(tEnd.getTime()-tStart.getTime())+"秒");根据直觉,`StringBuffer` 应该比直接使用 `+` 运算符更快。那么实际差距究竟有多大?以下是我的测试结果:
Js代码
FF3.0.10
原始的方法加号 拼接10000个字符串 花费时间:3豪秒
StringBuffer 拼接10000个字符串 花费时间:8豪秒
IE7
原始的方法加号 拼接10000个字符串 花费时间:15豪秒
StringBuffer 拼接10000个字符串 花费时间:16豪秒
IE8
原始的方法加号 拼接10000个字符串 花费时间:15豪秒
StringBuffer 拼接10000个字符串 花费时间:16豪秒
Chrome1.0.154.46
原始的方法加号 拼接10000个字符串 花费时间:1豪秒
StringBuffer 拼接10000个字符串 花费时间:2豪秒
五、怎么回事?
技术演进对传统优化策略的影响
结果是否令人困惑?是眼睛花了,还是测试数据有误?
事实上,一切都没有错。
2006年11月出版的《JavaScript高级程序设计》第84-85页中记载了上述内容,但我的测试结果与其结论完全相反。这反映了技术的快速变革。
这是一个深刻的教训,不知道阅读这篇文章的人会有何感想。