为了账号安全,请及时绑定邮箱和手机立即绑定

javascript 作用域链

标签:
JavaScript

本篇主要详细介绍作用域链的建立过程。

作用域链是由当前上下文和上层上下文的一系列变量对象组成的层级链。它保证了当前执行环境对符合访问权限的变量和函数的有序访问。

我们已经知道,执行上下文分为创建和执行两个阶段,而在执行上下文的执行阶段,当需要查找某个变量或函数时,会在当前上下文的变量对象(活动对象)中进行查找,若是没有找到,则会在上一层的上下文的变量对象中进行查找,一直找到全局上下文中的变量对象(全局对象)。

那么当前上下文是如何有序地去查找它所需要的变量或函数的呢?
答案就是依靠当前上下文中的作用域链,其包含了当前上下文和上层上下文中的变量对象,以便其一层一层地去查找其所需要的变量和函数。

那么我们接下来介绍一下作用域链的建立过程~

javascript 中主要包含了全局作用域和函数作用域,函数作用域是在函数被定义(声明)的时候确定的
每一个函数都会包含一个 [[scope]] 内部属性,在函数被定义的时候,该函数的 [[scope]] 属性会保存其上层上下文的变量对象,形成包含上层上下文变量对象的层级链。
需要注意的是,该层级链并没有包含当前上下文的变量对象,所以该层级链并不是完整的作用域链。
所以上述想表达的重点就是,[[scope]] 属性的值是在函数被定义(声明)的时候确定的。这一点很重要。

当该函数被调用的时候,其执行上下文会被创建并入栈。在创建阶段,会生成其变量对象,并将该变量对象添加到作用域链(scope)的顶端并将原先的层级链([[scope]])添加进该作用域链。而在执行阶段,变量对象会变为活动对象,其相应属性会被赋值。
至此该执行上下文的作用域链被建立,在执行阶段代码执行时,会根据作用域链对符合访问权限的变脸和函数进行有序访问。

举个例子~
假设有一个 javascript 文件中包含如下代码

var a = 1;function fn1(){    var b = 1;    function fn2(){        var c=1;
    }
    
    fn2();
}

fn1();

这里我们回顾一下其上述代码执行过程中执行上下文栈的行为

/*伪代码*/// 代码执行时最先进入全局环境,全局上下文被创建并入栈ECStack.push(globalContext);// fn1 被调用,fn1 函数上下文被创建并入栈ECStack.push(<fn1> functionContext);// fn1 中调用 fn2,fn2 函数上下文被创建并入栈ECStack.push(<fn2> functionContext);// fn2 执行完毕,fn2 函数上下文出栈ECStack.pop();// fn1 执行完毕,fn1 函数上下文出栈ECStack.pop();// 代码执行完毕,全局上下文出栈ECStack.pop();

这里我们主要关注的是 fn2 上下文被创建并入栈的过程。

上面说过,函数作用域是在函数被定义的时候确定的(即 [[scope]] 属性的值是在函数被定义的时候确定的)。所以我们可以知道,fn2 的上层上下文包括了 fn1 上下文和全局上下文

fn2.[[scope]]=[fn1Context.VO,globalContext.VO]

当 fn2 被调用的时候,其执行上下文被创建并入栈,此时会生成变量对象并将该变量对象添加进作用域链的顶端,并且将 [[scope]] 添加进作用域链

fn2Context.Scope=[fn2Context.VO].concat([[scope]])
=>
fn2Context.Scope=[fn2Context.VO,fn1Context.VO,globalContext.VO]

最后,在该上下文的执行阶段,变量对象会变为活动对象,并且其相应属性会被赋值。



作者:淘淘笙悦
链接:https://www.jianshu.com/p/b6a808ae4ed7


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消