我已经在SO上阅读了关于闭包和JavaScript的许多问题,但我无法找到关于函数关闭时会发生什么的信息。通常这些示例是字符串文字或简单对象。请参阅下面的示例。在函数中关闭时,即使稍后更改原始函数,也会保留原始函数。
技术上在闭包中保留的函数会发生什么?它是如何存储在内存中的?它是如何保存的?
请参阅以下代码作为示例:
var makeFunc = function () {
var fn = document.getElementById; //Preserving this, I will change it later
function getOriginal() {
return fn; //Closure it in
}
return getOriginal;
};
var myFunc = makeFunc();
var oldGetElementById = myFunc(); //Get my preserved function
document.getElementById = function () { //Change the original function
return "foo"
};
console.log(oldGetElementById.call(document, "myDiv")); //Calls the original!
console.log(document.getElementById("myDiv")); //Returns "foo"
答案 0 :(得分:1)
感谢您的评论和讨论。根据您的建议,我发现了以下内容。
闭包中保留的函数在技术上会发生什么?
作为对象的函数与任何其他简单对象的处理方式没有区别,如闭包变量(如字符串或对象)。
它是如何存储在内存中的?它是如何保存的?
要回答这个问题,我不得不深入研究编程语言的一些文本。 John C. Mitchell's Concepts in Programming Languages解释了关闭的变量通常最终会出现在程序堆中。
因此,嵌套子程序中定义的变量可能需要整个程序的生命周期,而不仅仅是定义它们的子程序处于活动状态的时间。一个变量的寿命是整个程序的变量,据说具有无限的范围。这通常意味着它们必须是堆动态的,而不是堆栈动态的。
更具体的JavaScript运行时,Dmitry Soshnikov describes
对于实现,为了在销毁上下文之后存储局部变量,基于堆栈的实现不再适合(因为它与基于堆栈的结构的定义相矛盾)。因此,在这种情况下,父上下文的闭合数据被保存在动态存储器分配中(在“堆”中,即基于堆的实现中),使用垃圾收集器(GC)和引用计数。这种系统的速度低于基于堆栈的系统。但是,实现可能总是优化它:在解析阶段找出,是否在函数中使用自由变量,并根据此决定 - 将数据放在堆栈中或“堆”中。
此外,Dmitry在函数闭包中显示了父作用域的各种实现:
正如我们所提到的,出于优化目的,当函数不使用自由变量时,实现可能不会保存父作用域链。但是,在ECMA-262-3规范中没有任何关于它的说法;因此,正式(并通过技术算法) - 所有函数在创建时将范围链保存在[[Scope]]属性中。
某些实现允许直接访问已关闭的作用域。例如,在Rhino中,函数的[[Scope]]属性对应于非标准属性__parent __。