JavaScript中的作用域和上下文

293 阅读2分钟

前提

想起来之前面试,被问了一道题目:

(function a(){
    function b(){}
})()

问我这个是不是闭包。我觉得不是,然后引发了一些思考。

1、作用域 scope

作用域就是指函数可访问的范围,决定了代码区块中变量和其他资源的可见性。举个例子:

var name = 'google';
function foo(){
    console.log.log(name)
}
function bar(){
    var name = 'baidu';
    foo()
}
bar() // 'google'

这说明了js的作用域是在函数被定义的时候确定的。

2、上下文 context


上下文通常就是指函数中this 的值。

var name = 'google'
function foo(){
    var name = 'baidu';
    console.log(this.name);
}
foo() // 'google'

函数执行时他的上下文默认指向window(严格模式为undefined),最终指向调用这个函数的对象,可以通过call、apply、bind改变上下文。

3、执行期上下文

在函数执行的时候(或者说执行前一刻)会创建一个名为执行期上下文的内部对象,它定义了一个函数执行时的环境。函数执行完毕,上下文销毁。

我的理解,执行期上下文跟作用域链是一样的东西

4、作用域链

作用域链 scope chain当中储存了执行其上下文的集合,这个集合呈链式链接,称作作用域链。举个例子:

function a(){
    var a = 123
    function b(){
        var b =234
    }
    b()
}
var global = 100;
a()
/*
    ·函数a 被定义时,他的作用域链的第0位,就是一个GO:
    a.[[scope]] -- > [{
        this: window,
        window: (Object),
        document:(object),
        a: (function),
        global: 100
    }]
    ·a执行时,创建自己的AO:
    {
        this: window,
        arguments: [],
        a: 123,
        b: (function)
    }
    放到作用域链的最顶端:
    a.[[scope]] --> [
        {
            this: window,
            arguments: [],
            a: 123,
            b: (function)
        },
        {
            this: window,
            window: (Object),
            document:(object),
            a: (function),
            global: 100
        }
    ]    
    ·然后创建函数b,函数n 作用域链的第0为就是GO,也就是函数a的作用域链:
    b.[[scope]] -- >[
        a.[[scope]]
    ]
    ·b函数执行的时候,再创建自己的AO:放到作用域链最顶端
    b.[[scope]] --> [
        {
            b: 234
        },
        a.[[scope]]
    ]
*/ 

由此,我们可以解释为什么作用域中那道题会输出'google'了。因为函数b被定义的时候作用域链已经形成,不管他在何处被调用。

总结

闭包的定义:函数的执行会形成一个私有的执行期上下文,如果上下文中有些内容(一般是指堆内存地址)被上下文以外的事物所占用,则当前上下文不能被出栈释放。

所以现在再回头去看开头的那道题,函数 b 的作用域链是[b.AO,a.[[scope]] ],可是当立即执行函数执行完毕,他的作用域链就已经被销毁了,连带函数b也被销毁了。没有任何内容被占用,所以我觉得不是闭包。

最后

这是我的第一篇博文,肯定有很多错谬的地方,希望大佬们不要喷我而是指出我错误地方,感谢!