JS作用域、作用域链
运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数待执行时的环境,函数每次执行时对应的函数上下文都是独一无二的,所以多次调用一个函数会调至创建多个执行上下文,当函数执行完毕时,它所产生的执行上下文被销毁。
查找变量:在那个函数中查找变量就从这个函数的作用域链的顶端一次向下查找。
作用域:
[[scope]]:每个JavaScript 函数都是一个对象,对象中有些属性我们可以访问,有些不可以访问,这些属性仅供js引擎存取,[[scope]]
就是其中一个。
[[scope]]指的是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链:
[[scope]] 中所存储的执行时期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
function a(){
function b() {
var b =234;
}
var a =123;
b();
}
var global =100;
a();
当a在定义时,它就有自己的属性和方法,那么它就具有a.name同理它也有a.scope:
此时a 中有个作用域链:
作用域链中存储执行上下文集合,但是此时没有达到集合的程度,此时作用域链中只一个值,即 第0位,此时第0位又指向Global Object 即GO
即当a 函数刚刚被定义的时候 a的scope中就存有东西了,存的就是只有一位的GO,此时GO中有this.window.document.a 函数和golb变量
然后函数a 开始执行那么势必会产生一个AO ,那么这个AO会存放在哪里,此时AO会放到作用域链的顶端。所以如图
由于AO放到了作用域的顶端所以 AO 在第0位GO在第一位
所以如果此时你要在a 函数中访问某个变量是,系统它在a的scope中找而且是从顶端依次向下找
然后紧接着由于a函数的执行,产生了b函数的定义,所以它也有自己的scope,它的环境是a 给的,
所以当b被创建时,发生如下过程:
但是此时它是定义并没有执行,所以此时的作用域链没有任何改变
当b 在执行时生成了自己的AO如下图:
它自己同样会生成作用域因为是它本身生成的,那么此时它会放到作用域链的顶端,所以0位置的是本身,1的位置是a,2的位置是GO,所以如果要在b中访问变量是自顶向下的。
如果此时b 被执行完了则断开自己的AO 那么此时a也就被执行完了,则a也断开自己的AO等待下一次被执行,重新生成AO。
例子一
function a(){
function b() {
function c() {
}
c();
}
b();
}
a();
作用域如下所示:
a defined a.[[scope]] --> 0:GO
a doing a.[[scope]] --> 0:aAO
--> 1:GO
b defined b.[[scope]] --> 0:aAO
--> 1:GO
b doing b.[[scope]] --> 0:bAO
--> 1:aAO
--> 2:GO
c defined c.[[scope]] --> 0:bAO
--> 1:aAO
--> 2:GO
c defined c.[[scope]] --> 0:cAO
--> 1:aAO
--> 2:aAO
--> 3:GO
function a() {
function b() {
var bbb = 234;
console.log(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo();
当a执行完也就是 执行return b 之后,应该将其执行上下文销毁如图:
则图中的红色线被销毁,由于此时a 被销毁了 那么a中的b也应该被销毁了,但是此时b 被返回出来了,所以b 依然存在所以b此时的执行期上下文如图
所以demo()的执行结果为 123。
例二
function a() {
var num = 100;
function b() {
num ++;
console.log(num);
}
return b;
}
var demo = a();
demo();
demo();
结果:
101
102