首页 文章

JavaScript闭包如何工作?

提问于
浏览
7654

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

我在维基百科上看过the Scheme example,但遗憾的是它并没有帮助 .

30 回答

  • 217

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

    // 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
    
  • 73

    我整理了一个交互式JavaScript教程来解释闭包的工作原理 . What's a Closure?

    这是一个例子:

    var create = function (x) {
        var f = function () {
            return x; // We can refer to x here!
        };
        return f;
    };
    // 'create' takes one argument, creates a function
    
    var g = create(42);
    // g is a function that takes no arguments now
    
    var y = g();
    // y is 42 here
    
  • 2287

    I do not understand why the answers are so complex here.

    这是一个闭包:

    var a = 42;
    
    function b() { return a; }
    

    是 . 你可能每天都要多次使用它 .

    没有理由相信闭包是解决特定问题的复杂设计攻击 . 不,闭包只是从声明函数(不运行)的角度使用来自更高范围的变量 . 现在它允许你做什么可以更壮观,看到其他答案 .

  • 81

    适用于初学者的JavaScript闭包

    莫里斯于星期二提交,2006-02-21 10:19 . 社区编辑以来 .

    闭包不是魔法

    这个页面解释了闭包,以便程序员可以理解它们 - 使用有效的JavaScript代码 . 它不适合大师或功能程序员 .

    一旦核心概念被弄清楚,关闭并不难理解 . 但是,通过阅读任何理论或学术导向的解释,他们无法理解!

    本文面向具有主流语言编程经验的程序员,并且可以阅读以下JavaScript函数:

    function sayHello(name) {
      var text = 'Hello ' + name;
      var say = function() { console.log(text); }
      say();
    }
    sayHello('Joe');
    

    两个简短摘要

    • 当函数(foo)声明其他函数(bar和baz)时,在函数退出时不会销毁在foo中创建的局部变量族 . 这些变量只会变得对外界不可见 . 因此,Foo可以狡猾地返回函数bar和baz,并且可以通过这个已关闭的变量系列("the closure")继续读取,写入和通信,其他任何人都无法干涉,甚至没有人再次调用foo未来 .

    • 封闭是支持first-class functions的一种方式;它是一个表达式,可以引用其范围内的变量(首次声明时),分配给变量,作为参数传递给函数,或作为函数结果返回 .

    一个闭包的例子

    以下代码返回对函数的引用:

    function sayHello2(name) {
      var text = 'Hello ' + name; // Local variable
      var say = function() { console.log(text); }
      return say;
    }
    var say2 = sayHello2('Bob');
    say2(); // logs "Hello Bob"
    

    大多数JavaScript程序员都会理解在上面的代码中如何将函数的引用返回给变量( say2 ) . 如果你不这样做,那么你需要先了解它,然后才能学习闭包 . 使用C的程序员会将函数视为返回指向函数的指针,并且变量 saysay2 都是指向函数的指针 .

    指向函数的C指针和函数的JavaScript引用之间存在严重差异 . 在JavaScript中,您可以将函数引用变量视为既包含指向函数的指针,也包含指向闭包的隐藏指针 .

    上面的代码有一个闭包,因为在这个例子中,匿名函数 function() { console.log(text); } 在另一个函数 sayHello2() 中声明 . 在JavaScript中,如果在另一个函数中使用 function 关键字,则创建一个闭包 .

    在C和大多数其他常用语言中,在函数返回后,所有局部变量都不再可访问,因为堆栈帧被销毁 .

    在JavaScript中,如果在另一个函数中声明一个函数,那么从函数返回后,外部函数的局部变量仍然可以访问 . 这在上面说明,因为我们在从 sayHello2() 返回后调用函数 say2() . 请注意,我们调用的代码引用变量 text ,它是函数 sayHello2() 的局部变量 .

    function() { console.log(text); } // Output of say2.toString();
    

    查看 say2.toString() 的输出,我们可以看到代码引用变量 text . 匿名函数可以引用 text ,其中包含值 'Hello Bob' ,因为 sayHello2() 的局部变量已在闭包中秘密保持活动状态 .

    天才的是,在JavaScript中,函数引用还具有对其创建的闭包的秘密引用 - 类似于委托是方法指针加上对象的秘密引用 .

    更多例子

    出于某种原因,当你阅读它们时,闭包似乎很难理解,但是当你看到一些例子时,它们的工作方式就变得清晰了(我花了一段时间) . 我建议您仔细研究这些示例,直到您了解它们的工作原理 . 如果你开始使用闭包而没有完全理解它们是如何工作的,你很快就会创建一些非常奇怪的错误!

    示例3

    此示例显示不复制局部变量 - 它们通过引用保留 . 即使在外部函数存在之后,就好像堆栈帧在内存中保持活跃!

    function say667() {
      // Local variable that ends up within closure
      var num = 42;
      var say = function() { console.log(num); }
      num++;
      return say;
    }
    var sayNumber = say667();
    sayNumber(); // logs 43
    

    例4

    所有三个全局函数都对同一个闭包有一个共同的引用,因为它们都是在 setupSomeGlobals() 的单个调用中声明的 .

    var gLogNumber, gIncreaseNumber, gSetNumber;
    function setupSomeGlobals() {
      // Local variable that ends up within closure
      var num = 42;
      // Store some references to functions as global variables
      gLogNumber = function() { console.log(num); }
      gIncreaseNumber = function() { num++; }
      gSetNumber = function(x) { num = x; }
    }
    
    setupSomeGlobals();
    gIncreaseNumber();
    gLogNumber(); // 43
    gSetNumber(5);
    gLogNumber(); // 5
    
    var oldLog = gLogNumber;
    
    setupSomeGlobals();
    gLogNumber(); // 42
    
    oldLog() // 5
    

    这三个函数共享访问同一个闭包 - 当定义了三个函数时 setupSomeGlobals() 的局部变量 .

    请注意,在上面的示例中,如果再次调用 setupSomeGlobals() ,则会创建一个新的闭包(stack-frame!) . 旧的 gLogNumbergIncreaseNumbergSetNumber 变量将被具有新闭包的新函数覆盖 . (在JavaScript中,每当你在另一个函数中声明一个函数时,每次外部函数都会重新创建内部函数 . 叫 . )

    例5

    此示例显示闭包包含在退出之前在外部函数内声明的所有局部变量 . 请注意,变量 alice 实际上是在匿名函数之后声明的 . 首先声明匿名函数,当调用该函数时,它可以访问 alice 变量,因为 alice 在同一范围内(JavaScript确实variable hoisting) . 另外 sayAlice()() 只是直接调用从 sayAlice() 返回的函数引用 - 它与先前所做的完全相同但没有临时变量 .

    function sayAlice() {
        var say = function() { console.log(alice); }
        // Local variable that ends up within closure
        var alice = 'Hello Alice';
        return say;
    }
    sayAlice()();// logs "Hello Alice"
    

    Tricky:还要注意 say 变量也在闭包内,并且可以在 sayAlice() 中声明的任何其他函数访问,或者可以在inside函数内递归访问 .

    例6

    对于很多人来说,这是一个真正的陷阱,所以你需要了解它 . 如果要在循环中定义函数,请务必小心:闭包中的局部变量可能不会像您首先想到的那样起作用 .

    您需要了解Javascript中的“变量提升”功能才能理解此示例 .

    function buildList(list) {
        var result = [];
        for (var i = 0; i < list.length; i++) {
            var item = 'item' + i;
            result.push( function() {console.log(item + ' ' + list[i])} );
        }
        return result;
    }
    
    function testList() {
        var fnlist = buildList([1,2,3]);
        // Using j only to help prevent confusion -- could use i.
        for (var j = 0; j < fnlist.length; j++) {
            fnlist[j]();
        }
    }
    
     testList() //logs "item2 undefined" 3 times
    

    result.push( function() {console.log(item + ' ' + list[i])} 行将一个匿名函数的引用添加到结果数组三次 . 如果您不熟悉匿名函数,请将其视为:

    pointer = function() {console.log(item + ' ' + list[i])};
    result.push(pointer);
    

    请注意,运行该示例时,会记录 "item2 undefined" 三次!这是因为就像前面的例子一样, buildList 的局部变量只有一个闭包( resultiitem ) . 当在 fnlist[j]() 行上调用匿名函数时;它们都使用相同的单个闭包,并且它们在一个闭包内使用 iitem 的当前值(其中 i 的值为 3 ,因为循环已完成, item 的值为 'item2' ) . 请注意,我们从0开始索引,因此 item 的值为 item2 . 并且我将 i 增加到值 3 .

    当使用变量 item 的块级声明(通过 let 关键字)而不是通过 var 关键字的函数范围变量声明时,可能会有所帮助 . 如果进行了更改,则数组 result 中的每个匿名函数都有自己的闭包;运行示例时,输出如下:

    item0 undefined
    item1 undefined
    item2 undefined
    

    如果变量 i 也使用 let 而不是 var 定义,则输出为:

    item0 1
    item1 2
    item2 3
    

    例7

    在最后一个示例中,每次调用main函数都会创建一个单独的闭包 .

    function newClosure(someNum, someRef) {
        // Local variables that end up within closure
        var num = someNum;
        var anArray = [1,2,3];
        var ref = someRef;
        return function(x) {
            num += x;
            anArray.push(num);
            console.log('num: ' + num +
                '; anArray: ' + anArray.toString() +
                '; ref.someVar: ' + ref.someVar + ';');
          }
    }
    obj = {someVar: 4};
    fn1 = newClosure(4, obj);
    fn2 = newClosure(5, obj);
    fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
    fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
    obj.someVar++;
    fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
    fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;
    

    摘要

    如果一切看起来都不清楚,那么最好的办法是玩这些例子 . 阅读解释要比理解例子困难得多 . 我对闭合和堆叠框架等的解释在技术上并不正确 - 它们是用于帮助理解的粗略简化 . 一旦基本想法被理解,您可以稍后获取详细信息 .

    终点:

    • 每当在另一个函数中使用 function 时,都会使用闭包 .

    • 每当在函数内部使用 eval() 时,都会使用闭包 . 你 eval 的文本可以引用函数的局部变量,在 eval 中你甚至可以使用 eval('var foo = …') 创建新的局部变量

    • 在函数内部使用 new Function(…)Function constructor)时,它不会创建闭包 . (新函数不能引用外部函数的局部变量 . )

    • JavaScript中的闭包就像保留所有局部变量的副本一样,就像函数退出时一样 .

    • 最好认为闭包始终只是函数的一个入口,并且局部变量被添加到该闭包中 .

    • 每次调用带闭包的函数时,都会保留一组新的局部变量(假设函数内部包含一个函数声明,并且返回对该函数内部函数的引用,或者在某些函数中保留一个外部引用)办法) .

    • 两个函数可能看起来像是具有相同的源文本,但由于它们的'hidden'闭包而具有完全不同的行为 . 我不认为JavaScript代码实际上可以找出函数引用是否有闭包 .

    • 如果您正在尝试进行任何动态源代码修改(例如: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); ),如果 myFunction 是一个闭包它将无效(当然,您甚至不会想到在运行时进行源代码字符串替换,但是.. ) .

    • 可以在函数&mdash中的函数声明中获取函数声明,并且可以在多个级别获得闭包 .

    • 我认为通常闭包是函数和捕获变量的术语 . 请注意,我在本文中没有使用该定义!

    • 我怀疑JavaScript中的闭包与通常在函数式语言中找到的闭包不同 .

    链接

    谢谢

    如果您刚刚学习了闭包(在这里或其他地方!),那么我对您提出的任何有关您可能建议的任何可能使本文更清晰的更改的反馈感兴趣 . 发送电子邮件至morrisjohns.com(morris_closure @) . 请注意,我不是JavaScript的大师 - 也不是关闭 .


    莫里斯的原帖可以在Internet Archive中找到 .

  • 6465

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

  • 329

    也许除了最早熟的六岁孩子之外,还有一些例子,但是有一些例子帮助我在JavaScript中实现了关闭概念 .

    闭包是一个可以访问另一个函数范围(其变量和函数)的函数 . 创建闭包的最简单方法是使用函数内的函数;原因是在JavaScript中,函数始终可以访问其包含函数的作用域 .

    function outerFunction() {
        var outerVar = "monkey";
        
        function innerFunction() {
            alert(outerVar);
        }
        
        innerFunction();
    }
    
    outerFunction();
    

    警告:猴子

    在上面的例子中,调用了outerFunction,后者又调用了innerFunction . 注意innerVar如何可用于innerFunction,通过正确警告outerVar的值来证明 .

    现在考虑以下内容:

    function outerFunction() {
        var outerVar = "monkey";
        
        function innerFunction() {
            return outerVar;
        }
        
        return innerFunction;
    }
    
    var referenceToInnerFunction = outerFunction();
    alert(referenceToInnerFunction());
    

    警告:猴子

    referenceToInnerFunction设置为outerFunction(),它只返回对innerFunction的引用 . 当调用referenceToInnerFunction时,它返回outerVar . 同样,如上所述,这表明innerFunction可以访问outerVar的变量outerVar . 此外,有趣的是,即使在outerFunction完成执行后,它仍保留此访问权限 .

    在这里,事情变得非常有趣 . 如果我们要删除outerFunction,比如将其设置为null,您可能会认为referenceToInnerFunction将失去对outerVar值的访问权限 . 但这种情况并非如此 .

    function outerFunction() {
        var outerVar = "monkey";
        
        function innerFunction() {
            return outerVar;
        }
        
        return innerFunction;
    }
    
    var referenceToInnerFunction = outerFunction();
    alert(referenceToInnerFunction());
    
    outerFunction = null;
    alert(referenceToInnerFunction());
    

    警告:猴子警告:猴子

    但这是怎么回事?现在,为了将outerFunction设置为null,referenceToInnerFunction如何知道outerVar的值?

    referenceToInnerFunction仍然可以访问outerVar的值的原因是因为当首先通过将innerFunction放在outerFunction中来创建闭包时,innerFunction将对outerFunction的作用域(其变量和函数)的引用添加到其作用域链中 . 这意味着innerFunction具有指向所有outerFunction变量的指针或引用,包括outerVar . 因此,即使在outerFunction完成执行时,或者即使它被删除或设置为null,其范围内的变量(如outerVar)也会留在内存中,因为在返回到的内部函数部分上对它们的未完成引用referenceToInnerFunction . 要从内存中真正释放outerVar和其余的outerFunction变量,你必须摆脱对它们的这种杰出引用,比如将referenceToInnerFunction设置为null .

    //////////

    关于闭包的另外两件事要注意 . 首先,闭包将始终可以访问其包含函数的最后一个值 .

    function outerFunction() {
        var outerVar = "monkey";
        
        function innerFunction() {
            alert(outerVar);
        }
        
        outerVar = "gorilla";
    
        innerFunction();
    }
    
    outerFunction();
    

    警告:大猩猩

    其次,当创建一个闭包时,它会保留对其所有封闭函数的变量和函数的引用;它无法挑选 . 但是,闭包应该谨慎使用,或者至少要谨慎使用,因为它们可能会占用大量内存;在包含函数完成执行后很长时间内,很多变量都可以保存在内存中 .

  • 455

    我在一段时间后写了一篇博文,解释了闭包 . 这就是我所说的关于 why 关闭的闭包你想要的一个 .

    闭包是一种让函数具有持久的私有变量的方法 - 也就是说,只有一个函数知道的变量,它可以跟踪以前运行的信息 .

    从这个意义上讲,它们让函数有点像具有私有属性的对象 .

    全文:

    So what are these closure thingys?

  • 3852

    你正在睡觉,你邀请丹 . 你告诉Dan带一个XBox控制器 .

    丹邀请保罗 . 丹要求保罗带一个控制器 . 有多少控制器被带到聚会上?

    function sleepOver(howManyControllersToBring) {
    
        var numberOfDansControllers = howManyControllersToBring;
    
        return function danInvitedPaul(numberOfPaulsControllers) {
            var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
            return totalControllers;
        }
    }
    
    var howManyControllersToBring = 1;
    
    var inviteDan = sleepOver(howManyControllersToBring);
    
    // The only reason Paul was invited is because Dan was invited. 
    // So we set Paul's invitation = Dan's invitation.
    
    var danInvitedPaul = inviteDan(howManyControllersToBring);
    
    alert("There were " + danInvitedPaul + " controllers brought to the party.");
    
  • 82

    好的,和一个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
    

    如您所见,无论房间是否锁定,房间内的玩具仍可通过兄弟进入 . 这是a jsbin玩它 .

  • 47

    闭包很简单:

    以下简单示例涵盖了JavaScript闭包的所有要点 . *

    这是一个 生产环境 可以增加和增加的计算器的工厂:

    function make_calculator() {
      var n = 0; // this calculator stores a single number n
      return {
        add: function(a) {
          n += a;
          return n;
        },
        multiply: function(a) {
          n *= a;
          return n;
        }
      };
    }
    
    first_calculator = make_calculator();
    second_calculator = make_calculator();
    
    first_calculator.add(3); // returns 3
    second_calculator.add(400); // returns 400
    
    first_calculator.multiply(11); // returns 33
    second_calculator.multiply(10); // returns 4000
    

    The key point: 每次调用 make_calculator 都会创建一个新的局部变量 n ,该函数在 make_calculator 返回后很长时间内仍然可以被 addmultiply 函数使用 .

    如果您熟悉堆栈帧,这些计算器似乎很奇怪:在 make_calculator 返回后,他们如何继续访问 n ?答案是想象JavaScript不使用"stack frames",而是使用"heap frames",它可以在使它们返回的函数调用之后持久化 .

    addmultiply 这样访问外部函数**中声明的变量的内部函数称为闭包 .

    That is pretty much all there is to closures.


    *例如,它涵盖了另一个答案中给出的“闭包傻瓜”一文中的所有要点,除了示例6,它简单地表明变量可以在声明之前使用,这是一个很好的事实,但是与闭包完全无关 . 它还涵盖了接受答案中的所有要点,除了函数将其参数复制到局部变量(命名函数参数)的点(1),以及(2)复制数字创建新数字,但复制对象引用为您提供对同一对象的另一个引用 . 这些也很好知道但又与封闭完全无关 . 它也非常类似于这个答案中的例子,但是更短,更抽象 . 它没有涵盖这个答案或这个评论的要点,即JavaScript使得很难将循环变量的当前值插入到内部函数中:“插入”步骤只能通过包含辅助函数来完成你的内部函数,并在每次循环迭代时调用 . (严格地说,内部函数访问辅助函数的变量副本,而不是插入任何东西 . )同样,在创建闭包时非常有用,但不是闭包的一部分或它是如何工作的 . 由于闭包在ML等函数式语言中的工作方式不同,还存在额外的混淆,其中变量绑定到值而不是存储空间,从而提供了一种持续理解闭包的人(即“插入”方式),即只是对JavaScript不正确,其中变量总是绑定到存储空间,而永远不会绑定到值 .

    **任何外部函数,如果有几个嵌套,或甚至在全局上下文中,正如这个答案清楚地指出的那样 .

  • 543

    认真对待这个问题,我们应该找出一个典型的6岁孩子的认知能力,尽管如此,对JavaScript感兴趣的人并不那么典型 .

    Childhood Development: 5 to 7 Years它说:

    您的孩子将能够按照两步指示 . 例如,如果您对您的孩子说:“去厨房给我一个垃圾袋”,他们就能记住这个方向 .

    我们可以使用这个例子来解释闭包,如下所示:

    厨房是一个具有局部变量的闭包,称为trashBags . 厨房内有一个名为getTrashBag的功能,它可以获得一个垃圾袋并将其返回 .

    我们可以在JavaScript中编写代码,如下所示:

    function makeKitchen() {
      var trashBags = ['A', 'B', 'C']; // only 3 at first
    
      return {
        getTrashBag: function() {
          return trashBags.pop();
        }
      };
    }
    
    var kitchen = makeKitchen();
    
    console.log(kitchen.getTrashBag()); // returns trash bag C
    console.log(kitchen.getTrashBag()); // returns trash bag B
    console.log(kitchen.getTrashBag()); // returns trash bag A
    

    进一步说明闭包有趣的原因:

    • 每次调用 makeKitchen() 时,都会创建一个新的闭包,它有自己独立的 trashBags .

    • trashBags 变量是每个厨房内部的本地变量,无法在外部访问,但 getTrashBag 属性的内部函数可以访问它 .

    • 每个函数调用都会创建一个闭包,但是除非可以从闭包外部调用可以访问闭包内部的内部函数,否则不需要保持闭包 . 使用 getTrashBag 函数返回对象在此处执行此操作 .

  • 52

    闭包就像一个物体 . 无论何时调用函数,它都会被实例化 .

    JavaScript中闭包的范围是词法,这意味着闭包所属的函数中包含的所有内容都可以访问其中的任何变量 .

    如果你,变量包含在闭包中

    • var foo=1; 分配它

    • 刚写 var foo;

    如果内部函数(包含在另一个函数中的函数)访问这样的变量而没有在var自己的范围内定义它,它会修改外部闭包中变量的内容 .

    闭包比运行时更长产生它的功能 . 如果其他函数使它超出定义它们的闭包/作用域(例如作为返回值),那些将继续引用该闭包 .

    示例

    function example(closure) {
      // define somevariable to live in the closure of example
      var somevariable = 'unchanged';
    
      return {
        change_to: function(value) {
          somevariable = value;
        },
        log: function(value) {
          console.log('somevariable of closure %s is: %s',
            closure, somevariable);
        }
      }
    }
    
    closure_one = example('one');
    closure_two = example('two');
    
    closure_one.log();
    closure_two.log();
    closure_one.change_to('some new value');
    closure_one.log();
    closure_two.log();
    

    输出

    somevariable of closure one is: unchanged
    somevariable of closure two is: unchanged
    somevariable of closure one is: some new value
    somevariable of closure two is: unchanged
    
  • 43

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

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

    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
    

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

    无论何时在较大范围内创建较小的范围,较小的范围始终都能够查看较大范围内的范围 .

  • 48