如何向某人解释JavaScript关闭,知道它们所构成的概念(例如功能、变量等),但不了解关闭本身?

我已经在维基百科上看到了图表的例子,但不幸的是,它没有帮助。


当前回答

关闭是当内部函数以某种方式为外部函数以外的任何范围提供时创建的。

例子:

var outer = function(params){ //Outer function defines a variable called params
    var inner = function(){ // Inner function has access to the params variable of the outer function
        return params;
    }
    return inner; //Return inner function exposing it to outer scope
},
myFunc = outer("myParams");
myFunc(); //Returns "myParams"

其他回答

函数和函数的外部范围(语法环境)的参考

函数 汽车(制造商,模型,年,颜色) { 返回 { 到String() { 返回 `${ 制造商} ${ 模型} (${ 年}, ${ 颜色})` } } 组车 = 新车(‘Aston Martin’,‘V8 Vantage’,‘2012’,‘Quantum Silver’) console.log(car.toString())

事件导向的编程

在下面的例子中,所有实施细节都隐藏在即时执行的函数表达式内。 函数标记和 toString 接近私人状态和函数他们需要完成工作。 关闭已使我们能够模块化和包容我们的代码。

例子1

这个例子表明,本地变量在关闭中没有复制:关闭保持了对原始变量的参考。

左边

让我们从这里开始,正如MDN所定义的那样:关闭是指独立(自由)变量的功能(被本地使用的变量,但在关闭范围内定义)。

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function    
  }
  displayName();    
}
init();

function init() {
    var name = "Mozilla"; // name is a local variable created by init
    function displayName() { // displayName() is the inner function, a closure
        alert (name); // displayName() uses variable declared in the parent function    
    }
    displayName();    
}
init();

关闭现在考虑下面的例子:

function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

您可能想要做到这一点的情况在网上特别常见. 在前端JavaScript中写的代码的大部分是基于事件的 - 我们定义了一些行为,然后将其附加到用户发起的事件(如点击或键盘)。

例如,假设我们想添加一些按钮到一个页面,调整文本大小的一种方式是指定字体元素的字体大小在像素,然后设置页面上的其他元素的大小(如标题)使用相应的EM单位:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>


function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

要了解更多关于关闭的信息,请访问MDN的链接

好吧,和一个6岁的孩子谈话,我可能会使用随后的协会。

想象一下 - 你正在玩你的小兄弟姐妹在整个房子,你正在移动周围与你的玩具,并将其中的一些带到你的哥哥的房间。 过了一会儿,你的哥哥从学校回来,去了他的房间,他锁在里面,所以现在你不能访问玩具留在那里再直接的方式。

比较一个情况,当一个门被草案锁定,没有人在里面(通用功能执行),然后一些当地的火灾发生并燃烧房间(垃圾收集器:D),然后一个新的房间被建造,现在你可以留下其他玩具在那里(新功能例子),但从来没有得到相同的玩具留在第一间房间例子。

对于一个先进的孩子,我会把这样的东西放在下面,这不是完美的,但它让你感觉到它是什么:

function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

正如你可以看到的那样,房间里留下的玩具仍然可以通过兄弟访问,不管房间是否被锁定。

TLDR

细节

当一个函数通过内部(呼叫)方法引用时,函数对象的环境参考将复制到新创建的执行框架(stack frame)环境记录的外部环境参考。

在下面的例子中,函数f 关闭了全球执行背景的语法环境:

function f() {}

在下面的例子中,函数h在函数g的语法环境中关闭,而函数h则在全球执行背景的语法环境中关闭。

function g() {
    function h() {}
}

如果内部函数由外部函数返回,则外部语法环境在外部函数返回后将继续存在,因为如果内部函数最终被召回,则外部语法环境必须可用。

const makePlus = 函数(x) { 返回函数(y) { 返回 x + y; } const plus5 = makePlus(5); console.log(plus5(3));

如果JavaScript不知道关闭,会发生什么? 只需由其方法体(基本上是函数通话所做的事情)在最后一行中取代通话,你会得到:

console.log(x + 3);

现在, x 的定义在哪里? 我们没有在当前范围内定义它. 唯一的解决方案是让 plus5 携带其范围(或更好地说,其父母范围)。