理解javascript作用域与作用域链

本文详细解释了JavaScript中作用域和作用域链的概念,包括作用域的定义、作用域链的创建过程及其如何帮助函数访问不同层级的变量。

如果不明白VO/AO可以先看一看上一个博客内容:理解javascript执行环境(执行上下文)


正文内容

作用域
- 一块代码可执行区域,在该区域中的变量和函数在区域外部是不可见的

作用域链
- 由多个执行上下文的变量对象构成的链表就叫做作用域链。
- 当一个函数被创建时会默认添加一个名为[[Scope]]的特殊内部属性,如果该函数时一个全局函数,那么在该函数被创建时会预先创建一个包含全局变量对象(全局VO)的作用域链,这个作用域链被保存在该函数的[[Scope]]属性中,当该函数调用时,会为该函数创建一个执行环境,通过复制[[Scope]]属性中的对象构建起执行环境作用域链,之后又为该函数创建一个活动对象(AO)并推入执行环境作用域链的前端。如果该执行环境内部还有子函数,那么将执行环境作用域链保存到子函数的[[Scope]]属性中,以便子函数调用时取用

作用域链的创建

第一步 当程序执行时创建全局作用域和全局VO对象,发现fn1与fn2函数是在全局环境下定义,那么在创建fn1与fn2函数时预先创建一个包含全局VO对象指针的作用域链,保存到这两个函数各自的[[Scope]]属性当中,

<script>

// 全局环境创建 / 创建 VO 对象
var a = 7

 // fn1定义在全局环境下 ,创建包含全局VO指针的作用域链Scope Chain,保存到fn1.[[Scope]]属性中
function fn1 () {  
    var b = 8
    function child () {
        var c = a + b
    }
    child()
}

// fn2也定义在全局环境下 ,创建包含全局VO指针的作用域链Scope Chain,保存到fn2.[[Scope]]属性中
function fn2 () {
    var x = a
}

fn1()
fn2()
</script>

这里写图片描述
第二步,当程序执行到fn1()调用,程序执行流进入fn1函数体中,创建fn1执行环境,并创建fn1的AO对象,之后将AO对象的指针添加到fn1.[[Scope]]中作用域链的前端

<script>

// 全局环境创建 / 创建 VO 对象
var a = 7

function fn1 () {  
    // 创建fn1执行环境 / 创建AO对象,将AO对象的指针添加到fn1.[[Scope]]中作用域链的最前端
    var b = 8
    function child () {
        var c = a + b
    }
    child()
}

function fn2 () {
    var x = a
}

fn1()
fn2()
</script>

这里写图片描述
第三步,程序在fn1中继续执行,发现函数child是定义在fn1的执行环境中的,那么将fn1.[[Scope]]中保存的作用域链复制给child.[[Scope]],这时fn1.[[Scope]]中保存的作用域链应该是Scope -> fn1 AO -> Global VO

<script>

// 全局环境创建 / 创建 VO 对象
var a = 7

function fn1 () {  
    // 创建fn1执行环境 / 创建AO对象,将AO对象的指针添加到fn1.[[Scope]]中作用域链的最前端
    var b = 8
    // 发现child定义在fn1执行环境中,将fn1.[[Scope]]中保存的作用域链复制给child.[[Scope]]
    function child () {
        var c = a + b
    }
    child()
}

function fn2 () {
    var x = a
}

fn1()
fn2()
</script>

第四步,发现child()函数调用,程序进入child函数中,创建环境与AO对象,并将AO对象添加到child.[[Scope]]中作用域链的前端
这里写图片描述
第五步,程序退出child函数并将其销毁,退出fn1函数并将其销毁,发现fn2()函数调用,进入fn2函数体内部,进行与fn1类似的操作

作用域链的访问

拿child函数来举例
1. 程序进入child函数体内部,查询child环境下的AO对象,没有发现a或b变量
2. 向后查找作用域链,发现fn1环境下的AO对象有变量b,但是没有变量a
3. 继续沿着作用域链向后查找,发现全局环境下的VO对象中包含着变量a
4. 取得a和b的值进行加法操作并将结果赋值给c
5. 全局VO永远处于作用域链的最末端,如果在全局VO中仍然没有找到需要的数据,那么浏览器将会报错
6. 作用域链 -> child AO -> fn1 AO -> Global VO

参考资料
- JavaScript高级程序设计(第3版)第七章-179页

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值