Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript深入之从作用域链理解闭包 #12

Open
CharlesGC opened this issue Aug 17, 2021 · 0 comments
Open

Javascript深入之从作用域链理解闭包 #12

CharlesGC opened this issue Aug 17, 2021 · 0 comments

Comments

@CharlesGC
Copy link
Owner

MDN 对闭包的定义为:

闭包是指那些能够访问自由变量的函数。

其中自由变量,指在函数中使用的,但既不是函数参数arguments也不是函数的局部变量的变量,其实就是另外一个函数作用域中的变量。

举个例子:

var a = 1

function foo() {
    console.log(a)
}

foo()

这里的a 既不是函数参数也不是foo的局部变量,那它就是自由变量。

那么,函数 foo + foo 函数访问的自由变量 a 不就是构成了一个闭包嘛……

确实如此,从技术角度上来讲,所有的Javascript 函数都是闭包。

还有个实践角度上的闭包,我们先来看看定义:

ECMAScript中,闭包指的是:

  1. 从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。

  2. 从实践角度:以下函数才算是闭包:

    i. 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)

    ii. 在代码中引用了自由变量

分析

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

var foo = checkscope();
foo();

首先我们要分析一下这段代码中执行上下文栈和执行上下文的变化情况

简要的执行过程如下:

  1. 进入全局代码,创建全局执行上下文,全局执行上下文压入执行上下文栈

  2. 全局执行上下文初始化

  3. 执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 执行上下文被压入执行上下文栈

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

  5. checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出

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

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

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

了解到这个过程,我们应该思考一个问题,那就是:

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

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

fContext = {
    Scope: [AO, checkscopeContext.AO, globalContext.VO],
}

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

所以,让我们再看一遍实践角度上闭包的定义:

  1. 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
  2. 在代码中引用了自由变量
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant