首先讲一讲执行环境:
执行环境按照字面上来理解就是指目前代码执行所在的环境。
当JavaScript代码执行的时候,会进入不同的执行上下文,这些执行上下文会构成了一个执行上下文栈(Execution context stack,ECS)。
执行环境的分类
- 全局执行环境:
在浏览器中,其指window对象
,是JS代码开始运行时的默认环境。
全局执行环境的变量对象始终都是作用域链中的最后一个对象。
- 函数执行环境:
当某个函数被调用时,会先创建一个执行环境及相应的作用域链。然后使用arguments和其他命名参数的值来初始化执行环境的变量对象。当该函数执行完之后会将该函数的执行环境弹出栈。
注:上述分类解释也表明了js中只有两种作用域,即全局作用域和函数局部作用域,不存在块作用域。
执行环境的组成
一个执行环境包括变量对象,内部[[Scope]]属性,this指针这三个属性
(注意:图片来源于简书——理解js的执行环境)
- 变量对象(Variable object,VO): 变量对象即包含变量的对象,使用包含的变量来初始化变量对象,除了我们无法访问它外,和普通对象没什么区别(全局执行环境的变量对象就是window,我们可以访问window)。
-
[[Scope]]
属性:作用域即变量对象,作用域链是一个由变量对象组成的带头结点的单向链表,其主要作用就是用来进行变量查找。而[[Scope]]属性是一个指向这个链表头节点的指针。 - this: 指向一个环境对象,注意是一个对象,而且是一个普通对象,而不是一个执行环境。
完全是小白的可能不太清楚作用域链,然后补充一下作用域链:
在前面提到过,当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对该执行环境有权访问的所有变量和函数的有序访问。
作用域链包含了执行环境栈中的每个执行环境对应的变量对象。
在访问一个变量时,先在作用域链表上的第一个变量对象上查找。如果没有找到则继续在作用域链上的第二个变量对象上查找。直至搜索到全局执行环境的变量对象即Window对象。
那么再看看闭包的特殊性访问,
function returnfunc (propertyName) {
return function (obj) { //-----这行定义并返回了一个闭包,也被称之为一个匿名函数
return obj[propertyName]; //这里用方括号法访问属性,因为属性是变量(returnfunc()函数的参数)
};
} var savefunc = returnfunc("name"); //调用returnfunc()
var result = savefunc({name:"Picasso"});//调用savefunc()
alert(result); //返回字符串“Picasso” 作者:闭家锁
链接:https://www.zhihu.com/question/36393048/answer/71879330
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(注意:以下图片来源于知乎——理解活动对象和变量对象的区别)
以上代码的最开始的作用域链和执行环境:
我们先开始调用returnfunc()函数,马上会创建一个包含returnfunc()变量对象的行环境,作用域链开始变化,如下图:
图的白色虚线表示执行程序产生的效果,它可能表示的是返回一个结果、复制某种值、产生一个新物体、建立某种联系等。
随后returnfunc()函数会返回它内部的匿名函数,当匿名函数被返回并执行后,整个作用域链和执行环境又发生了变化:
可以看到,returnfunc函数在返回内部函数之后其执行环境就不存在了,但是其活动对象仍然在内存中,并没有被销毁;按照前面讲过的一句话“作用域链包含了执行环境栈中的每个执行环境对应的变量对象。”来看,当returnfunc的执行环境不存在之后,其所包含的活动对象是应该会被销毁。事实上,直到匿名函数被销毁,returnfunc的活动对象才会被销毁。这是闭包的特殊性,在匿名函数从returnfunc中被返回后,它的作用域链被初始化为包含returnfunc函数的活动对象和全局变量对象,即使returnfunc函数的执行环境被弹出栈,因为匿名函数的作用域链仍然在引用returnfunc函数的活动对象,returnfunc函数的活动对象仍然在内存中,这样匿名函数就可以访问returnfunc函数中定义的所有变量。
以上内容学习自: