JavaScript是否使用不可变或可变的字符串?我需要一个“字符串生成器”吗?
JavaScript是否使用不可变或可变的字符串?我需要一个“字符串生成器”吗?
Answers:
他们是一成不变的。您不能使用类似的字符来更改字符串中的字符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的4倍
Firefox位居第二,Array.join
FF的速度稍慢,但Chrome的速度却慢得多(3倍)。
Chrome排名第三,但字符串concat的速度比Array.join快3倍
创建一个StringBuilder似乎不会对性能产生太大影响。
希望其他人觉得这有用
不同的测试用例
由于@RoyTinker认为我的测试存在缺陷,因此我创建了一个新案例,该案例不会通过连接同一字符串来创建大字符串,因此每次迭代都使用不同的字符。字符串连接似乎仍然更快或一样快。让我们运行这些测试。
我建议每个人都应继续思考其他测试方法,并随时在下面的不同测试用例中添加新链接。
join
vs字符串连接的严格比较,从而在测试之前构建数组。我不认为这是可以理解的目标(并join
在内部枚举数组,因此也可以for
从join
测试中省略一个循环)。
Array.join
从犀牛书中:
在JavaScript中,字符串是不可变的对象,这意味着其中的字符可能不会更改,并且对字符串的任何操作实际上都会创建新的字符串。字符串是通过引用而不是值来分配的。通常,当通过引用分配对象时,通过一个引用对对象所做的更改将通过对该对象的所有其他引用可见。但是,由于不能更改字符串,因此可以有多个对字符串对象的引用,不必担心字符串值会在不知不觉中发生变化。
var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
String
使用字符串构造函数创建的对象是JavaScript字符串值的包装。您可以使用.valueOf()
函数访问盒装类型的字符串值-对于Number
对象和数字值也是如此。重要的是要注意String
使用创建的对象new String
不是实际的字符串,而是字符串的包装或框。见es5.github.io/#x15.5.2.1。有关事物如何转换为对象的信息,请参见es5.github.io/#x9.9
性能提示:
如果必须连接较大的字符串,请将字符串部分放入数组中,然后使用该Array.Join()
方法获取整个字符串。连接大量字符串的速度可能快许多倍。
StringBuilder
JavaScript中没有。
只是为了澄清像我这样的简单想法(来自MDN):
不可变对象是一旦创建对象便无法更改其状态的对象。
字符串和数字是不可变的。
不可变意味着:
您可以使变量名称指向新值,但先前的值仍保留在内存中。因此需要垃圾收集。
var immutableString = "Hello";
//在上面的代码中,创建了一个带有字符串值的新对象。
immutableString = immutableString + "World";
//现在,我们将“世界”附加到现有值上。
看起来我们正在对字符串'immutableString'进行突变,但事实并非如此。代替:
在将“ immutableString”附加到字符串值后,将发生以下事件:
- 检索“ immutableString”的现有值
- “世界”附加到“ immutableString”的现有值之后
- 然后将结果值分配给新的内存块
- 现在,“ immutableString”对象指向新创建的内存空间
- 以前创建的内存空间现在可用于垃圾回收。
字符串类型的值是不可变的,但是使用String()构造函数创建的String对象是可变的,因为它是一个对象,您可以向其添加新属性。
> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }
同时,尽管可以添加新属性,但是不能更改已经存在的属性
总之,1.所有字符串类型值(原始类型)都是不可变的。2. String对象是可变的,但是它包含的字符串类型值(原始类型)是不可变的。
new String
生成围绕不变字符串的可变包装器
String
对象(包装器)添加新属性,这意味着它不是不可变的(默认情况下;与其他任何对象一样,您可以对其进行调用Object.freeze
以使其变为不可变的)。但是,无论是否包含在String
对象包装器中,原始字符串值类型始终是不可变的。
关于您在ASP.NET Ajax中关于StringBuilder的问题(在对Ash的回应的评论中),专家似乎对此表示不同意见。
克里斯汀·温兹(Christian Wenz)在他的《Programming ASP.NET AJAX(O'Reilly)》一书中说:“这种方法对内存没有任何可测量的影响(实际上,该实现似乎比标准方法稍慢一些)”。
另一方面,Gallo等人在他们的书《ASP.NET AJAX in Action(Manning)》中说:“当要连接的字符串数量较大时,字符串构建器将成为避免性能大幅下降的重要对象。”
我猜您需要进行自己的基准测试,并且浏览器之间的结果可能也会有所不同。但是,即使它不能提高性能,对于习惯使用C#或Java之类的StringBuilders进行编码的程序员来说,它仍然可能被认为“有用”。
这是一个较晚的帖子,但是我在答案中找不到一本好书。
除了可靠的书,这是肯定的:
字符串在ECMAScript中是不可变的,这意味着一旦创建它们,它们的值就无法更改。要更改变量保存的字符串,必须销毁原始字符串,并用另一个包含新值的字符串填充该变量... — Web开发人员的专业JavaScript,第3版,第43页
现在,引用Rhino书摘录的答案是关于字符串不变性的,但是说“字符串是通过引用而不是值分配的”是错误的。(可能原本是要相反地使用这些词)。
在“专业JavaScript”的“原始和参考值”一章中澄清了“参考/值”的误解:
五个基本类型... [是]:未定义,空,布尔值,数字和字符串。据说这些变量是按值访问的,因为您正在操纵存储在变量中的实际值。 — Web开发人员的专业JavaScript,第3版,第85页
与对象相反:
当操作一个对象时,实际上是在对该对象的引用而不是实际对象本身上进行引用。因此,据说这些值是通过引用访问的。— Web开发人员的专业JavaScript,第3版,第85页
JavaScript字符串确实是不可变的。
Javascript中的字符串是不可变的