javascript是否使用不可变或可变的字符串?我需要一个“字符串构建器”吗?
答案 0 :(得分:272)
他们是不可改变的。您无法使用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.push
和Array.join
Array.push
,然后使用Array.join
然后我通过将它们抽象为StringBuilderConcat
,StringBuilderArrayPush
和StringBuilderArrayIndex
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
Firefox排名第二,Array.join
仅在FF中略慢,但在Chrome中速度相当慢(3x)。
Chrome排名第三,但字符串concat比Array.join快3倍
创建StringBuilder似乎不会影响性能。
希望别人找到这个有用的
不同的测试用例
由于@RoyTinker认为我的测试有缺陷,我创建了一个新的案例,它不会通过连接相同的字符串来创建一个大字符串,它为每次迭代使用不同的字符。字符串连接似乎仍然更快或更快。让我们让这些测试运行起来。
我建议每个人都应该继续考虑其他测试方法,并随时为下面的不同测试用例添加新的链接。
答案 1 :(得分:43)
来自rhino book:
在JavaScript中,字符串是不可变对象,这意味着 其中的字符可能无法更改,也不能进行任何操作 字符串实际上创建新字符串字符串由。分配 参考,而不是价值。通常,当分配对象时 引用,通过一个引用对对象进行的更改将是 通过对象的所有其他引用可见。因为字符串 但是,您可以对a进行多次引用 字符串对象并不担心字符串值会在没有的情况下发生变化 你知道吗
答案 2 :(得分:20)
效果提示:
如果必须连接大字符串,请将字符串部分放入数组中,并使用Array.Join()
方法获取整个字符串。对于连接大量字符串,这可以快许多倍。
JavaScript中没有StringBuilder
。
答案 3 :(得分:2)
字符串是不可变的 - 它们无法更改,我们只能创建新的字符串。
示例:
git branch --contains <acommit>
答案 4 :(得分:2)
字符串类型值是不可变的,但是 String对象(使用String()构造函数创建)是可变的,因为它是一个对象,您可以向其添加新属性。
> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }
同时,虽然您可以添加新属性,但无法更改现有属性
A screenshot of a test in Chrome console
总之, 1.所有字符串类型值(基本类型)都是不可变的。 2. String对象是可变的,但它包含的字符串类型值(基本类型)是不可变的。
答案 5 :(得分:2)
只需要澄清像我这样的简单思想(来自MDN):
不可变对象是对象一旦创建便无法更改状态的对象。
字符串和数字是不可变的。
不可变表示:
您可以使变量名指向一个新值,但先前的值仍保留在内存中。因此需要进行垃圾收集。
var immutableString = "Hello";
//在上面的代码中,创建了一个带有字符串值的新对象。
immutableString = immutableString + "World";
//我们现在将“世界”附加到现有值上。
这看起来像我们正在对字符串'immutableString'进行突变,但事实并非如此。相反:
在将“ immutableString”附加到字符串值后,会发生以下事件:
- 检索“ immutableString”的现有值
- “世界”被附加到“ immutableString”的现有值上
- 然后将结果值分配给新的内存块
- “ immutableString”对象现在指向新创建的内存空间
- 以前创建的内存空间现在可用于垃圾回收。
答案 6 :(得分:1)
关于你在ASP.NET Ajax中关于StringBuilder的问题(在您对Ash的回复的评论中),专家似乎对此不一致。
Christian Wenz在他的书“编程ASP.NET AJAX ”(O'Reilly)中说“这种方法对内存没有任何可测量的影响(实际上,实现似乎更慢了)比标准方法。“
另一方面,Gallo等人在他们的书 ASP.NET AJAX in Action (Manning)中说“当连接的字符串数量越大时,字符串构建器就成为了一个必不可少的对象。避免巨大的性能下降。“
我猜您需要进行自己的基准测试,结果也可能因浏览器而异。但是,即使它没有提高性能,对于那些习惯使用C#或Java等语言编写StringBuilders的程序员来说,它仍然可能被认为是“有用的”。
答案 7 :(得分:1)
这是一篇较晚的文章,但我在答案中找不到合适的书名。
除了可靠的书之外,这是肯定的:
字符串在ECMAScript中是不可变的,这意味着一旦创建它们,它们的值就无法更改。要更改变量保存的字符串,必须销毁原始字符串,并用另一个包含新值的字符串填充该变量... -适用于Web开发人员的专业JavaScript,第3版,第43页
现在,引用Rhino书摘录的答案是关于字符串不变性的,但是说“字符串是通过引用而不是值分配的”是错误的。 (可能他们最初打算用相反的方式表示这些词)。
在“专业JavaScript”的“原始和参考值”一章中澄清了“参考/值”的误解:
与对象相反的五种基本类型... [是]:未定义,空,布尔值,数字和字符串。据说这些变量是按值访问的,因为您正在操纵存储在变量中的实际值。 -适用于Web开发人员的专业JavaScript,第3版,第85页
:
操作对象时,您实际上是在对该对象进行引用,而不是实际对象本身。因此,据说可以通过引用访问这些值。-Web开发人员的专业JavaScript,第3版,第85页
答案 8 :(得分:0)
JavaScript字符串确实是不可变的。
答案 9 :(得分:-2)
Javascript中的字符串是不可变的