为了账号安全,请及时绑定邮箱和手机立即绑定

浏览器是否总是将Javascript中的字符串和数字视为不可变的?

浏览器是否总是将Javascript中的字符串和数字视为不可变的?

慕尼黑5688855 2022-08-04 09:42:38
在 Javscript 中,浏览器运行时解释器是否总是将字符串和数字视为不可变的?当然,在可证明是无害的情况下,他们会优化并将其视为可变的。如果没有,为什么不呢?例如,考虑不起眼的 for 循环。for (let i = 0; i < 1000000000000; i++) { console.log(i)}由于变量的作用域限定为循环,并且循环中没有代码需要 i 变量的“旧值”,因此浏览器只需将符号指向每次迭代的数量递增是有意义的。否则,新字节的内存流将被 的新值占用,原因无法想象(“有人可能需要这些旧值!”)。我们将在 for 循环(在内存中创建的新值)和垃圾回收器(杀死 的所有旧值)之间进行不必要的争用,循环通常会赢得该值,并且我们将有一个堆栈溢出。iiiiii哦,这就是发生的事情,不是吗。如果是这样,为什么浏览器在以其他方式优化代码方面如此聪明时,以这种方式愚蠢?字符串也有类似的情况。请考虑以下事项。{   let completeWorks = "This string dictates the complete works of William Shakespeare. To be or not to be that is the question whether it is nobler in the mind..."   completeWorks += "The End."  // <-- what happens here?}该字符串是块范围的,并且可证明只存在于此块中。因此,当浏览器遇到指令时,它肯定会发生变化 。如果没有,为什么不呢?也许他们有一个很好的理由不这样做,我想学习它。completeWorkscompleteWorks += "The End"completeWorks
查看完整描述

1 回答

?
冉冉说

TA贡献1877条经验 获得超1个赞

(V8开发人员在这里 - 因此我对其他浏览器/引擎知之甚少。

这个问题没有简单的答案;实现很复杂。

在 V8 中,字符串始终是不可变的(在创建之后)。一个原因是,在堆上分配对象时,对象后面通常没有可用空间,因此我们不能只将字符附加到现有字符串。另一个原因是,跟踪哪些字符串可以安全地进行突变将增加大量的复杂性(除了一些更容易检测的利基案例,但如果只支持这些,那么该机制将提供更少的价值)。

V8确实有一些漂亮的字符串操作技巧:当你取一个较大字符串的子字符串时,则不会复制任何字符;新字符串只是一个引用,它说“我是那边另一个字符串的长度X的切片,从索引Y开始”。同样,当像您的示例一样连接两个字符串时,新字符串是一个引用,它说“我是这两个其他字符串的串联”。(为了完整起见,我会提到有最小的字符数,低于这些技巧不会应用,因为简单地复制字符至少同样有效。completeWorks

数字比字符串对性能更敏感,也更容易处理。通常,堆分配的数字始终是不可变的;但这并不是故事的结局。V8 大量使用“Smis”(“小整数”)的特殊表示形式,因为 JavaScript 程序中的许多数字都属于该存储桶。Smis 不是堆对象;创建一个新的与修改一个一样便宜,实际上无法区分(如C++)。对于超出 Smi 范围的数字,优化编译器还会执行“转义分析”,并可以“解压缩”非转义数字,这意味着将它们保留在 CPU 寄存器中(作为普通的 64 位浮点数),而不是首先将它们分配到堆上,这同样比改变其他不可变的堆对象还要好。对于存储在对象属性中的数字的特殊情况,V8 也(在某些情况下)使用可变存储。int

因此,您的问题的答案既是“是”(例如,在生成未优化的代码时,V8不会花时间执行分析,因此代码必须保守地假设某个地方需要任何旧值),又是“否”(对于优化编译器,您的直觉是正确的,这应该是可以避免的;但是,这仍然并不意味着在堆上分配的任何数字都会在那里发生突变)。

由于变量的作用域限定为循环i

JavaScript 中的范围很复杂。首先,没有.现在考虑一下:int i

for (var i = 0; i < 100; i++) {

  // Use i here, or don't.

}

console.log(i);  // Prints "100".

如果你的意思是 ,那么当然,你会有一个块范围的变量。在此示例中,性能将相同。let i

我们将在 for 循环(在内存中创建的新值)和垃圾回收器(杀死 的所有旧值)之间进行不必要的竞赛,循环通常会赢得ii

不。垃圾回收器具有高度的适应性,特别是当发生更多分配时,它会做更多的工作。没有办法“超越”它。如果需要,当垃圾回收器尝试查找可释放的内存时,程序执行将停止。

我们将有一个堆栈溢出。

不可以,堆栈溢出与对象分配、垃圾回收或一般的堆内存无关。


查看完整回答
反对 回复 2022-08-04
  • 1 回答
  • 0 关注
  • 85 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信