JavaScript字符串是不可变的吗?我是否需要一个“字符串构建器”;在JavaScript中?

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

97057 次浏览

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

Javascript中的字符串是不可变的

犀牛书:

在JavaScript中,字符串是不可变的对象,这意味着 其中的字符不能被改变,任何操作 字符串实际上会创建新的字符串。字符串由 引用,而不是通过值。通常,当对象被赋值时 引用,通过一个引用对对象所做的更改将为 通过对该对象的所有其他引用可见。因为字符串 属性的多个引用不能更改,但是可以对 字符串对象,不用担心字符串值会没有变化 你知道它

性能提示:

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

JavaScript中没有StringBuilder

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

Christian Wenz在他的书编程ASP。净AJAX (O'Reilly)中说:“这种方法对内存没有任何可测量的影响(事实上,实现似乎比标准方法慢一点)。”

另一方面,Gallo等人在他们的书ASP。NET AJAX的应用 (Manning)中说:“当要连接的字符串数量更大时,字符串构建器成为避免巨大性能下降的基本对象。”

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

它们是不可变的。你不能用var myString = "abbdef"; myString[2] = 'c'之类的东西来改变字符串中的字符。字符串操作方法如trimslice返回新字符串。

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

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

然而,我总是听到Ash在他的回答中提到的(使用Array。所以我想测试连接字符串的不同方法,并将最快的方法抽象到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"连接10万次到一个空字符串组成。

我创建了三种类型的测试

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

然后我通过将它们抽象为StringBuilderConcatStringBuilderArrayPushStringBuilderArrayIndex http://jsperf.com/string-concat-without-sringbuilder/5创建了相同的三个测试,请去那里运行测试,这样我们就可以得到一个很好的示例。请注意,我修复了一个小错误,因此测试数据被擦除,一旦有足够的性能数据,我将更新表。到http://jsperf.com/string-concat-without-sringbuilder/5找到旧的数据表。

以下是一些数字(Ma5rch 2018最新更新),如果你不想关注链接。每次测试的数值为每秒1000次操作(越高越好)

浏览器 指数 Concat SBIndex SBPush SBConcat
Chrome 71.0.3578 988 1006 2902 963 1008 2902
火狐65 1979 1902 2197 1917 1873 1953
边缘 593 373 952 361 415 444
爆炸者11 655 532 761 537 567 387
歌剧58.0.3135 1135 1200 4357 1137 1188 4294

发现

  • 现在,所有的常绿浏览器都能很好地处理字符串连接。Array.join只帮助IE 11

  • 总的来说,Opera是最快的,是Array.join的4倍

  • 火狐排名第二,Array.join在FF中仅略慢,但在Chrome中相当慢(3x)。

  • Chrome排名第三,但string concat比Array.join快3倍

  • 创建一个StringBuilder似乎对性能影响不大。

希望其他人会觉得这个有用

不同的测试用例

因为@RoyTinker认为我的测试是有缺陷的,我创建了一个新的情况下,不创建一个大字符串连接相同的字符串,它使用不同的字符为每次迭代。字符串连接似乎还是更快,或者一样快。让我们进行测试

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

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

字符串是不可变的 -它们不能改变,我们只能创建新的字符串。

例子:

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控制台测试截图

结论< p >, 1. 所有字符串类型值(基本类型)都是不可变的。 2. String对象是可变的,但它包含的字符串类型值(基本类型)是不可变的

这是一个很晚的帖子,但我没有在答案中找到一个好的书的引用。

这是一本可靠的书中明确的注释:

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

现在,引用Rhino书摘录的答案是正确的关于字符串不可变性,但错误的说“字符串是通过引用分配的,而不是通过值。”(可能他们最初的意思是反其道而行之)。

“引用/值”的误解在“专业JavaScript”的“原语和参考值”一章中得到了澄清:

五种基本类型…[是]:未定义,空,布尔,数字和字符串。这些变量被称为通过值访问,因为您正在操作存储在变量中的实际值。 - Web开发人员专业JavaScript,第3版,第85页

对象相反:

当您操作一个对象时,实际上是在处理对该对象的引用,而不是实际的对象本身。因此,这样的值被称为通过reference.-面向Web开发者的专业JavaScript,第3版,第85页访问

只是为了澄清像我这样头脑简单的人(from 中数):

不可变对象是一旦对象被创建,其状态就不能改变的对象。

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

不可变意味着:

你可以让一个变量名指向一个新的值,但是之前的值仍然保存在内存中。因此需要垃圾收集。

var immutableString = "Hello";

//在上面的代码中,创建了一个string值的新对象。

immutableString = immutableString + "World";

//我们现在追加“;world &;到现有值。

这看起来像是我们在改变字符串'immutableString',但我们没有。而不是:

关于添加“immutablestring”;使用字符串值,会发生以下事件:

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