Wil*_*iam 16 javascript anonymous-function lifetime
如果我在全球范围内写这个:
(function(){})();
Run Code Online (Sandbox Code Playgroud)
是在执行语句后立即执行和销毁语句时创建的匿名函数?
如果我在函数中写这个:
function foo()
{
var a=1;
(function(){})();
a++;
}
Run Code Online (Sandbox Code Playgroud)
匿名函数在foo返回之前是否存在,或者只是在执行该语句期间存在?
t.n*_*ese 22
在这种特殊情况下,大多数引擎会完全消除该功能,因为它不会做任何事情.
但是我们假设函数包含代码并且确实已执行.在这种情况下,该函数将一直存在,可以是编译代码,也可以是字节码,也可以是解释器的AST.
不会一直存在的部分是范围和可能创建的闭包.只有在执行函数或存在对具有特定绑定范围/闭包的函数的引用时,才会存在为该函数和闭包创建的作用域.
因此,组合函数引用+范围将在语句(function(){})();执行时分配,并可在该语句之后释放.但编译后的版本function(){}可能仍然存在于内存中供以后使用.
对于即时编译和优化的引擎,函数甚至可能存在于不同的编译版本中.
现代js引擎的JIT +优化器部分是一个复杂的主题,可以在这里找到v8的粗略描述html5rocks:JavaScript编译:
在V8中,Full编译器在所有代码上运行,并尽快开始执行代码,快速生成好但不是很好的代码.这个编译器几乎没有假设编译时的类型 - 它期望变量类型可以并且将在运行时更改.
与完整的编译器并行,V8使用优化编译器重新编译"热"函数(即,运行多次的函数).[...]在优化编译器中,操作以推测方式内联(直接放置在它们被调用的位置).这加快了执行速度(以内存占用为代价),但也可以实现其他优化.
因此,生成的代码可能与原始代码几乎没有任何相似之处.
因此,使用内联甚至可以完全优化立即调用的函数表达式.
\n\n如果我在全局范围内写这个:
\n
\n\nRun Code Online (Sandbox Code Playgroud)\n(function(){})();\n
\n\n匿名函数是在语句执行时创建并在语句执行后立即销毁吗?
\n
正如 t.niese 所说,引擎很可能会完全优化该功能。所以我们假设它里面有一些代码:
\n// At global scope\n(function(){ console.log("Hi there"); })();\nRun Code Online (Sandbox Code Playgroud)\n引擎不能保证该代码不会抛出错误(例如,如果您console用其他东西替换),所以我相当确定它不能只是内联它。
现在的答案是:这要看情况。
\n从语言/规范级别来看,所有代码都在一个编译单元中的所有代码(大致:脚本)在编译单元首次加载时都会被解析。然后,当代码在逐步执行中到达该函数时,就会创建该函数,在创建后执行(这涉及为调用创建执行上下文),并且在完成后立即有资格进行垃圾回收(以及执行上下文)因为没有任何东西可以引用它。但这只是理论/高级规范。
\n从 JavaScript 引擎的角度来看:
\nfoo)。如果foo变得无法访问,也许foo字节/机器代码和匿名函数可能会因为不必要而被丢弃。我不知道引擎是否能做到这一点。一方面,这似乎是唾手可得的成果;另一方面,这似乎是一种不常见的事情,不值得费心。(请记住,这里我们不是在讨论附加到函数实例的代码代码,而是从源生成的代码,该代码在创建实例时附加到实例,并且随着时间的推移可能会附加到多个实例。 )以下是 V8 博客上的几篇文章,读起来很有趣:
\n\n\n如果我把它写在一个函数中:
\n
\n\nRun Code Online (Sandbox Code Playgroud)\nfunction foo()\n{\n var a=1;\n (function(){})();\n a++;\n}\n
\n\n匿名函数在 foo 返回之前一直存在,还是仅在该语句执行期间存在?
\n
让我们再次假设console.log该函数中有一个,并且我是正确的(这是我的假设),它依赖于可写全局 ( console) 的事实意味着它不能只是内联。
高级/规范答案是相同的:函数在加载脚本时被解析,在脚本到达时创建,执行,并且在执行完成时有资格进行 GC。但同样,这只是高级概念。
\n引擎层面的情况可能有所不同:
\nfoo返回时 GC 变得微不足道。)