-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript深入之作用域链 #6
Comments
大神你好,问你一个问题,checkscope函数被创建时,保存到[[scope]]的作用域链和checkscope执行前的准备工作中,复制函数[[scope]]属性创建的作用域链有什么不同么?为什么会有两个作用域链? |
checkscope函数创建的时候,保存的是根据词法所生成的作用域链,checkscope执行的时候,会复制这个作用域链,作为自己作用域链的初始化,然后根据环境生成变量对象,然后将这个变量对象,添加到这个复制的作用域链,这才完整的构建了自己的作用域链。至于为什么会有两个作用域链,是因为在函数创建的时候并不能确定最终的作用域的样子,为什么会采用复制的方式而不是直接修改呢?应该是因为函数会被调用很多次吧。 |
@menglingfei 在js中复制有分两种,比如说基本类型的复制,就是直接的赋值,两个变量以后互不影响。而引用类型的复制,是指两个变量同时指向一个对象。我觉得这里应该说的是后者吧。 |
函数有一个内部属性 [[scope]],当函数创建的时候,就会保存所有父变量对象到其中,想问一下变量对象是创建上下文的时候才有的吧 |
@yh284914425 以你举的例子为例的话,当 foo 函数的执行上下文初始化的时候,才会创建 bar 函数。 |
@mqyqingfeng 好吧,当函数创建的时候,就会保存所有父变量对象到其中这个过程感觉不是很明白,能说的清楚一些吗,以我举的例子为例的话,当 foo 函数的执行上下文初始化的时候,才会创建 bar 函数,bar函数保存foo的变量对象,那更外层的变量对象呢 |
@yh284914425 更外层就是全局对象呐~ 所以bar 的 [[scope]] 属性值就是 [ fooContext.AO, globalContext.VO]; |
@mqyqingfeng 我知道呀,就是想问一下,[[scope]] 属性值是怎么把globalContext.VO保存进去的,有点转牛角尖,嘿嘿 |
@yh284914425 根据词法作用域的规则找出最外层的就是 globalContext ,然后……然后就保存呐……具体是怎么保存进去的,这个应该是实现层面上的吧 |
听君一席话 胜读十本书 |
@sandGuard 感谢,这真是对我莫大的肯定~ |
《JS高程》讲到,每一个函数都有自己的执行环境。好像这里没有讲到额 |
@xx19941215 函数都有自己的执行环境,其实就是讲函数执行的时候,会创建函数执行上下文,这个在《JavaScript深入之执行上下文栈》和 《JavaScript深入之变量对象》都有讲到 |
@xx19941215 关于这张图,有几个疑问的地方?一个是默认存在了一个 window 的引用是指什么意思?一个是先创建的执行环境还是先创建的活动对象?一个是如果有闭包的话,AO是否会被释放?一个是执行环境的作用域链对象的 AO 引用出栈,为什么需要出栈呢? |
@xx19941215 想听听你的理解哈~ |
受益! |
@mqyqingfeng 1.函数作用域链在初始化的时候顶端是window。2.先创建活动对象,然后创建执行环境。关于后两个问题,我之前写了一篇文章介绍了我的理解:图解JS闭包 这是我的理解,还请大神看看对不对啊 |
@suoz知乎见过你 |
@yh284914425 我也有过你的疑问,不过看了作者的答复明白了,在源代码中当你定义(书写)一个函数的时候(并未调用),js引擎也能根据你函数书写的位置,函数嵌套的位置,给你生成一个[[scope]],作为该函数的属性存在(这个属性属于函数的)。即使函数不调用,所以说基于词法作用域(静态作用域)。 然后进入函数执行阶段,生成执行上下文,执行上下文你可以宏观的看成一个对象,(包含vo,scope,this),此时,执行上下文里的scope和之前属于函数的那个[[scope]]不是同一个,执行上下文里的scope,是在之前函数的[[scope]]的基础上,又新增一个当前的AO对象构成的。 函数定义时候的[[scope]]和函数执行时候的scope,前者作为函数的属性,后者作为函数执行上下文的属性。 |
@keyiran 恩恩 谢谢 我也懂了 |
捋一捋中第4步中 这会是不是还是VO 当进行到函数执行修改值时候才变成AO? |
4.第二步:用 arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明 这时候应该不叫AO吧,应该是VO,当函数执行的时候才是AO。 |
各位大佬我想问下为什么 [[Scopes]] l里面的Closure (outerFunction) {hidden: 4}。会为4 的按照我的 理解 inc 函数会 查找当前活动对象 在查找outerFunction 变脸对象在查找 Global 变量对象 自身活动对象没 有hidden 变量然后查找outerFunction 但是 打印的话他的值是4 为什么 myClosure.inc() 的返回结果是正确的呢。对不上了呀 |
var myClosure = (function outerFunction() {
}()); |
AO:activation object,活动对象;VO:variable object,变量对象。 |
@keyiran 我想问下作者说的啥叫AO,啥叫VO,对这些抽象概念一脸懵逼,活动对象是干嘛的,变量对象又是干嘛的 |
@yyqxjwxy 这些概念可以看本篇的上一篇文章 《JavaScript深入之变量对象》, 属于 JavaScript ES5 规范里的概念 |
你好,请教一下~
开始执行时, 请问innerScope具体是什么?按照理解,scope2查找到checkscope这层时,实际上是到checkscopeAO找 |
因为你 return 3 之后执行了 hidden++ |
我是曾满英,您的邮件我已收到!
|
@mqyqingfeng 捋一捋里面的第一步是不是应该是 globalContext.AO? |
|
我是曾满英,您的邮件我已收到!
|
这是来自QQ邮箱的假期自动回复邮件。
您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
|
在《javascript忍者秘籍》里说到的词法环境[[Environment]]与这里的[[Scope]]和作用域链有什么关系?感觉书里讲的[[Environment]]很像作用域链 |
我是曾满英,您的邮件我已收到!
|
大佬 可以举例说明下 什么情况下会遇到函数预解析时的:《 第二次复制函数[[scope]]属性创建作用域链》的逻辑吗 这块还是没有理解透彻 |
我是曾满英,您的邮件我已收到!
|
函数执行上下文中的对象是不是就等于这个函数的作用域 |
我是曾满英,您的邮件我已收到!
|
这里应该想说的是完整的构建了自己的执行上下文吧?而不是作用域链 |
我是曾满英,您的邮件我已收到!
|
这是来自QQ邮箱的假期自动回复邮件。你好,你的邮件我收到,谢谢。
|
前言
在《JavaScript深入之执行上下文栈》中讲到,当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。
对于每个执行上下文,都有三个重要属性:
今天重点讲讲作用域链。
作用域链
在《JavaScript深入之变量对象》中讲到,当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
下面,让我们以一个函数的创建和激活两个时期来讲解作用域链是如何创建和变化的。
函数创建
在《JavaScript深入之词法作用域和动态作用域》中讲到,函数的作用域在函数定义的时候就决定了。
这是因为函数有一个内部属性 [[scope]],当函数创建的时候,就会保存所有父变量对象到其中,你可以理解 [[scope]] 就是所有父变量对象的层级链,但是注意:[[scope]] 并不代表完整的作用域链!
举个例子:
函数创建时,各自的[[scope]]为:
函数激活
当函数激活时,进入函数上下文,创建 VO/AO 后,就会将活动对象添加到作用链的前端。
这时候执行上下文的作用域链,我们命名为 Scope:
至此,作用域链创建完毕。
捋一捋
以下面的例子为例,结合着之前讲的变量对象和执行上下文栈,我们来总结一下函数执行上下文中作用域链和变量对象的创建过程:
执行过程如下:
1.checkscope 函数被创建,保存作用域链到 内部属性[[scope]]
2.执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈
3.checkscope 函数并不立刻执行,开始做准备工作,第一步:复制函数[[scope]]属性创建作用域链
4.第二步:用 arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明
5.第三步:将活动对象压入 checkscope 作用域链顶端
6.准备工作做完,开始执行函数,随着函数的执行,修改 AO 的属性值
7.查找到 scope2 的值,返回后函数执行完毕,函数上下文从执行上下文栈中弹出
下一篇文章
《JavaScript深入之从ECMAScript规范解读this》
本文相关链接
《JavaScript深入之词法作用域和动态作用域》
《JavaScript深入之执行上下文栈》
《JavaScript深入之变量对象》
深入系列
JavaScript深入系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: