javascript 使用不可变字符串还是可变字符串?我需要“字符串生成器”吗?
JavaScript 字符串是不可变的吗?我需要 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.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 次操作/秒(越高越好)
浏览器 | 指数 | 推 | 康卡特 | 指数 | SBP推送 | SBConcat |
---|---|---|---|---|---|---|
铬 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 倍
Firefox 位居第二,
Array.join
在 FF 中仅稍慢,但在 Chrome 中慢得多(3 倍)。Chrome 排第三,但字符串 concat 比 Array.join 快 3 倍
创建 StringBuilder 似乎不会对性能产生太大影响。
希望其他人觉得这很有用
不同的测试用例
由于@RoyTinker 认为我的测试有缺陷,因此我创建了一个新案例,该案例不会通过连接相同的字符串来创建大字符串,它为每次迭代使用不同的字符。字符串连接似乎仍然更快或同样快。让我们运行这些测试。
我建议每个人都应该继续考虑其他方法来测试这个,并随时添加到下面不同测试用例的新链接。
来自犀牛书:
在 JavaScript 中,字符串是不可变对象,这意味着其中的字符可能不会改变,并且对字符串的任何操作实际上都会创建新的字符串。字符串是按引用分配的,而不是按值分配的。通常,当对象通过引用赋值时,通过一个引用对对象所做的更改将通过对该对象的所有其他引用可见。但是,由于字符串无法更改,因此您可以对一个字符串对象进行多次引用,而不必担心字符串值会在您不知情的情况下发生更改
性能提示:
如果必须连接大字符串,请将字符串部分放入数组并使用该Array.Join()
方法获取整个字符串。对于连接大量字符串,这可以快很多倍。
StringBuilder
JavaScript 中没有。
只是为了澄清像我这样的简单头脑(来自MDN):
不可变对象是一旦创建对象其状态就无法更改的对象。
字符串和数字是不可变的。
不可变意味着:
您可以使变量名称指向一个新值,但先前的值仍保留在内存中。因此需要垃圾收集。
var immutableString = "Hello";
// 在上面的代码中,创建了一个带有字符串值的新对象。
immutableString = immutableString + "World";
// 我们现在将“World”附加到现有值。
这看起来我们正在改变字符串 'immutableString',但我们没有。反而:
将字符串值附加到“immutableString”后,会发生以下事件:
- 检索“immutableString”的现有值
- “World”附加到“immutableString”的现有值
- 然后将结果值分配给新的内存块
- “immutableString”对象现在指向新创建的内存空间
- 以前创建的内存空间现在可用于垃圾收集。
string 类型的值是不可变的,但是使用 String() 构造函数创建的 String 对象是可变的,因为它是一个对象,您可以向其添加新属性。
> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }
同时,虽然你可以添加新的属性,但你不能改变已经存在的属性
总结一下,1.所有的字符串类型值(primitive type)都是不可变的。2. String 对象是可变的,但它包含的字符串类型值(原始类型)是不可变的。