作用域的创建
作用域的创建时机解析器在解析的过程中在生成AST的时候,同时构建其作用域。
查看其作用域链
如下代码:在控制台直接执行
var testSelf = {};
function A(){
var a = "1";
var b = testSelf;
var c = "3";
function B(){//闭包1
var c = "1"
function C(){//闭包2
var d = "1"
console.log(a,b,c,d)
}
debugger;
C()
}
debugger;
return B
}
debugger
var B = A()
B()
testSelf.a="aaa";
B()
A的scope链
B的scope链
C的scope链g
根据上图即代码我们可以得到一下结论:
- 方法的Scope链在该方法创建后以及生成了
[[scopes]]
,而这个scopes是数组形式的链式。 - 当个方法的内部使用的
变量未在该方法中声明
。则会在其创建的时所在的scope中查找,如果找到了,就将该scope加入[[scopes]]
,并值将该变量加入scope。 - 当上层scope没有找到,则依次向上查找并重复步骤2。
- 子scopes共享父的scopes
123比较容易理解,关于4.
我们观察B:
在B中并没有使用到变量a
和b
,
如同C中没有使用变量e
,
但是C中使用了a和b
,根据123,将会在C的scopes
中加入A的作用域里的a和b。
但是我们发现B的scopes
中也有A的作用域里的a和b。
所以我觉得可以理解为,B的scopes
是C的父scopes
,当在解析C的时候,发现a和b变量不存在,则将会在scopes
中添加Closure(闭包)A。从而B也会有Closure(闭包)A。
小结:
scope链生成是在代码解析的时候生成的,解析器通过识别到使用的变量未在该方法中声明
,则会在该方法的声明创建
的地方的作用域查找,依次向上直到全局scope。
同时scope中存储的数据是可修改的,具体参考demojs打印结果。
附加题
var x = 10
function fn() {
console.log(x)
}
function show(f) {
var x = 20;
(function() {
f()
})()
}
show(fn)
我们尝试解答这个题:
首先回顾,闭包是在js解析的时候生成的。
- 当解析器解析fn的时候,发现console.log(x)里面的x没有在fn中定义。
- 那么将会在fn声明所在的scope(即全局scope)中查找x,当找到x后,将添加到的
scopes
里面,且x的值为10。 - 接下来show将fn作为参数传递进去了。
- 尽管show内部声明了x,并且有个闭包的匿名函数,但是f执行时,这个f在解析过程中已经形成了闭包,即第2步。那么执行时,直接在Scopes中找到了x的值为10。随即打印了10
延深思考:如果我删除
var x = 10
。执行会出现什么结果?
网友评论