JavaScript有内置的stringbuilder类吗?

262
我看到了一些《The Code Project》的解决方案。
但是在JavaScript中有没有常规的实现呢?

阅读http://blog.codeeffects.com/Article/String-Builder-In-Java-Script。 - Kizz
11个回答

393

如果您需要为Internet Explorer编写代码,请确保选择使用数组连接的实现。在IE上,使用加号(+)或加等号(+=)运算符连接字符串非常缓慢。特别是在IE6上更是如此。在现代浏览器中,+=通常与数组连接一样快。

当我需要进行大量字符串连接时,我通常会填充一个数组而不是使用字符串构建器类:

var html = [];
html.push(
  "<html>",
  "<body>",
  "bla bla bla",
  "</body>",
  "</html>"
);
return html.join("");

请注意push方法可以接收多个参数。


10
如果你在内联生成输出,或者所有成员都是文字,[foo(), "bar", "baz"].join(""); 也可以使用。 - Anonymous
2
请注意,此处的IE指的是IE 7及更早版本。在IE 8+中,字符串连接与其他浏览器相比并不差。 - Sam Hanes
这听起来不错,直到分配额外空间不是一个选项,因为它将直接依赖于输入数量,从而创建不必要的O(n)空间复杂度。必须避免这种情况。 - TrickOrTreat
@TrickOrTreat 我认为这是必要的,因为你要么接受字符串拼接的性能问题,要么依赖于内存。在C#和Java中,字符串构建器本质上就是这样做的,只不过使用的是字符数组。 - undefined

73

4
谢谢你。这条回复应该在答案列表中优先显示。在IE10上速度也要快得多(我知道那不是现代浏览器,但我提到它是为了让任何可能看到这条信息的NMCI开发人员知道)。 - James Wilson
1
@Andreas 我认为你的测试正在触发 Chrome 中一个代码路径,因为它从未执行实际的连接操作,因为字符串从未被读取。即使强制执行,执行速度仍然相当快:https://jsperf.com/yet-another-string-concat-test/1 - Joseph Lennox
3
JSPerf已经停止维护了。我创建了一个jsbench,它在我的浏览器(Safari 14)中确认了相同的结果:https://jsben.ch/SrMoI - Han Seoul-Oh
这个结果可能会误导,因为数组扩展以接受新成员的方式对连接测试的性能有影响。将数组设置为预定义的起始容量,并在数组满时将其大小加倍,可能比每次追加另一个字符串时只调用array.push要高效得多。 - user3308241
1
http://jsperf.com链接的症状是“此部署已被禁用... 429:TOO_MANY_REQUESTS”。 - Peter Mortensen

50

不,JavaScript没有内置支持构建字符串的方法。您需要使用字符串连接代替。

当然,您可以创建一个包含字符串不同部分的数组,然后在该数组上调用 join() 方法,但这取决于您所使用的 JavaScript 解释器中如何实现该方法。

我做了一个实验来比较 str1+str2 方法和 array.push(str1, str2).join() 方法的速度。代码很简单:

var iIterations =800000;
var d1 = (new Date()).valueOf();
str1 = "";
for (var i = 0; i<iIterations; i++)
    str1 = str1 + Math.random().toString();
var d2 = (new Date()).valueOf();
log("Time (strings): " + (d2-d1));

var d3 = (new Date()).valueOf();
arr1 = [];
for (var i = 0; i<iIterations; i++)
    arr1.push(Math.random().toString());
var str2 = arr1.join("");
var d4 = (new Date()).valueOf();
log("Time (arrays): " + (d4-d3));

我在 Windows 7 x64 上用 Internet Explorer 8 和 Firefox 3.5.5 进行了测试。

一开始,我测试了少量迭代(几百或几千个项目)。结果不可预测(有时字符串连接花费 0 毫秒,有时花费 16 毫秒,数组连接也是如此)。

当我将次数增加到 50,000 时,不同浏览器的结果不同 - 在 Internet Explorer 中,字符串连接更快(94 毫秒),而连接的速度较慢(125 毫秒);而在 Firefox 中,数组连接比字符串连接更快(113 毫秒比 117 毫秒)。

然后我将次数增加到 500,000。现在在两个浏览器中,array.join() 比字符串连接:Internet Explorer 中字符串连接为 937 毫秒,Firefox 中为 1155 毫秒,而数组连接则分别为 Internet Explorer 中的 1265 毫秒和 Firefox 中的 1207 毫秒。

在 Internet Explorer 中,我可以测试的最大迭代次数是 850,000,否则会出现“脚本执行时间过长”的提示。然后,Internet Explorer 中的字符串连接为 1593,数组连接为 2046,而 Firefox 中的字符串连接为 2101,数组连接为 2249。

结果 - 如果迭代次数很小,则可以尝试使用 array.join(),因为在 Firefox 中可能更快。当次数增加时,string1+string2 方法更快。

更新

我在 Windows XP 上测试了 Internet Explorer 6,如果我尝试对超过 100,000 次的迭代进行测试,进程会立即停止响应且永远无法结束。在 40,000 次迭代中,结果是

Time (strings): 59175 ms
Time (arrays): 220 ms

这意味着 - 如果您需要支持Internet Explorer 6,请选择array.join(),它比字符串连接方式更快。


join() 是 ECMAScript 的一部分,据我所知,每个 JavaScript 解释器都实现了它。为什么会“依赖”呢? - Eli Grey
他指的是它是如何实现的... 如果在循环中字符串不断追加而不是一次性创建,那么使用join将毫无意义。 - John
是的,那正是我想表达的意思。请原谅我的英语 ;-) 我添加了比较两个浏览器中哪种方法运行速度更快的结果。你可以看到,它们是不同的。 - naivists
2
IE6,一如既往,是个例外 :) - Gordon Tucker
14
使用IE6的人习惯了一切都运行得很缓慢。我认为他们不会责备你。 - Lodewijk

11

那段代码看起来就像你想要使用的路线,但需要进行一些更改。

你需要将append方法改成这样。我已经将其更改为接受数字0,并使其返回this,这样你就可以链接你的appends了。

StringBuilder.prototype.append = function (value) {
    if (value || value === 0) {
        this.strings.push(value);
    }
    return this;
}

2
为什么只接受非NaN数字和非空字符串?您的方法不会接受nullfalse,空字符串,undefinedNaN - Eli Grey
2
@Elijah - 我更喜欢保持我的 StringBuilder 类的纯洁性,不接受除了有效字符串和数字以外的任何内容。这只是我的个人偏好。 - Gordon Tucker

5

编辑

不,JavaScript没有内建类。不过,字符串字面量可能是一个合适的解决方案。

原始回答

JavaScript的ECMAScript 6版(又称ECMAScript 2015)引入了 字符串字面量

var classType = "stringbuilder";
var q = `Does JavaScript have a built-in ${classType} class?`;

请注意,反引号(`)而不是单引号,用于括住该字符串。


38
这个回答如何解答问题? - Peter Mortensen
@Peter Mortensen,这个答案仅仅提供了另一种构建字符串的方式。原帖并没有明确指出正在寻找什么类型的字符串构建功能。 - Theophilus
8
这并没有回答问题,完全没有。 - Massimiliano Kraus
4
我不认为它回答了问题,但有些评论确实对其他成员不友好或不够有帮助。 - Fabio Milheiro
1
@Theophilus,原帖确实指定了要寻找的字符串构建功能类型:即.NET的System.Text.StringBuilder,一种最小化分配内存的连接多个字符串的方法。 - MiffTheFox
因为这与 StringBuilder 没有任何关系,所以被踩了。 - Switch386

5

尝试使用JavaScript中的StringBuilder...

  
function StringBuilder(value) {
    this.strings = new Array();
    this.append(value);
}

StringBuilder.prototype.append = function (value) {
    if (value) {
        this.strings.push(value);
    }
}

StringBuilder.prototype.clear = function () {
    this.strings.length = 0;
}

StringBuilder.prototype.toString = function () {
    return this.strings.join("");
}

var sb = new StringBuilder();
sb.append("This is");
sb.append("much better looking");
sb.append("than using +=");

var myString = sb.toString();

sb.clear();


2
这是一个TypeScript中StringBuilder类的快速示例。
export class StringBuilder {
  private _lines: string[] = [];

  write(line: string = ""): void {
    this._lines.push(line);
  }

  writeln(line: string = ""): void {
    this._lines.push(line);
    this._lines.push("\n");
  }

  toString(): string {
    return this._lines.join("");
  }
}


您可以像下面这样使用:
const sb = new StringBuilder();
sb.write("Hello World");
sb.writeln("!");
console.log(sb.toString());

1

对于那些感兴趣的人,这里有一个替代方法来调用Array.join:

var arrayOfStrings = ['foo', 'bar'];
var result = String.concat.apply(null, arrayOfStrings);
console.log(result);

输出结果如预期的那样是字符串 'foobar'。在 Firefox 中,这种方法的性能优于 Array.join,但不及 + 连接。由于 String.concat 要求将每个段指定为单独的参数,因此调用者受到执行 JavaScript 引擎所施加的任何参数计数限制的限制。请查看 Function.prototype.apply() 的文档 以获取更多信息。

这在Chrome中会失败,因为“String.concat”未定义。相反,您可以使用''.concat.apply('', arrayOfStrings)。但这仍然是一种非常慢的方法。 - Andreas

0
当我在JavaScript中发现自己需要大量字符串拼接时,我会开始寻找模板。Handlebars.js非常适合保持HTML和JavaScript更易读。http://handlebarsjs.com

-2
在C#中,你可以这样做:
 String.Format("hello {0}, your age is {1}.",  "John",  29) 

在JavaScript中,你可以这样做:
 var x = "hello {0}, your age is {1}";
 x = x.replace(/\{0\}/g, "John");
 x = x.replace(/\{1\}/g, 29);

4
用正则表达式代替字符串拼接,我非常怀疑这样会更具性能优势。 - tic
此外,这是一个糟糕的实现。如果用于替换 {0} 的字符串包含 {1},它将会出错。 - ikegami
@ikegami 这个字符串不是一个变量,而是一个常量,因此你事先就知道它包含什么。 - sports
@运动,将所有这些内容复制并粘贴到您的代码中甚至更糟。 - ikegami
一行代码,其中$1和$2替换非捕获组:x..replace(/([\s\S]?){0}([\s\S]?){1}/g, "$1Tom$225") - T.CK
2
这并没有回答问题。StringBuilder类不是用于字符串插值的。 - Massimiliano Kraus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接