闭包与作用域
大约 2 分钟
闭包与作用域
作用域 (Scope)
作用域规定了变量能够被访问的范围。
- 全局作用域: 代码中任何地方都能访问。
- 函数作用域: 只能在函数内部访问。
- 块级作用域 (ES6):
let和const在{}中声明的变量只能在块内部访问。
let globalVar = "Global";
function outer() {
let outerVar = "Outer";
if (true) {
let blockVar = "Block";
console.log(blockVar); // Block
}
// console.log(blockVar); // Error
function inner() {
console.log(outerVar); // Outer (作用域链)
console.log(globalVar); // Global
}
inner();
}
闭包 (Closure)
闭包是指有权访问另一个函数作用域中变量的函数。通常是在一个函数内部创建另一个函数。
核心特性: 即使外部函数已经执行完毕,闭包依然可以访问外部函数的变量(变量依然保存在内存中)。
基础示例
function createCounter() {
let count = 0; // 私有变量
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
const counter2 = createCounter();
console.log(counter2()); // 1 (独立的闭包环境)
应用场景
- 模拟私有变量/模块化:
const User = (function() {
let _name = "Unknown"; // 私有变量
return {
getName: function() {
return _name;
},
setName: function(newName) {
_name = newName;
}
};
})();
console.log(User.getName()); // Unknown
User.setName("Tom");
console.log(User.getName()); // Tom
// console.log(User._name); // undefined
- 柯里化 (Currying):
function makeAdder(x) {
return function(y) {
return x + y;
};
}
const add5 = makeAdder(5);
console.log(add5(2)); // 7
- 循环中的陷阱:
// 问题代码
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 输出三次 4
}, 1000);
}
// 解决方案1: IIFE (立即执行函数) 创建闭包
for (var i = 1; i <= 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // 1, 2, 3
}, 1000);
})(i);
}
// 解决方案2: 使用 let (推荐)
for (let i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 1, 2, 3
}, 1000);
}
内存泄漏
由于闭包会引用外部函数的变量,导致这些变量无法被垃圾回收机制回收。如果闭包使用不当,可能会导致内存泄漏。
解决方法: 在不需要使用闭包时,将引用闭包的变量设置为 null。
let closure = createCounter();
// ... 使用闭包
closure = null; // 释放内存
this 指向
this 的指向是在函数被调用时确定的。
- 默认绑定: 独立函数调用,指向全局对象 (严格模式下为 undefined)。
- 隐式绑定: 对象方法调用,指向调用该方法的对象。
- 显式绑定:
call,apply,bind,指向指定的对象。 - new 绑定: 指向新创建的实例对象。
- 箭头函数: 继承外层作用域的
this。
const obj = {
name: "Alice",
say: function() {
console.log(this.name);
},
sayArrow: () => {
console.log(this.name);
}
};
obj.say(); // Alice
obj.sayArrow(); // undefined (浏览器中指向 window.name)
const func = obj.say;
func(); // undefined (默认绑定)
const boundFunc = obj.say.bind({ name: "Bob" });
boundFunc(); // Bob (显式绑定)
优先级: new > 显式绑定 (bind/call/apply) > 隐式绑定 (obj.method) > 默认绑定。
Loading...
