资讯专栏INFORMATION COLUMN

[学习笔记] JavaScript 作用域链

?xiaoxiao, / 2701人阅读

摘要:全局执行环境的变量对象始终是作用域链中的最后一个变量对象。综上,每个函数对应一个执行环境,每个执行环境对应一个变量对象,而多个变量对象构成了作用域链,如果当前执行环境是函数,那么其活动对象在作用域链的前端。

1.几个概念

先说几个概念:函数执行环境变量对象作用域链活动对象。这几个东东之间有什么关系呢,往下看~

函数

函数大家都知道,我想说的是,js中,在函数内部有两个特殊的对象:argumentsthisarguments 是一个类数组对象,包含着传入函数中的所有参数。 this 引用的是函数据以执行的环境对象。

执行环境

在《js高级程序设计》中,是这样定义的:

  

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。

这里要特别提到一个执行环境——全局执行环境。全局执行环境是最外围的执行环境,在web浏览器中,全局执行环境被认为是window对象。全局执行环境会一直存在于环境栈的最底端,直到关闭网页或者浏览器。
执行环境也叫执行上下文。

变量对象

《js高级程序设计》定义如下:

  

每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

由上可以看出两点:1.执行环境和变量对象是一一对应的。2.执行环境其实是一个“虚”的概念,而变量对象是实际存在的对象,能够被解析器访问到。不严谨的说,为了访问执行环境,就创造了变量对象这个东东,通过变量对象就可以访问执行环境中的所有变量和函数了,它俩其实是一个东西,只不过一个是虚的,一个是真实存在的。

当一个执行环境中的所有代码执行完毕后,该执行环境被销毁,保存在其中的所有变量和函数定义也随之销毁。其实是这个执行环境对应的变量对象被销毁。发现很多地方都把执行环境和变量对象混谈,大家似乎很少提到变量对象,都用 “执行环境” 这四个字替代了,把执行环境说成了一个对象,没办法,谁让说的是一个东西呢。

变量对象补充

当JS执行流进入函数时,JavaScript引擎在内部创建一个对象,叫做Variable Object。
对应函数的每一个参数,在Variable Object上添加一个属性,属性的名字、值与参数的名字、值相同。
函数中每声明一个变量,也会在Variable Object上添加一个属性,名字就是变量名,因此为变量赋值就是给Variable Object对应的属性赋值。
在函数中访问参数或者局部变量时,就是在variable Object上搜索相应的属性,返回其值。
一般情况下Variable Object是一个内部对象,JS代码中无法直接访问。

作用域链
  

作用域链的用途:保证对执行环境有权访问的所有变量和函数的 有序 访问。
当代码在一个执行环境中执行时,就会创建变量对象的一个作用域链。

我的理解是,作用域链是由一个一个变量对象链接起来的一个链,整个作用域链构成了当前执行环境中变量和函数可访问的范围,即作用域。由于变量对象是按一定顺序链接在一起的,所以就达到了对所有可访问变量、函数有序访问的效果。那么它们是按怎样的顺序链接成作用域链的呢?这就要说到最后一个概念——活动对象。

活动对象
  

当函数运行时就会为其创建一个活动对象,其中包含形参和函数特殊的arguments对象。活动对象之后会做为函数执行环境的变量对象来使用。

回到之前的问题,作用域链中的变量对象是如何排序的呢?
作用域链的前端,始终都是当前执行的代码所在环境的变量对象。但如果这个环境是函数,则将其活动对象作为变量对象,放在其作用域链的前端。作用域链中的下一个变量对象来自包含环境,而再下一个变量对象来自下一个包含环境……这样一直延续到全局执行环境。全局执行环境的变量对象始终是作用域链中的最后一个变量对象。

为什么执行环境是函数会有这样特殊的规定呢?
《JS权威指南》中有一句很精辟的描述:

  

JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。

按照之前所说,在函数定义的作用域里,当前执行环境的作用域链上是没有该函数的活动对象的,为了访问函数内部的变量、函数,所以要将其活动对象插在当前作用域链的前端。

2.它们之间的关系

每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在执行后,栈将其环境弹出,把控制权返回给之前的执行环境。

综上,每个函数对应一个执行环境,每个执行环境对应一个变量对象,而多个变量对象构成了作用域链,如果当前执行环境是函数,那么其活动对象在作用域链的前端。

3.其他补充

JS的语法风格和 C/C++ 类似, 但作用域的实现却和 C/C++ 不同,并非用“堆栈”方式,而是使用列表,具体过程如下(ECMA262中所述):

任何执行上下文时刻的作用域, 都是由作用域链(scope chain)来实现.
在执行func的定义语句的时候, 会创建一个这个函数对象的[[scope]]属性(内部属性,只有JS引擎可以访问),并将这个[[scope]]属性链接到定义它的作用域链上。
在调用func的时候, 会创建一个活动对象,然后将调用参数赋值给形参数,对于缺少的调用参数,赋值为undefined。然后将这个活动对象做为scope chain的最前端, 并将func的[[scope]]属性所指向的,定义func时候的顶级活动对象,加入到scope chain.

参考资料

《js高级程序设计》 P73 4.2 执行环境及作用域
鸟哥:JavaScript作用域原理
JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91506.html

相关文章

  • 学习笔记JavaScript 闭包是怎么通过作用域链霸占更多内存的?

    摘要:闭包是怎么通过作用域链霸占更多内存的本文是作者学习高级程序设计第一小节的一点个人理解,详细教程请参考原教材。函数执行过程创建了一个函数的活动对象,作用域链的最前端指向这个对象。函数执行完毕返回值后执行环境作用域链和活动对象一并销毁。 JavaScript 闭包是怎么通过作用域链霸占更多内存的? 本文是作者学习《JavaScript 高级程序设计》7.2第一小节的一点个人理解,详细教程请...

    HmyBmny 评论0 收藏0
  • javascript学习笔记(1)

    摘要:关于作用域实现的描述任何执行上下文时刻的作用域,都是由作用域链来实现的。在一个函数被定义的时候,会将它此时的作用域链链接到这个函数对象的属性。参考资料鸟哥作用域原理理解作用域和作用域链阮一峰老师微博上的关于作用域的一道题 javascript作用域原理学习   在每次调用一个函数的时候,就会进入一个函数内的作用域,当从函数返回 以后,就会返回调用前的作用域。   ECMA262关于作...

    Chaz 评论0 收藏0
  • [学习笔记] JavaScript 闭包

    摘要:但是,必须强调,闭包是一个运行期概念。通过原型链可以实现继承,而与闭包相关的就是作用域链。常理来说,一个函数执行完毕,其执行环境的作用域链会被销毁。所以此时,的作用域链虽然销毁了,但是其活动对象仍在内存中。 学习Javascript闭包(Closure)javascript的闭包JavaScript 闭包深入理解(closure)理解 Javascript 的闭包JavaScript ...

    sunsmell 评论0 收藏0
  • JavaScript 作用学习笔记

    摘要:每一个运行期上下文都和一个作用域链关联。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在处于第二个作用域链对象中,因此访问代价更高了。在代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中。 参考: Javascript作用域原理 理解 JavaScript 作用域和作用域链 JavaScript 作用域 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数...

    赵连江 评论0 收藏0
  • JavaScript作用学习笔记

    摘要:我们再来看一下第一段代码小红小黑脚本出错脚本出错在这段代码中变量与函数,都拥有局部作用域。作用域链的最前端,始终都是当前执行代码所在的作用域的变量对象。 个人博客原址 无论什么语言中,作用域都是一个十分重要的概念,在JavaScript中也不例外,作用域定义了变量或者函数有权访问的范围,决定了它们各自的行为。要理解JavaScript中的作用域首先就要知道:在let出现之前,JS中变...

    jerryloveemily 评论0 收藏0

发表评论

0条评论

?xiaoxiao,

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<