JavaScript是否使用不可变或可变的字符串? 我需要一个"字符串生成器"吗?
他们是一成不变的。您不能使用var myString ="abbdef"; myString[2] = 'c'之类的内容来更改字符串中的字符。字符串处理方法(例如trim,slice)返回新的字符串。
同样,如果您有两个引用相同的字符串,则修改一个不会影响另一个
1 2 3
| let a = b ="hello";
a = a +" world";
// b is not affected |
但是,我总是听到Ash在他的回答中提到的内容(使用Array.join进行连接的速度更快),因此我想测试一下连接字符串并将最快的方法抽象为StringBuilder的不同方法。我写了一些测试,看这是否正确(不是!)。
我一直认为这是最快的方法,尽管我一直认为添加方法调用可能会使它变慢。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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次操作/秒为单位(越高越好)
1 2 3 4 5 6 7
| | 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中则慢得多(3x)。
-
Chrome排名第三,但字符串concat的速度比Array.join快3倍
-
创建一个StringBuilder似乎不会对性能产生太大影响。
希望其他人觉得这有用
不同的测试用例
由于@RoyTinker认为我的测试存在缺陷,因此我创建了一个新案例,该案例无法通过连接相同的字符串来创建大字符串,因此每次迭代都使用不同的字符。字符串连接似乎仍然更快或更快。让我们运行这些测试。
我建议每个人都应该继续思考其他测试方法,并随时在下面的不同测试用例中添加新链接。
http://jsperf.com/string-concat-without-sringbuilder/7
来自犀牛的书:
In JavaScript, strings are immutable objects, which means that the
characters within them may not be changed and that any operations on
strings actually create new strings. Strings are assigned by
reference, not by value. In general, when an object is assigned by
reference, a change made to the object through one reference will be
visible through all other references to the object. Because strings
cannot be changed, however, you can have multiple references to a
string object and not worry that the string value will change without
your knowing it
性能提示:
如果必须连接较大的字符串,请将字符串部分放入数组中,并使用Array.Join()方法获取整个字符串。连接大量字符串的速度可能快许多倍。
JavaScript中没有StringBuilder。
只是为了澄清像我这样的简单想法(来自MDN):
Immutables are the objects whose state cannot be changed once the object is created.
String and Numbers are Immutable.
不可变意味着:
You can make a variable name point to a new value, but the previous value is still held in memory. Hence the need for garbage collection.
var immutableString ="Hello";
// In the above code, a new object with string value is created.
immutableString = immutableString +"World";
// We are now appending"World" to the existing value.
看起来我们正在对字符串'immutableString'进行突变,但事实并非如此。代替:
On appending the"immutableString" with a string value, following events occur:
Existing value of"immutableString" is retrieved
"World" is appended to the existing value of"immutableString"
The resultant value is then allocated to a new block of memory
"immutableString" object now points to the newly created memory space
Previously created memory space is now available for garbage collection.
字符串类型的值是不可变的,但是使用String()构造函数创建的String对象是可变的,因为它是一个对象,您可以向其添加新属性。
1 2 3 4 5 6 7 8
| > 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对象是可变的,但是它包含的字符串类型值(原始类型)是不可变的。
字符串是不可变的-它们不能更改,我们只能制造新的字符串。
例:
1 2 3
| var str="Immutable value"; // it is immutable
var other= statement.slice(2, 10); // new string |
这是一个较晚的帖子,但是我在答案中找不到很好的书名。
除了可靠的书,这是肯定的:
Strings are immutable in ECMAScript, meaning that once they are created, their values cannot change. To change the string held by a variable, the original string must be destroyed and the variable filled with another string containing a new value...
—Professional JavaScript for Web Developers, 3rd Ed., p.43
现在,引用Rhino书摘录的答案是关于字符串不变性的,但是说"字符串是通过引用而不是值分配的"是错误的。 (可能它们最初的意思是相反地使用)。
"参考/值"的误解在"专业JavaScript"的"原始和参考值"一章中得以阐明:
The five primitive types...[are]: Undefined, Null, Boolean, Number, and String. These variables are said to be accessed by value, because you are manipulating the actual value stored in the variable.
—Professional JavaScript for Web Developers, 3rd Ed., p.85
与对象相反:
When you manipulate an object, you’re really working on a reference to that object rather than the actual object itself. For this reason, such values are said to be accessed by reference.—Professional JavaScript for Web Developers, 3rd Ed., p.85
关于您关于ASP.NET Ajax中的StringBuilder的问题(在您对Ash的回应的评论中),专家似乎对此表示不同意见。
克里斯汀·温兹(Christian Wenz)在他的《 Programming ASP.NET AJAX(O'Reilly)》一书中说:"这种方法对内存没有任何可测量的影响(实际上,该实现似乎比标准方法稍慢一点)"。
另一方面,盖洛(Gallo)等人在他们的书《 ASP.NET AJAX in Action(Manning)》中说:"当要连接的字符串数量较大时,字符串构建器将成为避免性能大幅下降的重要对象。"
我猜您需要进行自己的基准测试,并且浏览器之间的结果可能也会有所不同。但是,即使它不能提高性能,对于习惯使用C#或Java之类的StringBuilders进行编码的程序员来说,它仍然可能被认为"有用"。
JavaScript字符串确实是不可变的。
Javascript中的字符串是不可变的