深度解析JS中的作用域和作用域链的问题

写在前面

当一个人开始认真过自己的生活时,就是最美好最幸福的时刻,要一直记得这种美好。

今天呢,再来掰扯掰扯js中作用域和作用域链的问题,就是我偶然之间发现的一个宝藏老师,看了他对作用域和作用域链的解释,我又了解到了很多我之前不知道的知识。这个宝藏老师呢就是渡一教育的姬成老师,叫成哥吧。哈哈哈哈哈哈哈,就是在哔哩哔哩上面找到的,个人感觉成哥讲的知识点是很好理解的,讲授的知识对刚接触js语言的同学是很容易接受的。个人是非常非常非常喜欢的成哥讲课,很搞笑,收获也很多,听他的课再也不用担心犯困了。推荐给你们——Javascript。喜欢的话,可以看看。

还有一点,今天的这篇文章跟我昨天的那篇文章有联系——JS预编译。这里面写的有一些相关知识,看看会比较好理解这次深入写作用域和作用域链的内容。

主要目的是为了深入理解作用域链构成的过程,有点偏理论,但是我会画图的,尽量描述的形象一点。其实也没有那么难了,在了解了整个过程后,明白了就完全不是问题。ok,下面要进入主题拉。

 

作用域和作用域链

我们应知道,每个JavaScript函数都是一个对象,有对象相继的就会出现属性和方法。既然对象有属性和方法,我们就能访问对象的属性和方法,但是呢,对象的有些属性是我们访问不了的,这些我们访问不了的属性是仅供Javascript引擎存取的。JavaScript引擎具体内容呢,暂且忽略(目前是知识盲区部分,捂脸捂脸)。[[ scope ]] 就是一个我们访问不了的对象的属性

 

作用域

之前接触的函数作用域时,简单理解就是函数运行的一个环境,执行完毕环境释放。下面有一个更深刻的含义:

[[ scope ]] :就是我们所说的作用域,其中存储了运行期上下文(执行期上下文,也就是AO对象)的集合

运行期上下文——作用域。重点理解:执行期上下文内部对象AO。

当函数执行时,也可以说函数执行的前一刻,函数会创建一个称为执行期上下文的内部对象(就是AO对象)一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对于的执行期上下文是独一无二的,所以多次调用一个函数会导致创建多个执行期上下文,当函数执行完毕后,他所产生的执行期上下文被销毁。

这样详细的解释,让我们知道了作用域的本质是什么,作用域的本质就是函数对象的[[ scope ]]属性。主要是我们通常都叫作用域,执行期上下文、执行期上下文产生的内部对象AO以及[[ scope ]]属性,都是用来辅助理解作用域的,同时还为接下来进一步理解作用域链做铺垫的。

总结一下

作用域是(1)执行期上下文(2)内部对象AO,(3)[[ scope ]]属性。就是一个概念,有不同的名字,但是指的都是一个概念。

作用域链

简答理解的作用域链就是由多个不同作用域组成的。

[[ scope ]]中所存储的执行上下文对象的集合,这个集合呈链式连接,我们把这种链式连接叫做作用域链。

简单点说,就是[[ scope ]]属性里存储的是执行期上下文的集合,执行期上下文就是

 

作用域和作用域链的案例分析

举例说明

        function a() {
            function b() {
                var bb = 234;
            }
            var aa = 123;
            b();
        }
        var glob = 100;
        a();

 上面的例子中,有三个执行期上下文就是三个作用域。全局作用域、a函数作用域以及b函数作用域。要注意哈,只有当函数执行时,才会产生对应得执行期上下文。

代码执行分析

1. 首先读取得是a函数的定义,此时,a.[[ scope ]]属性中,就存储了全局作用域得执行期上下文产生的GO对象。存储的是a函数当前所处的执行期上下文

2.  到a函数的执行时:

(1)a函数此时会产生一个自己的执行期上下文的对象AO,并把它放在a.[[ scope ]]属性最顶端,就是a的作用域链的最顶端。此时a.[[ scope ]]属性中存储了两个作用域,a函数的AO对象和全局GO对象。

(2)读取函数b的定义,同时 b.[[ scope ]] 属性存贮的内容就是把执行a函数时a.[[ scope ]]中的内容复制一份给到b.[[ scope ]]。

(3)到函数b的执行时,b函数产生一个自己的执行期上下文对象AO,并把它放到b.[[ scope ]]的最顶端,就是b的作用域链的最顶端。

(4)函数b执行完毕后,销毁b的执行期上下文对象AO。

3. 代码执行完毕。释放函数a的执行期上下文对象AO。就是,a函数执行完毕,释放自己的执行期上下文对象,把a的AO对象销毁掉,此时a.[[ scope ]]中只有全局对象GO。这里要注意,销毁了函数a的AO对象,直接就把函数b也销毁了。这一点,可以为下一步了解闭包做铺垫的。哈哈哈哈哈哈哈。

 

其实我们可以这样理解:把[[ scope ]]属性看做是一个仓库,用来存储数据,需要使用哪个数据了,就去里面取出来。

 

看下代码解释,里面的注释

// 程序执行 读取函数a定义——声明变量并赋值——执行函数a
        // 1. 读取到a函数的定义
        // a denfind a.[[scope]] -- > 0:GO 对象
        // 就是a的[[scope]]属性存储的是a函数当前所处执行期上下文 即现在的全局作用域 会产生GO对象
        function a() {
            // b denfined 时 b.[[scope]] -- > 0:AO 对象
            //                             1:GO 对象
            function b() {
                var bb = 234;
            }
            var aa = 123;
            // b doing 时 b.[[scope]] -- > 0:bAO 对象
            //                             1:aAO 对象
            //                             2:GO 对象
            b();
        }
        var glob = 100;
        // 调用a函数,在a函数产生一个执行期上下文 创建a的AO对象 并把自己产生的AO对象放到作用域的最顶端
        // a doing a.[[scope]] -- > 0:AO 对象
        //                          1:GO 对象
        a();

 

图解图解图解 

为了更好的理解,放个图就很好理解了。

这个我用Process ON在线作图工具画的,没找到合适的框图,就用流程图的框架一个一个矩形堆成的,但是还是能看的。这个图解再结合上面我的文章描述,以及代码解释,应该可以非常非常清楚地理解作用域链的问题了啊。

 

这里面的斜杠线就是短线的问题,销毁AO对象的本质就是0号位置与AO对象的短线

 

 

完整过程的图解

 

 

 

 

好了好了,结束了。下一步就要浅析闭包了啊啊啊啊啊。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值