资讯专栏INFORMATION COLUMN

深入执行环境、作用域链和闭包

gyl_coder / 1614人阅读

摘要:执行环境对象和作用域链执行环境,又称执行上下文,是指一个函数在执行的时候所能直接引用的变量等的一个集合。为了解释作用域链的机制,我们再来引入一个属性的概念。而函数的执行环境对象作用域链保存了函数在执行时能解析到的变量。

执行环境对象和作用域链

执行环境,又称执行上下文,是指一个函数在执行的时候所能直接引用的变量等的一个集合。

在JavaScript引擎中,执行环境是由一类特殊的对象——执行环境对象——来实现的。由于一个函数执行的时候可能对应不同的上下文,所以每次函数执行的时候都会由引擎为该函数创建一个独一无二的执行环境对象。函数执行完毕时,由垃圾回收(GC)机制来决定是否将该执行环境对象回收。

为了区别执行环境和执行上下文,我将下文中的执行环境称作“执行上下文”。

注意:全局环境(全局作用域所在的环境)虽然不是一个函数,但是其中的代码执行时,也会有一个相应的执行环境对象与之对应。

不同执行环境中的变量是存在依赖关系的。
例如:一个全局执行环境下创建的函数在执行时,其执行环境需要知道全局执行环境中的变量:window、document、以及其他声明的全局变量(注意:未声明的变量作为window对象的属性,和声明过的全局变量有略微的不同之处)。熟悉函数作用域概念的同学,不难理解这种依赖关系。

这种依赖关系是通过执行环境对象中的一个特殊的属性,引用创建该函数时的所对应的执行环境对象来实现的。由于这种引用关系可以形成一条引用链,一个函数执行时,引擎对变量的解析就是通过对执行环境对象引用链的遍历来解析确定的。

这个引用链有个高大上的、经常听到的名字——作用域链

为了解释作用域链的机制,我们再来引入一个scope属性的概念。

函数对象的scope属性

我们知道,JavaScript的函数是Function构造函数的实例,本质是一类特殊的对象。某一个对象只可能在某一个独一无二的执行上下文中创建,但是函数对象会在不同的执行上下文中执行。

函数对象有很多属性,其中一个就是只有在JavaScript引擎中可见的scope属性。这个scope属性指向创建该函数时对应的执行环境对象

该函数执行的时候,使用函数内的函数作用域变量创建一个新执行环境对象,并且引用scope属性指向的执行环境对象。这个执行环境对象和该函数的执行上下文相对应。

这三者对应关系如下图所示:

但是scope属性依然引用创建这个函数的执行环境对象,原因跟上面的解释是一样的:

一个函数只能在某个特定的执行上下文中创建,但是会在不同的执行上下文中执行。

函数执行时变量解析

从作用域链的角度解释:首先从该函数所对应的执行环境对象中搜索该变量,如果没有则沿着作用域链继续搜索,直到找到为止。然后将数据取出或者存储。

这里有一个优化问题:不要在代码中过多的引用作用域链中离头结点(即当前执行环境对象)较远的结点中的变量。解决办法:将这个非头结点中的变量赋值给函数局部变量变为头结点中的变量,这样就不需要每次都去搜索作用域链了。

题外话:this

this可以认为是一个特殊的变量,代表函数的调用者。每一个执行环境对象中都有一个this,但是变量搜索时,只需搜索当前的执行环境对象就可以找到这个变量。当需要找到作用域链中非头结点的this时,需要将其保存为其他特定的能被引用到的局部变量来处理。

闭包

现在我们可以看看小宇宙中的黑魔法了。

函数B在函数A中被返回。那么创建函数B的执行环境对象就是函数A对应的执行环境对象。那么函数B的scope对象会保存函数A的执行环境对象。

而函数A的执行环境对象作用域链保存了函数A在执行时能解析到的变量。所以函数B中就能通过其scope属性访问函数A的执行环境中的变量。

假设函数B的调用者无法访问函数A中的变量,那么它只能通过函数B的行为来获得函数A中的变量状态。

此时函数B由于其scope属性保存了函数A的执行环境对象的作用域链,从而形成一个闭包

结束

一点微小的见解。

本文涉及JavaScript界的敏感话题,故而,如有纰漏,欢迎吐槽。

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

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

相关文章

  • 【进阶2-1期】深入浅出图解作用链和闭包

    摘要:本期推荐文章从作用域链谈闭包,由于微信不能访问外链,点击阅读原文就可以啦。推荐理由这是一篇译文,深入浅出图解作用域链,一步步深入介绍闭包。作用域链的顶端是全局对象,在全局环境中定义的变量就会绑定到全局对象中。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周开始前端进阶的第二期,本周的主题是作用域闭包,今天是第6天。 本...

    levius 评论0 收藏0
  • javascript系列--javascript深入浅出图解作用链和闭包

    摘要:变量对象也是有父作用域的。作用域链的顶端是全局对象。当函数被调用的时候,作用域链就会包含多个作用域对象。当函数要访问时,没有找到,于是沿着作用域链向上查找,在的作用域找到了对应的标示符,就会修改的值。 一、概要 对于闭包的定义(红宝书P178):闭包就是指有权访问另外一个函数的作用域中的变量的函数。 关键点: 1、闭包是一个函数 2、能够访问另外一个函数作用域中的变量 二、闭包特性 对...

    Jensen 评论0 收藏0
  • 【进阶2-3期】JavaScript深入闭包面试题解

    摘要:闭包面试题解由于作用域链机制的影响,闭包只能取得内部函数的最后一个值,这引起的一个副作用就是如果内部函数在一个循环中,那么变量的值始终为最后一个值。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第8天。 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了...

    alanoddsoff 评论0 收藏0
  • 【进阶2-2期】JavaScript深入之从作用域链理解闭包

    摘要:使用上一篇文章的例子来说明下自由变量进阶期深入浅出图解作用域链和闭包访问外部的今天是今天是其中既不是参数,也不是局部变量,所以是自由变量。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第7天。 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计...

    simpleapples 评论0 收藏0
  • JS基础知识:变量对象、作用链和闭包

    摘要:前言这段时间一直在消化作用域链和闭包的相关知识。而作用域链则是这套规则这套规则的具体运行。是变量对象的缩写那这样放有什么好处呢我们知道作用域链保证了当前执行环境对符合访问权限的变量和函数的有序访问。 前言:这段时间一直在消化作用域链和闭包的相关知识。之前看《JS高程》和一些技术博客,对于这些概念的论述多多少少不太清楚或者不太完整,包括一些大神的技术文章。这也给我的学习上造成了一些困惑,...

    Keven 评论0 收藏0

发表评论

0条评论

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