JavaScript 字符串是不可变的吗?我需要 JavaScript 中的“字符串生成器”吗?

StackOverflow https://stackoverflow.com/questions/51185

  •  09-06-2019
  •  | 
  •  

javascript 使用不可变字符串还是可变字符串?我需要“字符串生成器”吗?

有帮助吗?

解决方案

它们是一成不变的。您不能使用类似的内容更改字符串中的字符 var myString = "abbdef"; myString[2] = 'c'. 。字符串操作方法如 trim, slice 返回新字符串。

同样,如果对同一字符串有两个引用,则修改其中一个不会影响另一个

let a = b = "hello";
a = a + " world";
// b is not affected

但是,我一直听到 Ash 在他的回答中提到的内容(使用 Array.join 的连接速度更快),所以我想测试连接字符串的不同方法并将最快的方法抽象为 StringBuilder。我写了一些测试来看看这是否属实(事实并非如此!)。

这是我认为最快的方法,尽管我一直认为添加方法调用可能会使其变慢......

function StringBuilder() {
    this._array = [];
    this._index = 0;
}

StringBuilder.prototype.append = function (str) {
    this._array[this._index] = str;
    this._index++;
}

StringBuilder.prototype.toString = function () {
    return this._array.join('');
}

以下是性能速度测试。它们三个都创建了一个由连接组成的巨大字符串 "Hello diggity dog" 十万次进入空字符串。

我创建了三种类型的测试

  • 使用 Array.pushArray.join
  • 使用数组索引来避免 Array.push, ,然后使用 Array.join
  • 直字符串连接

然后我通过将它们抽象为来创建相同的三个测试 StringBuilderConcat, StringBuilderArrayPushStringBuilderArrayIndex http://jsperf.com/string-concat-without-sringbuilder/5 请去那里进行测试,以便我们可以获得一个好的样本。请注意,我修复了一个小错误,因此测试数据被擦除,一旦有足够的性能数据,我将更新表。去 http://jsperf.com/string-concat-without-sringbuilder/5 对于旧数据表。

如果您不想点击链接,这里有一些数字(Ma5rch 2018 中的最新更新)。每次测试的数量为 1000 次操作/秒 (越高越好)

| Browser          | Index | Push | Concat | SBIndex | SBPush | SBConcat |
---------------------------------------------------------------------------
| Chrome 71.0.3578 | 988   | 1006 | 2902   | 963     | 1008   | 2902     |
| Firefox 65       | 1979  | 1902 | 2197   | 1917    | 1873   | 1953     |
| Edge             | 593   | 373  | 952    | 361     | 415    | 444      |
| Exploder 11      | 655   | 532  | 761    | 537     | 567    | 387      |
| Opera 58.0.3135  | 1135  | 1200 | 4357   | 1137    | 1188   | 4294     | 

发现

  • 如今,所有常青浏览器都可以很好地处理字符串连接。 Array.join 仅支持 IE 11

  • 总体而言,Opera 是最快的,是 Array.join 的 4 倍

  • 火狐浏览器位居第二, Array.join 在 FF 中仅稍微慢一些,但在 Chrome 中则慢得多(3 倍)。

  • Chrome 排名第三,但字符串连接比 Array.join 快 3 倍

  • 创建 StringBuilder 似乎不会对性能产生太大影响。

希望其他人发现这很有用

不同的测试用例

由于@RoyTinker 认为我的测试有缺陷,所以我创建了一个新案例,它不会通过连接相同的字符串来创建大字符串,而是为每次迭代使用不同的字符。字符串连接似乎仍然更快或同样快。让我们运行这些测试。

我建议每个人都应该继续考虑其他方法来测试这一点,并随意添加到下面不同测试用例的新链接。

http://jsperf.com/string-concat-without-sringbuilder/7

其他提示

来自 犀牛书:

在JavaScript中,字符串是不变的对象,这意味着它们内部的字符可能不会更改,并且在字符串上的任何操作实际上都会创建新字符串。字符串是通过引用分配的,而不是通过值分配。通常,当通过引用分配对象时,通过对对象的所有其他引用可以通过一个参考来对象进行更改。但是,由于无法更改字符串,因此您可以对字符串对象有多个引用,而不必担心字符串值会在不知道的情况下更改

性能提示:

如果必须连接大字符串,请将字符串部分放入数组中并使用 Array.Join() 方法来获取整个字符串。对于连接大量字符串来说,这可以快很多倍。

没有 StringBuilder 在 JavaScript 中。

字符串是不可变的 – 它们无法改变,我们只能制造新的琴弦。

例子:

var str= "Immutable value"; // it is immutable

var other= statement.slice(2, 10); // new string

字符串类型值是不可变的, 使用 String() 构造函数创建的 String 对象是可变的,因为它是一个对象,您可以向它添加新属性。

> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }

同时,虽然您可以添加新属性,但无法更改已经存在的属性

Chrome 控制台中的测试屏幕截图

总之,1。所有字符串类型值(原始类型)都是不可变的。2.String 对象是可变的,但它包含的字符串类型值(原始类型)是不可变的。

只是为了像我这样简单的头脑澄清(来自 MDN):

不可变对象是指一旦创建对象,其状态就无法更改的对象。

字符串和数字是不可变的。

不可变意味着:

您可以使变量名称指向新值,但以前的值仍保留在内存中。因此需要垃圾收集。

var immutableString = "Hello";

// 在上面的代码中,创建了一个具有字符串值的新对象。

immutableString = immutableString + "World";

// 我们现在将“World”附加到现有值中。

看起来我们正在改变字符串“immutableString”,但事实并非如此。反而:

在向“immutableString”附加字符串值时,会发生以下事件:

  1. 检索“immutableString”的现有值
  2. “World”被附加到“immutableString”的现有值上
  3. 然后将结果值分配给新的内存块
  4. “immutableString”对象现在指向新创建的内存空间
  5. 以前创建的内存空间现在可用于垃圾回收。

关于您关于 ASP.NET Ajax 中的 StringBuilder 的问题(在您对 Ash 的回复的评论中),专家们似乎不同意这一点。

克里斯蒂安·温茨在他的书中说 ASP.NET AJAX 编程 (O'Reilly)“这种方法对内存没有任何可测量的影响(事实上,实现似乎比标准方法慢一点)。”

另一方面,加洛等人在他们的书中说 ASP.NET AJAX 实际应用 (曼宁)“当要连接的字符串数量较大时,字符串生成器就成为避免性能大幅下降的重要对象。”

我想您需要进行自己的基准测试,并且浏览器之间的结果也可能有所不同。然而,即使它没有提高性能,对于习惯使用 C# 或 Java 等语言使用 StringBuilder 进行编码的程序员来说,它仍然可能被认为是“有用的”。

这是一篇迟到的帖子,但我在答案中没有找到好的书籍引用。

这是一本可靠的书中的明确例外:

字符串在 ECMAScript 中是不可变的,这意味着一旦创建它们,它们的值就不能改变。要更改变量保存的字符串,必须销毁原始字符串,并用另一个包含新值的字符串填充该变量... —Web 开发人员的专业 JavaScript,第三版,第 43 页

现在,引用Rhino Book摘录的答案是对字符串不变性的正确性,但错误地说“字符串是通过引用而不是通过价值分配的”。 (可能它们最初是为了以相反的方式将单词放置)。

“引用/值”的误解在“Professional JavaScript”的“Primitive and Reference value”章节中得到了澄清:

五种原始类型...[是]:未定义、空、布尔值、数字和字符串。这些变量被称为按值访问,因为您正在操作存储在变量中的实际值。 —Web 开发人员的专业 JavaScript,第三版,第 85 页

这反对 物体:

当你操作一个对象时,你实际上是在处理对该对象的引用,而不是实际的对象本身。因此,这些值被称为通过引用访问。—Web 开发人员的专业 JavaScript,第三版,第 85 页

JavaScript 字符串确实是不可变的。

JavaScript 中的字符串是不可变的

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top