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”
    闭包是一对:

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


    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)
    TLDR

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

    详细信息

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

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

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

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

    function g() {
        function h() {}
    }
    
    如果外部函数返回内部函数,那么外部词法环境将在外部
    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"