Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/465.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaScript闭包是如何工作的?_Javascript_Function_Variables_Scope_Closures - Fatal编程技术网

JavaScript闭包是如何工作的?

JavaScript闭包是如何工作的?,javascript,function,variables,scope,closures,Javascript,Function,Variables,Scope,Closures,您如何向了解JavaScript闭包所包含的概念(例如函数、变量等)但不了解闭包本身的人解释JavaScript闭包 我在维基百科上看到过Give,但不幸的是它没有帮助。闭包是一对: 函数,以及 对该函数外部作用域(词法环境)的引用 词法环境是每个执行上下文(堆栈帧)的一部分,是标识符(即局部变量名)和值之间的映射 JavaScript中的每个函数都维护对其外部词汇环境的引用。此引用用于配置调用函数时创建的执行上下文。此引用使函数内部的代码能够“查看”函数外部声明的变量,而不管函数何时何地被调用

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


我在维基百科上看到过Give,但不幸的是它没有帮助。

闭包是一对:

  • 函数,以及
  • 对该函数外部作用域(词法环境)的引用
  • 词法环境是每个执行上下文(堆栈帧)的一部分,是标识符(即局部变量名)和值之间的映射


    JavaScript中的每个函数都维护对其外部词汇环境的引用。此引用用于配置调用函数时创建的执行上下文。此引用使函数内部的代码能够“查看”函数外部声明的变量,而不管函数何时何地被调用

    如果一个函数被一个函数调用,而另一个函数又被另一个函数调用,那么就会创建一个指向外部词汇环境的引用链。此链称为范围链

    在以下代码中,
    internal
    与调用
    foo
    时创建的执行上下文的词法环境形成闭包,在变量
    secret
    上关闭:

    函数foo(){
    const secret=Math.trunc(Math.random()*100)
    返回函数内部(){
    log(`密码是${secret}.`)
    }
    }
    const f=foo()/`secret`不能直接从`foo'外部访问`
    f()//检索'secret'的唯一方法是调用'f`
    TLDR

    闭包是函数与其外部词法(即编写的)环境之间的链接,这样,在该环境中定义的标识符(变量、参数、函数声明等)从函数内部可见,而不管函数何时或从何处调用

    详细信息

    在ECMAScript规范的术语中,闭包可以说是通过引用每个函数对象来实现的,它指向定义函数的对象

    通过内部方法调用函数时,函数对象上的引用将复制到新创建的(堆栈帧)的外部环境引用中

    在以下示例中,函数
    f
    在全局执行上下文的词法环境中关闭:

    function f() {}
    
    在下面的示例中,function
    h
    关闭function
    g
    的词法环境,这反过来又关闭全局执行上下文的词法环境

    function g() {
        function h() {}
    }
    
    如果内部函数由外部函数返回,那么外部词法环境将在外部函数返回后继续存在。这是因为如果最终调用内部函数,则外部词汇环境需要可用

    在以下示例中,函数
    j
    关闭函数
    i
    的词法环境,这意味着在函数
    i
    完成执行后很久,变量
    x
    从函数
    j
    内部可见:

    函数i(){
    变量x='mochacchino'
    返回函数j(){
    log('从函数j中打印x的值:',x)
    }
    } 
    常数k=i()
    
    setTimeout(k,500)//在500毫秒后调用k(即j)
    闭包很难解释,因为它们用于使某些行为正常工作,而每个人直觉上都希望这些行为正常工作。我发现解释它们的最好方法(以及我了解它们的方式)是想象没有它们的情况:

    const makePlus=函数(x){
    返回函数(y){returnx+y;};
    }
    常数plus5=makePlus(5);
    
    控制台日志(plus5(3))JavaScript中的每个函数都维护到其外部词汇环境的链接。词法环境是范围内所有名称(如变量、参数)及其值的映射

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

    函数foo(x){ var-tmp=3; 功能条(y){ console.log(x+y+(++tmp));//将记录16 } 巴(10); }
    富(2),闭包是内部函数可以访问外部函数中变量的地方。这可能是关于闭包的最简单的一行解释。

    这是一种尝试,旨在澄清在其他一些答案中出现的关于闭包的几个(可能的)误解

    • 闭包不仅仅是在返回内部函数时创建的。事实上,为了创建闭包,封闭函数根本不需要返回。您可以将内部函数指定给外部作用域中的变量,或者将其作为参数传递给另一个函数,在该函数中可以立即调用或稍后随时调用。因此,封闭函数的闭包可能会在调用封闭函数时立即创建,因为无论何时调用内部函数,在封闭函数返回之前或之后,任何内部函数都可以访问该闭包
    • 闭包不引用其作用域中变量旧值的副本。变量本身是闭包的一部分,因此访问其中一个变量时看到的值是访问该变量时的最新值。这就是为什么在循环内部创建的内部函数可能很棘手,因为每个函数都可以访问相同的外部变量,而不是在创建或调用函数时获取变量的副本
    • 闭包中的“变量”包括函数中声明的任何命名函数。它们还包括函数的参数。闭包还可以访问其包含闭包的变量
      var i;
      function foo(x) {
          var tmp = 3;
          i = function (y) {
              console.log(x + y + (++tmp));
          }
      }
      foo(2);
      i(3);
      
      /*
      *    When a function is defined in another function and it
      *    has access to the outer function's context even after
      *    the outer function returns.
      *
      * An important concept to learn in JavaScript.
      */
      
      function outerFunction(someNum) {
          var someString = 'Hey!';
          var content = document.getElementById('content');
          function innerFunction() {
              content.innerHTML = someNum + ': ' + someString;
              content = null; // Internet Explorer memory leak for DOM reference
          }
          innerFunction();
      }
      
      outerFunction(1);​
      
      var isVotedUp = false;
      var isVotedDown = false;
      
      function voteUp_click() {
        if (isVotedUp)
          return;
        else if (isVotedDown)
          SetDownVote(false);
        else
          SetUpVote(true);
      }
      
      function voteDown_click() {
        if (isVotedDown)
          return;
        else if (isVotedUp)
          SetUpVote(false);
        else
          SetDownVote(true);
      }
      
      function SetUpVote(status) {
        isVotedUp = status;
        // Do some CSS stuff to Vote-Up button
      }
      
      function SetDownVote(status) {
        isVotedDown = status;
        // Do some CSS stuff to Vote-Down button
      }
      
      function princess() {
      
          var adventures = [];
      
          function princeCharming() { /* ... */ }
      
          var unicorn = { /* ... */ },
              dragons = [ /* ... */ ],
              squirrel = "Hello!";
      
          /* ... */
      
          return {
      
              story: function() {
                  return adventures[adventures.length - 1];
              }
          };
      }
      
      var littleGirl = princess();
      
      littleGirl.story();
      
      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
      
      var db = (function() {
          // Create a hidden object, which will hold the data
          // it's inaccessible from the outside.
          var data = {};
      
          // Make a function, which will provide some access to the data.
          return function(key, val) {
              if (val === undefined) { return data[key] } // Get
              else { return data[key] = val } // Set
          }
          // We are calling the anonymous surrounding function,
          // returning the above inner function, which is a closure.
      })();
      
      db('x')    // -> undefined
      db('x', 1) // Set x to 1
      db('x')    // -> 1
      // It's impossible to access the data object itself.
      // We are able to get or set individual it.
      
      // 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
      
      function foo (initValue) {
         //This variable is not destroyed when the foo function exits.
         //It is 'captured' by the two nested functions returned below.
         var value = initValue;
      
         //Note that the two returned functions are created right now.
         //If the foo function is called again, it will return
         //new functions referencing a different 'value' variable.
         return {
             getValue: function () { return value; },
             setValue: function (newValue) { value = newValue; }
         }
      }
      
      function bar () {
          //foo sets its local variable 'value' to 5 and returns an object with
          //two functions still referencing that local variable
          var obj = foo(5);
      
          //Extracting functions just to show that no 'this' is involved here
          var getValue = obj.getValue;
          var setValue = obj.setValue;
      
          alert(getValue()); //Displays 5
          setValue(10);
          alert(getValue()); //Displays 10
      
          //At this point getValue and setValue functions are destroyed
          //(in reality they are destroyed at the next iteration of the garbage collector).
          //The local variable 'value' in the foo is no longer referenced by
          //anything and is destroyed too.
      }
      
      bar();
      
      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
      
      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
      
      console.log('CLOSURES DONE RIGHT');
      
      var arr = [];
      
      function createClosure(n) {
          return function () {
              return 'n = ' + n;
          }
      }
      
      for (var index = 0; index < 10; index++) {
          arr[index] = createClosure(index);
      }
      
      for (var index in arr) {
          console.log(arr[index]());
      }
      
      console.log('CLOSURES DONE WRONG');
      
      function createClosureArray() {
          var badArr = [];
      
          for (var index = 0; index < 10; index++) {
              badArr[index] = function () {
                  return 'n = ' + index;
              };
          }
          return badArr;
      }
      
      var badArr = createClosureArray();
      
      for (var index in badArr) {
          console.log(badArr[index]());
      }
      
      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
      
      function sing(person) {
      
          var firstPart = "There was " + person + " who swallowed ";
      
          var fly = function() {
              var creature = "a fly";
              var result = "Perhaps she'll die";
              alert(firstPart + creature + "\n" + result);
          };
      
          var spider = function() {
              var creature = "a spider";
              var result = "that wiggled and jiggled and tickled inside her";
              alert(firstPart + creature + "\n" + result);
          };
      
          var bird = function() {
              var creature = "a bird";
              var result = "How absurd!";
              alert(firstPart + creature + "\n" + result);
          };
      
          var cat = function() {
              var creature = "a cat";
              var result = "Imagine That!";
              alert(firstPart + creature + "\n" + result);
          };
      
          fly();
          spider();
          bird();
          cat();
      }
      
      var person="an old lady";
      
      sing(person);
      
      function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }
      
      function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }
      
      var person="an old lady";
      
      sing(person);
      
      fly();
      spider();
      bird();
      cat();
      
      var a = 42;
      
      function b() { return a; }
      
      var parent = function() {
       var name = "Mary"; // secret
      }
      
      var parent = function() {
        var name = "Mary";
        var child = function(childName) {
          // I can also see that "name" is "Mary"
        }
      }
      
      var parent = function() {
        var name = "Mary";
        var child = function(childName) {
          return "My name is " + childName  +", child of " + name; 
        }
        return child; // child leaves the parent ->
      }
      var child = parent(); // < - and here it is outside 
      
      child("Alice") => "My name is Alice, child of Mary"