我理解语义,即闭包对变量的引用延长了它的生命周期,使原始变量不受调用堆栈的限制,因此对闭包捕获的那些变量应进行特殊处理。
我还理解,同一范围内的变量是否可以不同对待取决于它是否被当今的javascript引擎中的闭包捕获。例如,
function foo(){
var a=2;
var b=new Array(a_very_big_number).join('+');
return function(){
console.log(a);
};
}
var b=foo();
由于没有人在b
中保留对foo
的引用,因此无需将b
保留在内存中,因此可以在foo
返回时立即释放使用的内存(甚至从未在进一步优化下创建)。
我的问题是,为什么v8似乎在每个调用上下文中将所有闭包引用的所有变量打包在一起?例如,
function foo(){
var a=0,b=1,c=2;
var zig=function(){
console.log(a);
};
var zag=function(){
console.log(b);
};
return [zig,zag];
}
zig
和zag
似乎都引用了a
和b
,即使很明显b
对{{1}也不可用}。当zig
很大且b
持续很长时间时,这可能会很糟糕。
但是从实现的角度来看,我不明白为什么这是必须的。根据我的知识,无需调用zig
,就可以在执行之前确定作用域链,从而可以确定引用关系。引擎应注意,eval
不再可用时,zig
会继续执行,因此引擎会将其标记为垃圾。
Chrome和Firefox似乎都遵守规则。标准是否说任何实现都必须这样做?还是这种实现更实用,更有效?我很困惑。
答案 0 :(得分:1)
主要障碍是可变性。如果两个闭包共享相同的var
,则它们必须以一种可见的方式在另一个闭包中进行更改。因此,不可能像功能语言那样将引用变量的值复制到每个闭包环境中(绑定是不可变的)。您需要共享一个指向公共可变堆位置的指针。
现在,您可以将每个捕获的变量分配为堆上的单独单元,而不是一个包含所有变量的数组。但是,这在空间和时间上通常会更昂贵,因为您需要多个分配和两个间接级别(每个闭包都指向其自己的闭包环境,该闭包环境指向每个共享的可变变量单元格)。在当前的实现中,它仅是每个作用域的一种分配,并且是一种间接访问变量(单个作用域内的所有闭包都指向同一可变变量数组)。缺点是某些使用寿命比您预期的更长。这是一个权衡。
其他考虑因素是实现的复杂性和可调试性。借助eval
之类的可疑功能,并且期望调试器可以检查范围链,基于范围的实现更加易于处理。
答案 1 :(得分:0)
该标准未对垃圾回收作任何说明,但提供了一些应该发生的线索。 参考:Standard
外部词法环境当然可以有自己的外部词法环境 词汇环境。词汇环境可以作为外部环境 多个内部词法环境的环境。例如,如果 函数声明包含两个嵌套的函数声明,然后 每个嵌套函数的词法环境将具有 他们的外部词汇环境当前的词汇环境 执行周围的功能。”
Section 13 Function definition
step 4: "Let closure be the result of creating a new Function object as specified in 13.2"
Section 13.2 "a Lexical Environment specified by Scope" (scope = closure)
Section 10.2 Lexical Environments:
"The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically surrounds the inner Lexical Environment.
因此,一个函数将有权访问父级的环境。