Home >Web Front-end >JS Tutorial >A closer look at closures in JavaScript

A closer look at closures in JavaScript

PHPz
PHPzforward
2016-05-16 16:32:512017browse

This article will introduce you to closures in JavaScript. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to everyone.

A closer look at closures in JavaScript

Closure - a very important but difficult to grasp concept. Understanding closure can be seen as a rebirth in a sense - "You don't know Js》
Although everyone may be tired of seeing closures, I still want to try to summarize it! ! !

1. What is a closure

As the name suggests, when we encounter a problem, we first ask why is our usual way of thinking. Let’s try to answer:

  • A closure is a sub-function inside a function - 等于没说

  • It occurs when a function can remember and access the lexical scope it is in. Closure, even if the function is executed outside the current lexical scope. ——靠谱

  • A closure is a function that can read the internal variables of other functions. It is essentially a bridge between the internal and external links of the function——靠谱

  • A function and a reference to its surrounding state (lexical environment) are bundled together to form a closure - 很靠谱

Let’s try using Use code to describe the above answers and see which one you favorite~

1.1 Closure is a sub-function inside a function

First Look at this code:

function foo(params) {
    var a = '余光';

    function bar() {
        console.log(a);
    }
    bar()
}

foo(); // 余光

Based on the search rules of lexical scope, bar函数 can successfully print the a variable, and it is also a sub-function of foo, but strictly speaking it is not The concept of closure is not clearly expressed. It is more accurate to say that nested functions can access variables declared in a large outer scope.

1.2 Closure is a function that can read the internal variables of other functions. It is essentially a bridge between the internal function and the external link of the function

Look at the following example: The results of

function foo(params) {
    var a = '余光';

    function bar() {
        console.log(a);
    }
    return bar;
}

var res = foo();
res(); // 余光

are consistent because res is the foo reference returned when executing the bar function, and the bar function is able to save its lexical environment.

1.3 A function and a reference to its surrounding state (lexical environment) are bundled together to form a closure

Let’s look at the following code:

var name = '余光';

function foo() {
  console.log(name); // 余光 
}

foo(); //余光

The context of foo is statically saved, and it is saved when the function is created. Let's verify it below:

var name = '余光';

function foo() {
  console.log(name); // 余光
}

(function (func) {
    var name = '老王';

    func()
})(foo); // 余光

Here we can understand - closures are formed after functions are created. They save the scope chain of the upper context and save it in [[scope]]. If your concept of [[scope]] is already vague, you might as well take a few minutes to read the article "Execution Context in JavaScript".

1.4 Summary

Note: The statement that closure is a returned sub-function inside a function is correct in itself, but it depends on what Starting point:

In ECMAScript, closure refers to:

  • From a theoretical perspective: all functions. Because they all save the data of the upper context when they are created. This is true even for simple global variables, because accessing global variables in a function is equivalent to accessing free variables. At this time, the outermost scope is used.

  • From a practical perspective: the following functions are considered closures:

    • It still exists even if the context in which it was created has been destroyed (e.g., an inner function is Returned in the function)
    • refers to the free variable in the code

Summary:

  • Closed The package code block creates the combination of data in the context of the code block
  • A closure is a function that can read the internal variables of other functions. It is essentially a bridge between the internal and external links of the function
  • Different perspectives have different interpretations of closures

Note: These are not all closures, just like when you are asked - what is closure, your above answer does not Failure to end this topic will often lead to more topics.

A closer look at closures in JavaScript

2. Try to analyze the closure

It’s still the classic code:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}

var foo = checkscope();
foo(); // local scope

First we To analyze the changes in the execution context stack and execution context in this code.

  • Enter the global code, create a global execution context, and push the global execution context into the execution context stack

  • Global execution context initialization

  • Execute checkscope function, create checkscope function execution context, checkscope execution context is pushed into execution context stack

  • checkscope execution context initialization, create variable object, scope chain , this, etc.

  • The checkscope function is executed, and the checkscope execution context is popped from the execution context stack

  • 执行 f 函数,创建 f 函数执行上下文,f 执行上下文被压入执行上下文栈

  • f 执行上下文初始化,创建变量对象、作用域链、this等

  • f 函数执行完毕,f 函数上下文从执行上下文栈中弹出

A closer look at closures in JavaScript

f 函数执行的时候,checkscope 函数上下文已经被销毁了啊(即从执行上下文栈中被弹出),怎么还会读取到 checkscope 作用域下的 scope 值呢?

当我们了解了具体的执行过程后,我们知道 f 执行上下文维护了一个作用域链:

因为这个作用域链:

  • f 函数依然可以读取到 checkscopeContext.AO 的值;
  • f 函数引用了 checkscopeContext.AO 中的值的时候,即使 checkscopeContext 被销毁了,JavaScript 依然会让 checkscopeContext.AO 活在内存中;
  • f 函数依然可以通过 f 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。

多么浪漫的思想——只要你需要我,那我我本应该被销毁,你也能找到我~

A closer look at closures in JavaScript

三、经典问题

3.1 多个对象引用同一个[[Scope]],你遇到过吗?

直接上代码:

var child1;
var child2;
function parent() {
    var x = 1;

    child1 = function () {
        console.log(++x)
    };
    child2 = function () {
        console.log(--x)
    };
}
parent();
child1(); // 2
child1(); // 3
child2(); // 2

大家可能不理解,child1child他们两个函数在创建后都保存了上层上下文,万万没想到,同一个上下文创建的闭包是共用一个[[scope]]属性的,某个闭包对其中[[Scope]]的变量做修改会影响到其他闭包对其变量的读取。

3.2 闭包轻松解决的经典问题

大家一定对下面这段代码很眼熟:

var arr = []
for(var i = 0; i < 10; i++){
    arr[i] = function () {
        console.log(i)
    }
}
arr[0](); // 10
arr[1](); // 10
arr[2](); // 10
arr[3](); // 10

我们这么解释它:同一个上下文中创建的闭包是共用一个[[Scope]]属性的

因此上层上下文中的变量i是可以很容易就被改变的。

arr[0],arr[1]…arr[9]他们共用一个[[scope]],最终执行的时候结果当然一样。

如何利用闭包来解决这个问题呢?

var arr = []
for(var i = 0; i < 10; i++){
    arr[i] = (function (i) {
        return function () {
            console.log(i);
        }
    })(i)
}
arr[0](); // 0
arr[1](); // 1
arr[2](); // 2
arr[3](); // 3

我们通过立即执行匿名函数的方式隔离了作用域,当执行 arr[0] 函数的时候,arr[0] 函数的作用域链发生了改变:

arr[0]Context = {
    Scope: [AO, 匿名函数Context.AO globalContext.VO]
}

匿名函数执行上下文的AO为:

匿名函数Context = {
    AO: {
        arguments: {
            0: 0,
            length: 1
        },
        i: 0
    }
}

我们看到,这时函数的[[Scope]]属性就有了真正想要的值了,为了达到这样的目的,我们不得不在[[Scope]]中创建额外的变量对象。要注意的是,在返回的函数中,如果要获取i的值,那么该值还是会是10。

3.3 总结

  • 函数内的所有内部函数都共享一个父作用域,因此创建的闭包是共用的。
  • 利用闭包隔离作用域的特性可以解决共享作用域的问题

推荐学习:《PHP视频教程

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete