作用域与闭包
外观
作用域(Scope)[编辑 | 编辑源代码]
作用域是程序中变量、函数和对象的可访问性规则,决定了代码中变量的可见性。JavaScript采用词法作用域(静态作用域),即作用域在代码编写时确定。
作用域类型[编辑 | 编辑源代码]
全局作用域[编辑 | 编辑源代码]
全局作用域中定义的变量可在代码任何位置访问:
var globalVar = "全局变量";
function showGlobal() {
console.log(globalVar); // 可访问
}
showGlobal(); // 输出: "全局变量"
函数作用域[编辑 | 编辑源代码]
通过var
声明的变量具有函数作用域:
function demoFunctionScope() {
var localVar = "局部变量";
console.log(localVar); // 可访问
}
demoFunctionScope(); // 输出: "局部变量"
console.log(localVar); // 报错: localVar未定义
块级作用域[编辑 | 编辑源代码]
ES6引入的let
和const
支持块级作用域:
if (true) {
let blockVar = "块级变量";
console.log(blockVar); // 输出: "块级变量"
}
console.log(blockVar); // 报错: blockVar未定义
作用域链[编辑 | 编辑源代码]
JavaScript通过作用域链逐级向上查找变量:
示例:
var outerVar = "外部";
function outer() {
var middleVar = "中间";
function inner() {
console.log(outerVar, middleVar); // 依次向上查找
}
inner(); // 输出: "外部 中间"
}
outer();
闭包(Closure)[编辑 | 编辑源代码]
闭包是函数与其词法环境的组合,即使函数在其词法作用域外执行,仍能访问原始作用域的变量。
闭包原理[编辑 | 编辑源代码]
闭包的形成需要三个条件:
- 函数嵌套
- 内部函数引用外部变量
- 内部函数在外部作用域被调用
示例:
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 输出: 1
console.log(counter()); // 输出: 2
闭包的实际应用[编辑 | 编辑源代码]
数据封装[编辑 | 编辑源代码]
实现私有变量:
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit: function(amount) {
balance += amount;
return balance;
},
withdraw: function(amount) {
if (amount > balance) return "余额不足";
balance -= amount;
return balance;
}
};
}
const account = createBankAccount(1000);
console.log(account.deposit(500)); // 输出: 1500
console.log(account.withdraw(200)); // 输出: 1300
模块模式[编辑 | 编辑源代码]
现代模块系统的基础:
const calculator = (function() {
function add(a, b) { return a + b; }
function multiply(a, b) { return a * b; }
return { add, multiply };
})();
console.log(calculator.add(2, 3)); // 输出: 5
性能注意事项[编辑 | 编辑源代码]
闭包可能导致内存泄漏,需注意:
- 不再使用的闭包应及时解除引用
- 避免在循环中创建闭包
进阶概念[编辑 | 编辑源代码]
词法环境(Lexical Environment)[编辑 | 编辑源代码]
JavaScript引擎内部通过词法环境实现作用域,包含:
- 环境记录(存储变量)
- 对外部词法环境的引用
数学表示:
IIFE(立即调用函数表达式)[编辑 | 编辑源代码]
创建独立作用域的经典模式:
(function() {
var privateVar = "私有";
console.log(privateVar); // 输出: "私有"
})();
// console.log(privateVar); // 报错
常见面试题分析[编辑 | 编辑源代码]
循环中的闭包问题[编辑 | 编辑源代码]
经典问题及解决方案:
// 问题代码
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 输出三次 4
}, 100);
}
// 解决方案1:使用IIFE
for (var i = 1; i <= 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // 输出 1, 2, 3
}, 100);
})(i);
}
// 解决方案2:使用let
for (let i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 输出 1, 2, 3
}, 100);
}