参考文章
「http://suo.im/5L3v4k」
「http://suo.im/60LVo7」
在介绍JavaScript作用域链之前,先介绍一下与之相关的一些概念
1 词法作用域
众所周知,作用域即只变量的可见性和可访问性,JavaScript采用的作用域是词法作用域。
❝词法作用域就是由你写代码时将变量和块作用域写在哪里来决定的。
❞
例:
let number = 42;
function printNumber() {
console.log(number);
}
function test() {
let number = 54;
printNumber();
}
test(); // 42
可以看到,打印的值是42,而不是54,决定其结果的原因是由printNumber书写的位置决定的。
2 作用域的类型
2.1全局作用域
在全局作用域中的变量,可以在程序中任意位置访问
例:
var say = 'Hello';
function greet() {
console.log(say);
}
greet(); // Hello
2.2局部作用域或函数作用域
声明在函数内部的变量只能被函数内部所访问,函数外部无权访问
例:
function greet() {
var say = 'Hello';
console.log(say);
}
greet();// Hello
console.log(say); // Uncaught ReferenceError: say is not defined
2.3块级作用域
ES6引入了let,const变量(和var 不同, var变量没有块级作用域),他们的作用域是最近的一对花括号。他们不能被花括号之外被访问。
例:
{
let say = 'Hello';
var haha = 'hahaha';
console.log(say); // Hello
}
console.log(haha); // hahaha
console.log(say); // Uncaught ReferenceError: say is not defined
3 作用域链
❝当前js引擎在当前作用域中查找不到要引用的变量后,js引擎会在其外部作用域中查找,直到找到为止。 如果找不到,则会报一个引用错误
❞
例:
let a = 'a';
function bar() {
let b = 'b';
console.log(b); // b
console.log(a); // a
c = 'c';
console.log(c); // c
}
bar();
- 当初始化bar函数时,会为bar函数维护一个私有属性[[scope]],会使用当前环境的作用域链初始化,这里就是bar.[[scope]] = global scope;
- 当bar函数执行的时候,构建bar的作用域链,bar.scopeChain = [test.[[scope]]]
- 初始化bar的活动对象,即bar.activationObject = [arguments], 然后活动对象被当作变量对象进行初始化,即bar.variableObject = bar.activationObject.concat[b];
- bar函数的变量对象,被压入其作用域链的顶端,即bar.scopeChain = [test.variableObject, test.[[scope]]];