如果我在全局范围内编写此代码:
(function(){})();
在执行该语句时是否创建了匿名函数,并在执行该语句后立即销毁了该匿名函数?
如果我在函数中编写此代码:
function foo()
{
var a=1;
(function(){})();
a++;
}
匿名函数在foo返回之前是否一直存在,还是在该语句执行期间一直存在?
答案 0 :(得分:22)
在这种特殊情况下,大多数引擎会完全不优化该功能,因为它不会执行任何操作。
但是让我们假设函数包含代码并且确实已执行。在这种情况下,该功能将一直存在,无论是作为编译代码,字节码还是解释器的AST。
一直不存在的部分是范围和可能创建的闭包。为该函数创建的作用域和闭包仅在执行该函数或存在具有特定绑定范围/闭包的对该函数的引用时才存在。
因此,函数引用+作用域组合将在执行语句(function(){})();
时分配,并且可以在该语句后释放。但是function(){}
的编译版本可能仍存在于内存中以备后用。
对于只进行及时编译和优化的引擎,甚至在不同的编译版本中都可能存在一个函数。
现代js引擎的JIT + optimizer部分是一个复杂的主题,可以在html5rocks: JavaScript Compilation处找到v8的粗略描述:
在V8中,Full编译器在所有代码上运行,并尽快开始执行代码,从而迅速生成好的但不是好的代码。该编译器在编译时几乎不假设类型,它希望变量类型可以并且将在运行时更改。
与完整的编译器并行,V8使用优化的编译器重新编译“热”函数(即,多次运行的函数)。 [...]在优化编译器中,操作以推测方式内联(直接放置在调用它们的位置)。这样可以加快执行速度(以内存占用为代价),但还可以进行其他优化。
因此,所生成的代码可能与原始代码几乎没有任何相似之处。
因此,立即调用的函数表达式甚至可以使用内联进行完全优化。
答案 1 :(得分:4)
正如p.niese所说,引擎完全可以优化该功能的可能性很大。因此,我们假设其中包含一些代码:如果我在全局范围内编写此代码:
(function(){})();
在执行该语句时是否创建了匿名函数,并在执行该语句后立即销毁了该匿名函数?
// At global scope
(function(){ console.log("Hi there"); })();
引擎无法保证该代码不会引发错误(例如,如果您将console
替换为其他内容),那么我敢肯定,它不能只是内联。 / p>
现在的答案是:这取决于。
从语言/规范级别开始,当第一次加载编译单元时,将解析编译单元(大致为:脚本)中的所有代码。然后,当代码在逐步执行中到达该函数时创建该函数,在创建后执行该函数(这涉及为调用创建执行上下文),并在完成后立即有资格进行垃圾回收(连同执行上下文)因为没有任何参考。但这仅仅是理论/高级规范。
从JavaScript引擎的角度来看:
以下是V8博客上的几篇有趣的文章:
如果我在函数中编写此代码:
function foo() { var a=1; (function(){})(); a++; }
匿名函数在foo返回之前是否一直存在,还是在执行该语句期间一直存在?
让我们再次假设该函数中有一个console.log
,并且我是对的(对我来说这是 的假设)是事实,它依赖于可写的全局变量({ {1}})意味着它不能仅内联。
高级/规范的答案是相同的:该函数在脚本加载时解析,在到达脚本时创建,执行并在执行完毕后才有资格使用GC。但这又只是一个高级概念。
引擎级别的情况可能有所不同:
console
返回时,GC变得微不足道了。)