js作用域和作用域链
作用域:在js预编译时、代码执行之前对全局或局部的变量进行收集,存放在一指定内存中scope,执行时对scope进行访问,scope 就是全局或局部的作用域。
如:
1 function test() { 2 var a = 123; 3 } 4 5 test();
在test() 执行之前对test 进行预编译 生成test 的 scope(AO Activetion Object)。收集test 函数中的变量存放在scope中。
此时test 的scope 为:
{
a:undefined
}
在test 函数执行时 对 scope 中的 a 变量进行赋值,a = 123;
实际上在全局环境进行预编译时也会产生一个作用域,在test 被定义时会在test 函数上定义一个作用域数组([[Scopes]])。并将全局作用域放入test的作用域数组中。
如:
1 var a = 456; 2 3 function test() { 4 var b = 123; 5 console.log(b); 6 console.log(a); 7 } 8 9 test();
在全局环境进行预编译时,会生成全局作用域Global
Global {
a: undefined,
test: undefined
}
此时test 函数的作用域数组中就已经包含了Global
test.[[Scopes]] = [Global] // 实际上[[Scopes]] 是不可访问的
全局环境执行完后Global变成
Global{
a: 456,
test: function test() {...}
}
在test 函数执行前会对test函数进行预编译,生成test 函数的scope并放入到test 函数的作用域数组的第一位。
scope {
b: undefined
}
test.[[Scopes]] = [scope,Global] // 实际上[[Scopes]] 是不可访问的
在test 代码执行b= 123时对作用域scope 中的b进行赋值
scope {
b: 123
}
在test 函数中打印a 时会沿着test 作用域数组中的顺序进行查找,首先会查找test 的scope 中是否有a,如果有者直接使用,如果没有沿着[[Scopes]] 继续查找,在Global 中进行查找,所以打印的是Global 中的a的值--456。
如果沿着[[Scopes]] 的顺序找到Global 一直找不到a,就会报错 a is not defined。
由于在访问变量时会沿着[[Scopes]] 查找,这种查找机制类似链条结构,所以称[[Scopes]] 为作用域链。