深刻执行环境、做用域链和闭包

执行环境对象和做用域链

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

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

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

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

<!-- more -->spa

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

这种依赖关系是经过执行环境对象中的一个特殊的属性,引用建立该函数时的所对应的执行环境对象来实现的。因为这种引用关系能够造成一条引用链,一个函数执行时,引擎对变量的解析就是经过对执行环境对象引用链的遍从来解析肯定的。对象

这个引用链有个高大上的、常常听到的名字——做用域链图片

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

函数对象的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界的敏感话题,故而,若有纰漏,欢迎吐槽。

相关文章
相关标签/搜索