前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java作用域-javaScript预编译、作用域,作用域链详解

java作用域-javaScript预编译、作用域,作用域链详解

作者头像
囍楽云
发布2022-12-29 11:21:30
1.4K0
发布2022-12-29 11:21:30
举报
文章被收录于专栏:囍楽云博客

ES5中只分为全局作用域和函数作用域java作用域,也就是说for,if,while等语句是不会创建作用域的。ES6(let,const)除外。

  几个概念

  执行环境:也称为执行期上下文,当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,执行上下文被销毁。它定义了变量或函数有权访问的其他数据,决定了它们的各自行为。

  执行环境分为全局执行环境和函数执行环境(这个名字我自己取的),其中在浏览器中全局执行环境是一个对象。

  活动对象(AO)与变量对象(OV): 活动对象也就是前面说的执行期上下文的内部对象,每个函数执行的时候都会创建这样一个活动对象,当函数函数还未执行完毕的时候,又进入到其他的作用域中,那么这个活动对象就变为变量对象(OV),比如说在一个函数里面执行另一个函数,另一个函数会创建一个活动对象,而当前的变为变量对象。当另一个函数执行完毕,它的执行上下文被销毁,返回上一个函数,那么上一个函数的变量对象又变为活动对象。可以理解为当前执行环境的执行期上下文的内部对象称为活动对象,否者称为变量对象。另外变量对象和活动对象都存储了本级环境中所定义的变量和函数,函数的话还包括参数

预编译:在进入全局环境和函数环境之前就会进行

  预编译步骤:

  1、创建AO对象(活动对象 或者说执行期上下文) 如果是全局环境就叫做window对象或者Go对象

  2、查找形参和变量声明,值为

  3、将实参和形参相统一,值为实参里面的值值为实参里面的值

  4、找函数声明,属性值为声明时候的属性值

  [scope]:每个函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供引擎存取,[scope]就是其中一个。[scope]指的就是我们所说的作用域,其中存储了前面说的执行期上下文的集合,这个属性是在函数被定义的时候就创建

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

  寻找变量的过程就是从变量作用域链开始查找的,如果在当前的变量作用域没找到,那么就去上一个变量作用域里面去早

  作用域链创建的过程:

  预编译

  先来理解一下预编译,看例子:

   var a = 10

代码语言:javascript
复制
            b = 20
            function test (a, b) {
                console.log(a, b, test1, d)
                var a = 10
                console.log(a)
                if (b) {
                    var c = 20
                }
                console.log(a, b, test1, d)
                function test1 () {
                    
                }
                var d = function () {
                    
                }
                console.log(a, b, test1, d)
            }

  第一步 全局环境下预编译

  进入全局环境并在执行全局环境代码之前进行预编译,跟着我前面说步骤

  1、生成GO对象

  GO: {}

  2、查找形参和变量声明(这里不是函数,因此没有查找形参这一步),值为

  GO: { a: } // 注意这里因为b是没有声明的所以不会被放到GO这个活动对象里面

  3、将实参和形参相统一,值为实参里面的值值为实参里面的值(在全局环境依然没有这一步)

  4、找函数声明,属性值为声明时候的属性值

  GO: { a: , test: () {} }

  第二步 全局环境下执行代码

  先执行 a = 10 ,注意因为声明的步骤我们已经在预编译的时候执行过了,所以这里就只是赋值操作,此时的GO对象是

  GO: { a: 10, test: () {} }

  再执行 b = 10 ,注意这里由于没有声明预编译的时候没有被放到GO对象里面去,因此在这行代码之前访问这个对象会报错,当执行到这个代码的时候会被放到GO对象里面去,此时的GO对象为

  GO: { a: 10, test: () {} , b: 10}

  第三步 函数环境下预编译

  现在该执行test函数了,进入到一个新的执行环境 ,在执行函数代码之前会进行预编译

  1、生成AO对象

  AO: {}

  2、查找形参和变量声明, 值为

  AO: { a: , b: , c: , d: }

  3、将实参和形参相统一,值为实参里面的值

  AO: { a: 1, b: , c: , d: }

  4、找函数声明,属性值为声明时候的属性值

  AO: { a: 1, b: , c: , d: , test1: () {} }

  第四步 函数环境下执行代码

  依旧和在全局环境下执行代码一样,执行赋值语句,下面就说当执行到某个.log() 的时候它的AO对象是什么

  第一个.log()的时候AO对象为

  AO: { a: 1, b: , c: , d: , test1: () {} }

  第二个.log()的时候AO对象为

  AO: { a: 10, b: , c: , d: , test1: () {} }

  第三个.log()的时候AO对象为

  AO: { a: 10, b: , c: , d: , test1: () {} }

  第三个.log()的时候AO对象为

  AO: { a: 10, b: , c: , d: () {}, f: () {} }

  注意: 在预编译的时候,像let和const这种块级作用域的,如果放到if语句里面,是不会被添加到AO或者GO对象里面的,像上面的例子中if里面如果换成let或者const的话,预编译阶段是不会被添加进去的

  作用域链

  看例子:

  第一步:a函数被定义,创建[scope]

  上面函数被定义的时候,[scope]会把当前函数所在的上下文放入这个对象中。需要注意的时候,函数存储被定义时的上下文的时候java作用域,只是存储的是一个引用,而不是副本,正是因为这样,才能形成作用域链,当函数在本函数的AO对象找不到的时候,就沿着本函数的[scope]存储的上一个作用域的变量对象的引用到上一个作用域里面去早。

  第二步,执行函数

  执行函数的时候,会先发生预编译,然后会把AO对象放入到这个[scope]中

  第三步:函数b在函数中是被定义了的,在执行预编译的时候,会为它创建一个[scope]属性,里面存储了b函数当前所在的上下文,也就是a的[scope]。

  第四步,执行b函数

  和a函数执行一样,依然创建一个AO对象放入

  作用域链就是这样形成的,每个函数都有一个[scope]里面储存了运行期上下文的集合。寻寻找变量的过程,就是沿着作用域链从上到下寻找,找不到就在最上面一层定义,也就是在global object里面成为全局变量,也就是我们常说的变量不定义就使用会成为全局变量。

  ---------------------

本文共 1709 个字数,平均阅读时长 ≈ 5分钟

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档