当JavaScript函数作为变量关闭时会发生什么?

时间:2015-06-03 19:54:09

标签: javascript function closures

我已经在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"

1 个答案:

答案 0 :(得分:1)

感谢您的评论和讨论。根据您的建议,我发现了以下内容。

  

闭包中保留的函数在技术上会发生什么?

作为对象的函数与任何其他简单对象的处理方式没有区别,如闭包变量(如字符串或对象)。

  

它是如何存储在内存中的?它是如何保存的?

要回答这个问题,我不得不深入研究编程语言的一些文本。 John C. Mitchell's Concepts in Programming Languages解释了关闭的变量通常最终会出现在程序堆中。

  

因此,嵌套子程序中定义的变量可能需要整个程序的生命周期,而不仅仅是定义它们的子程序处于活动状态的时间。一个变量的寿命是整个程序的变量,据说具有无限的范围。这通常意味着它们必须是堆动态的,而不是堆栈动态的。

更具体的JavaScript运行时,Dmitry Soshnikov describes

  

对于实现,为了在销毁上下文之后存储局部变量,基于堆栈的实现不再适合(因为它与基于堆栈的结构的定义相矛盾)。因此,在这种情况下,父上下文的闭合数据被保存在动态存储器分配中(在“堆”中,即基于堆的实现中),使用垃圾收集器(GC)和引用计数。这种系统的速度低于基于堆栈的系统。但是,实现可能总是优化它:在解析阶段找出,是否在函数中使用自由变量,并根据此决定 - 将数据放在堆栈中或“堆”中。

此外,Dmitry在函数闭包中显示了父作用域的各种实现:

  

正如我们所提到的,出于优化目的,当函数不使用自由变量时,实现可能不会保存父作用域链。但是,在ECMA-262-3规范中没有任何关于它的说法;因此,正式(并通过技术算法) - 所有函数在创建时将范围链保存在[[Scope]]属性中。

     

某些实现允许直接访问已关闭的作用域。例如,在Rhino中,函数的[[Scope]]属性对应于非标准属性__parent __。