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。我编写了一些测试来验证这是否正确(事实并非如此!)。
这是我认为最快的方法,避免使用push,并使用数组来存储字符串,然后在最后将它们连接起来。
class StringBuilderArrayIndex {
array = [];
index = 0;
append(str) {
this.array[this.index++] = str
}
toString() {
return this.array.join('')
}
}
我创建了两种类型的测试
Array.push
,然后使用Array.join
对于这些测试中的每一个,我循环追加一个常量值和一个随机字符串;
<script benchmark>
// Number of times to loop through, appending random chars
const APPEND_COUNT = 1000;
const STR = 'Hot diggity dizzle';
function generateRandomString() {
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const length = Math.floor(Math.random() * 10) + 1; // Random length between 1 and 10
let result = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters.charAt(randomIndex);
}
return result;
}
const randomStrings = Array.from({length: APPEND_COUNT}, generateRandomString);
class StringBuilderStringAppend {
str = '';
append(str) {
this.str += str;
}
toString() {
return this.str;
}
}
class StringBuilderArrayIndex {
array = [];
index = 0;
append(str) {
this.array[this.index] = str;
this.index++;
}
toString() {
return this.array.join('');
}
}
// @group Same string 'Hot diggity dizzle'
// @benchmark array push & join
{
const sb = new StringBuilderArrayIndex();
for (let i = 0; i < APPEND_COUNT; i++) {
sb.append(STR)
}
sb.toString();
}
// @benchmark string concatenation
{
const sb = new StringBuilderStringAppend();
for (let i = 0; i < APPEND_COUNT; i++) {
sb.append(STR)
}
sb.toString();
}
// @group Random strings
// @benchmark array push & join
{
const sb = new StringBuilderArrayIndex();
for (let i = 0; i < APPEND_COUNT; i++) {
sb.append(randomStrings[i])
}
sb.toString();
}
// @benchmark string concatenation
{
const sb = new StringBuilderStringAppend();
for (let i = 0; i < APPEND_COUNT; i++) {
sb.append(randomStrings[i])
}
sb.toString();
}
</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
如今,所有常用的浏览器在字符串拼接方面的处理能力都有所提升,至少快了两倍。
--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
string concatenation 1.0x | x100000 224 232 254 266 275
array push & join 3.2x | x100000 722 753 757 762 763
Random strings
string concatenation 1.0x | x100000 261 268 270 273 279
array push & join 5.4x | x10000 142 147 148 155 166
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark
--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
string concatenation 1.0x | x100000 304 335 353 358 370
array push & join 9.5x | x10000 289 300 301 306 309
Random strings
string concatenation 1.0x | x100000 334 337 345 349 377
array push & join 5.1x | x10000 169 176 176 176 180
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark
--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
string concatenation 1.0x | x100000 574 592 594 607 613
array push & join 2.7x | x10000 156 157 159 164 165
Random strings
string concatenation 1.0x | x100000 657 663 669 675 680
array push & join 4.3x | x10000 283 285 295 298 311
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark
--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
string concatenation 1.0x | x100000 546 648 659 663 677
array push & join 5.8x | x10000 314 320 326 331 335
Random strings
string concatenation 1.0x | x100000 647 739 764 765 804
array push & join 2.9x | x10000 187 188 199 219 231
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark
--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
string concatenation 1.0x | x100000 566 571 572 579 600
array push & join 2.5x | x10000 144 145 159 162 166
Random strings
string concatenation 1.0x | x100000 649 658 659 663 669
array push & join 4.4x | x10000 285 285 290 292 300
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark `
--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
string concatenation 1.0x | x10000 76 77 77 79 82
array push & join 2.2x | x10000 168 168 174 178 186
Random strings
string concatenation 1.0x | x100000 878 884 889 892 903
array push & join 2.3x | x10000 199 200 202 202 204
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark `
--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
string concatenation 1.0x | x100000 577 579 581 584 608
array push & join 2.7x | x10000 157 162 165 166 171
Random strings
string concatenation 1.0x | x100000 688 694 740 750 781
array push & join 4.2x | x10000 291 315 316 317 379
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark
来自Rhino Book:
在JavaScript中,字符串是不可变的对象,这意味着它们内部的字符不能被更改,并且对字符串进行的任何操作都会实际创建新的字符串。字符串是通过引用而非值分配的。通常情况下,当通过引用分配对象时,通过一个引用对对象所做的更改将通过所有对该对象的其他引用可见。然而,由于字符串无法更改,因此您可以对字符串对象拥有多个引用,而不必担心字符串值会在未知情况下更改。
null
、undefined
、number
和boolean
并列。字符串是通过_值_分配而不是通过引用传递的。因此,字符串不仅是不可变的,而且是一个_值_。将字符串“hello”更改为“world”,就像决定从现在开始数字3是数字4一样……这毫无意义。 - Benjamin Gruenbaumvar a = "hello"; var b=a; a.x=5; console.log(a.x,b.x);
- Benjamin Gruenbaum.valueOf()
函数访问包装类型的字符串值 - 对于Number
对象和数字值也是如此。需要注意的是,使用new String
创建的“String”对象不是实际的字符串,而是字符串的包装器或盒子。请参见http://es5.github.io/#x15.5.2.1。有关内容如何转换为对象,请参见http://es5.github.io/#x9.9。 - Benjamin Gruenbaum为了对像我这样的简单脑袋进行澄清(来自MDN):
不可变对象是指对象在创建后其状态不能更改。
字符串和数字是不可变的。
不可变意味着:
您可以将变量名称指向新值,但以前的值仍然保留在内存中。因此需要垃圾回收。
var immutableString = "Hello";
// 在上面的代码中,创建了一个具有字符串值的新对象。
immutableString = immutableString + "World";
// 我们现在正在将“World”附加到现有值上。
这似乎是我们正在改变字符串“immutableString”的内容,但实际上并非如此。相反:
附加字符串值“immutableString”会触发以下事件:
- 检索“immutableString”的现有值
- 将“World”附加到“immutableString”的现有值上
- 将结果值分配给新的内存块
- “immutableString”对象现在指向新创建的内存空间
- 以前创建的内存空间现在可以进行垃圾回收。
var immutableString = "Hello"; immutableString="world"
,那么结果会是一样的吗?我的意思是给变量分配一个全新的值。 - user6791921性能提示:
如果你需要连接大型字符串,请将字符串部分放入数组中,并使用 Array.Join()
方法获取整个字符串。对于连接大量字符串,这可以比直接连接快很多。
在 JavaScript 中没有StringBuilder
。
字符串类型的值是不可变的,但是使用String()构造函数创建的String对象是可变的,因为它是一个对象,你可以向其中添加新属性。
> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }
new String
会在不可变字符串周围生成一个可变的包装器。 - tomdemuytString
对象(包装器)添加新属性,这意味着它_不是_不可变的(默认情况下;像任何其他对象一样,您可以调用Object.freeze
使其不可变)。但是,包含在String
对象包装器中或不包含在其中的原始字符串值类型始终是不可变的。 - Mark Reed字符串是不可变的 - 它们不能改变,我们只能创建新字符串。
例如:
var str= "Immutable value"; // it is immutable
var other= statement.slice(2, 10); // new string
JavaScript字符串确实是不可变的。