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 循环(在内存中创建的新值)和垃圾回收器(杀死 的所有旧值)之间进行不必要的竞赛,循环通常会赢得
i
i
不。垃圾回收器具有高度的适应性,特别是当发生更多分配时,它会做更多的工作。没有办法“超越”它。如果需要,当垃圾回收器尝试查找可释放的内存时,程序执行将停止。
我们将有一个堆栈溢出。
不可以,堆栈溢出与对象分配、垃圾回收或一般的堆内存无关。
添加回答
举报