JavaScript闭包如何工作?

您将如何向了解其闭包概念(例如函数,变量等)的人解释JavaScript闭包,但却不了解闭包本身?

我已经在Wikipedia上看到了Scheme示例,但是不幸的是它没有帮助。

Tony樱番长2020/03/09 11:50:37

我只是将它们指向Mozilla Closures页面是我发现的关于闭包基础知识和实际用法的最佳,最简洁明了的解释强烈建议任何学习JavaScript的人使用。

是的,我什至推荐给6岁的孩子使用-如果6岁的孩子正在学习闭包,那么可以随时理解本文提供简洁明了的逻辑是合乎逻辑的。

Stafan古一2020/03/09 11:50:37

The author of Closures has explained closures pretty well, explaining the reason why we need them and also explaining LexicalEnvironment which is necessary to understanding closures.
Here is the summary:

What if a variable is accessed, but it isn’t local? Like here:

在此处输入图片说明

In this case, the interpreter finds the variable in the outer LexicalEnvironment object.

The process consists of two steps:

  1. First, when a function f is created, it is not created in an empty space. There is a current LexicalEnvironment object. In the case above, it’s window (a is undefined at the time of function creation).

在此处输入图片说明

When a function is created, it gets a hidden property, named [[Scope]], which references the current LexicalEnvironment.

在此处输入图片说明

If a variable is read, but can not be found anywhere, an error is generated.

Nested functions

Functions can be nested one inside another, forming a chain of LexicalEnvironments which can also be called a scope chain.

在此处输入图片说明

So, function g has access to g, a and f.

Closures

A nested function may continue to live after the outer function has finished:

在此处输入图片说明

Marking up LexicalEnvironments:

在此处输入图片说明

As we see, this.say is a property in the user object, so it continues to live after User completed.

And if you remember, when this.say is created, it (as every function) gets an internal reference this.say.[[Scope]] to the current LexicalEnvironment. So, the LexicalEnvironment of the current User execution stays in memory. All variables of User also are its properties, so they are also carefully kept, not junked as usually.

The whole point is to ensure that if the inner function wants to access an outer variable in the future, it is able to do so.

To summarize:

  1. The inner function keeps a reference to the outer LexicalEnvironment.
  2. The inner function may access variables from it any time even if the outer function is finished.
  3. 浏览器将LexicalEnvironment及其所有属性(变量)保留在内存中,直到有一个内部函数引用它为止。

这称为关闭。

--神经Xiao--2020/03/09 11:50:37

一个六岁孩子的答案(假设他知道什么是函数,什么是变量以及什么数据):

函数可以返回数据。您可以从函数返回的一种数据是另一种函数。返回该新函数时,创建该函数的函数中使用的所有变量和参数都不会消失。而是,该父函数“关闭”。换句话说,除了返回的功能外,什么都看不到它,也看不到它使用的变量。该新函数具有特殊的功能,可以回顾创建它的函数内部并查看其中的数据。

function the_closure() {
  var x = 4;
  return function () {
    return x; // Here, we look back inside the_closure for the value of x
  }
}

var myFn = the_closure();
myFn(); //=> 4

解释它的另一种非常简单的方法是在范围上:

每当您在较大范围内创建较小范围时,较小范围将始终能够看到较大范围中的内容。

阿飞小卤蛋2020/03/09 11:50:36

我不明白为什么答案在这里这么复杂。

这是一个闭包:

var a = 42;

function b() { return a; }

是。您可能一天使用多次。


没有理由相信闭包是解决特定问题的复杂设计方法。不,从函数声明的位置(不运行)的角度来看,闭包只是使用来自更高范围的变量

现在,它允许您执行的操作会更加壮观,请参阅其他答案。

StafanHarry2020/03/09 11:50:36

dlaliberte第一点的示例:

闭包不仅在您返回内部函数时创建。实际上,封闭函数根本不需要返回。您可以改为将内部函数分配给外部作用域中的变量,或将其作为参数传递给可以立即使用的另一个函数。因此,在调用封闭函数时,封闭函数的关闭可能已经存在,因为任何内部函数都可以在调用它后立即对其进行访问。

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);
神奇神奇Near2020/03/09 11:50:36

我知道已经有很多解决方案,但是我猜想这个小而简单的脚本可以用来说明这个概念:

// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined
NearSamHarry2020/03/09 11:50:36

闭包是内部函数可以访问其外部函数中的变量的地方。这可能是闭包最简单的单行解释。

猪猪卡卡西西里2020/03/09 11:50:36

我如何向六岁的孩子解释:

您知道大人如何拥有房屋,他们称之为房屋吗?当妈妈有孩子时,孩子实际上并不拥有任何东西,对吗?但是它的父母拥有一所房子,因此只要有人问孩子“你的房子在哪里?”,他/她就可以回答“那所房子!”,并指向其父母的房子。“关闭”是孩子始终(即使在国外)能够说出自己有房屋的能力,即使实际上这是父母的财产。

乐猪猪2020/03/09 11:50:36

前一段时间,我写了一篇博客文章解释了闭包。这就是我说的关于闭包的原因因为您想要一个闭包

闭包是一种使函数具有永久性私有变量的方法,也就是说,只有一个函数才知道的变量,它可以在其中跟踪以前运行时的信息。

从这种意义上讲,它们使函数的行为有点像具有私有属性的对象。

全文:

那么这些关闭的东西是什么呢?

LEY神乐2020/03/09 11:50:36

闭包很难解释,因为闭包用于使某些行为正常工作,每个人都希望它们能正常工作。我发现解释它们的最佳方法(以及了解它们的方法)是想象没有它们的情况:

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

如果JavaScript 知道闭包,在这里会发生什么只需将最后一行的调用替换为其方法主体(基本上是函数调用所做的工作),您将获得:

console.log(x + 3);

现在,的定义在x哪里?我们没有在当前范围内对其进行定义。唯一的解决方案是plus5 随身携带其范围(或更确切地说,其父级的范围)。这种方式x定义明确,并绑定到值5。

ItachiHarry2020/03/09 11:50:36

好吧,6岁的瓶盖粉丝。您是否想听到最简单的闭包示例?

让我们想象下一个情况:驾驶员坐在汽车上。那辆车在飞机上。飞机在机场。驾驶员即使在飞机离开机场后也无法进入汽车外部但在飞机内部的东西,这是封闭的。而已。27岁时,请查看更详细的说明或以下示例。

这是将飞机上的故事转换为代码的方法。

var plane = function(defaultAirport) {

  var lastAirportLeft = defaultAirport;

  var car = {
    driver: {
      startAccessPlaneInfo: function() {
        setInterval(function() {
          console.log("Last airport was " + lastAirportLeft);
        }, 2000);
      }
    }
  };
  car.driver.startAccessPlaneInfo();

  return {
    leaveTheAirport: function(airPortName) {
      lastAirportLeft = airPortName;
    }
  }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");

神无宝儿2020/03/09 11:50:35

JavaScript中的每个函数都维护与其外部词法环境的链接。词汇环境是作用域中所有名称(例如变量,参数)及其值的映射。

因此,每当您看到function关键字时,该函数内部的代码都可以访问该函数外部声明的变量。

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

这将记录下来16,因为函数bar关闭了参数x和变量tmp,两者都存在于外部函数的词法环境中foo

Function bar及其与函数的词法环境的链接foo是一个闭包。

一个函数不必为了创建一个闭包返回仅仅凭借其声明,每个函数都会在其封闭的词法环境中关闭,从而形成一个闭合。

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2);
bar(10); // 16
bar(10); // 17

上面的函数也会记录16,因为里面的代码bar仍然可以引用参数x和变量tmp,即使它们不再直接在范围内。

但是,由于tmp仍在内部bar的封闭中徘徊,因此可以对其进行递增。每次调用时,它都会增加bar

关闭的最简单示例是:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

When a JavaScript function is invoked, a new execution context ec is created. Together with the function arguments and the target object, this execution context also receives a link to the lexical environment of the calling execution context, meaning the variables declared in the outer lexical environment (in the above example, both a and b) are available from ec.

Every function creates a closure, because every function has a link to its outer lexical environment.

Note that variables themselves are visible from within a closure, not copies.