作用域链
JavaScript 的作用域链(Scope Chain)是理解变量查找机制的关键概念。作用域链是一个由多个变量对象组成的列表,用于解析标识符(如变量名、函数名等)。当一个变量被引用时,JavaScript 引擎会沿着作用域链从当前执行上下文的变量对象开始查找,如果找不到,则继续向上查找,直到找到该变量或到达全局作用域。
作用域链的工作原理
创建执行上下文:每当一个函数被调用时,JavaScript 引擎会创建一个新的执行上下文。每个执行上下文都有自己的变量对象(Variable Object, VO),这个对象包含了函数的参数、局部变量和内部函数。
建立作用域链:在创建执行上下文时,引擎还会建立一个作用域链。作用域链是一个包含多个变量对象的列表,这些变量对象按顺序排列,从当前执行上下文的变量对象开始,依次向上级作用域的变量对象延伸,最终到达全局变量对象。
变量查找:当一个变量被引用时,JavaScript 引擎会从当前执行上下文的变量对象开始查找。如果找不到,就继续沿着作用域链向上查找,直到找到该变量或到达全局作用域。如果在整个作用域链中都找不到该变量,就会抛出
ReferenceError。
示例
以下是一些示例,展示了作用域链的工作原理:
基本示例
javascript
var globalVar = "I'm global";
function outer() {
var outerVar = "I'm in the outer function";
function inner() {
var innerVar = "I'm in the inner function";
console.log(globalVar); // 全局变量
console.log(outerVar); // 外部函数的局部变量
console.log(innerVar); // 内部函数的局部变量
}
inner();
}
outer();在这个例子中:
inner函数的作用域链包括inner函数的变量对象、outer函数的变量对象和全局变量对象。- 当
inner函数中的console.log(globalVar)被执行时,globalVar会在inner的变量对象中查找,找不到则继续向上查找,最终在全局变量对象中找到。 - 同样地,
outerVar和innerVar也会在相应的作用域中找到。
闭包示例
javascript
function createCounter() {
let count = 0;
return function () {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 输出: 1
counter(); // 输出: 2
counter(); // 输出: 3在这个例子中:
createCounter函数返回了一个匿名函数。- 这个匿名函数形成了一个闭包,因为它保留了对
count变量的访问权。 - 即使
createCounter已经执行完毕并退出了,count仍然存在于内存中,因为counter函数需要访问它。 counter函数的作用域链包括其自身的变量对象、createCounter函数的变量对象和全局变量对象。
作用域链的可视化
假设我们有以下代码:
javascript
var globalVar = "I'm global";
function outer() {
var outerVar = "I'm in the outer function";
function inner() {
var innerVar = "I'm in the inner function";
console.log(globalVar, outerVar, innerVar);
}
inner();
}
outer();对应的作用域链可以表示为:
inner函数的作用域链:inner函数的变量对象(包含innerVar)outer函数的变量对象(包含outerVar)- 全局变量对象(包含
globalVar)
总结
- 作用域链:是一个由多个变量对象组成的列表,用于解析标识符。
- 变量查找:从当前执行上下文的变量对象开始,沿着作用域链向上查找,直到找到该变量或到达全局作用域。
- 闭包:如果一个函数返回另一个函数,并且这个内部函数引用了外部函数的局部变量,那么这些局部变量会被闭包捕获,即使外部函数已经执行完毕,这些变量仍然可以通过闭包访问。
理解作用域链对于编写高效、可维护的 JavaScript 代码非常重要。如果你有更多关于作用域链的具体问题或需要进一步的例子,请告诉我!