在 JavaScript 里,作用域和作用域链是基础概念,它们决定了变量与函数的可访问范围。
作用域
作用域的定义是变量和函数的可访问范围,它控制着变量与函数的可见性和生命周期。在 JavaScript 中,作用域可以分为以下几种类型:
- 全局作用域:在代码里任何地方都能访问全局作用域中的变量。
var globalVar = '我处于全局作用域';
function test() {
console.log(globalVar); // 能够访问全局变量
}
- 函数作用域:在函数内部定义的变量,只能在该函数内部被访问。
function example() {
var localVar = '我处于函数作用域';
console.log(localVar); // 可以访问局部变量
}
example();
console.log(localVar); // 会报错,无法访问局部变量
- 块级作用域(借助
let和const实现):在{}内部定义的变量,只能在这个块内被访问。
if (true) {
let blockVar = '我处于块级作用域';
console.log(blockVar); // 可以访问块级变量
}
console.log(blockVar); // 会报错,无法访问块级变量
作用域链
当查找一个变量时,JavaScript 引擎会先在当前作用域中进行搜索,如果没找到,就会到父级作用域中继续搜索,直到找到该变量或者到达全局作用域。这种层层嵌套的作用域组合起来,就形成了作用域链。如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错。
下面通过一个例子来理解作用域链:
var globalVar = '全局变量';
function outer() {
var outerVar = '外层函数变量';
function inner() {
var innerVar = '内层函数变量';
console.log(innerVar); // 访问自身作用域的变量
console.log(outerVar); // 访问外层函数的变量
console.log(globalVar); // 访问全局变量
}
inner();
}
outer();
作用域链的工作流程:
- 当
inner函数尝试访问innerVar时,会直接在自己的作用域中找到该变量。 - 当访问
outerVar时,在自身作用域中找不到,就会到外层函数outer的作用域中去寻找。 - 当访问
globalVar时,在内层和外层函数的作用域中都找不到,就会到全局作用域中寻找。
闭包与作用域链
闭包是指有权访问另一个函数作用域中的变量的函数。闭包会携带包含它的函数的作用域,因此可能会导致内存占用方面的问题。
function outer() {
var count = 0;
return function() {
count++; // 闭包可以访问外层函数的变量
console.log(count);
};
}
var counter = outer();
counter(); // 输出 1
counter(); // 输出 2
在这个例子中,内部函数形成了一个闭包,它始终保留着对 outer 函数中 count 变量的引用。
总结
- 作用域:规定了变量和函数的可见范围,有全局作用域、函数作用域和块级作用域之分。
- 作用域链:是由多个嵌套的作用域组成的,它决定了变量的查找顺序。
- 闭包:能够访问其他函数作用域中变量的函数,即使该函数已经执行完毕。