概念

JavaScript 闭包是指在函数内部定义的函数,能够访问其外部函数的作用域中的变量,即使外部函数执行完毕后,内部函数仍然保留对这些变量的引用。这样的特性使得 JavaScript 具有强大的灵活性和封装性。

什么情况下会出现闭包

a.函数当做返回值被返回

1
2
3
4
5
6
7
8
9
function fn(){
const a = 1;
return function(){
console.log(a)
}
}
const a = 5
const cb = fn()
cb()//1

b.函数当作参数传递

1
2
3
4
function fn(cb) {
const a = 100;
cb()
}

c.自执行匿名函数

1
2
3
(function(index){
console.log(index)
})(10)

1. 基本结构示例:

1
2
3
4
5
6
7
8
9
10
11
12
function outerFunction() {
let outerVariable = 'I am from outer!';

function innerFunction() {
console.log(outerVariable);
}

return innerFunction;
}

const closureExample = outerFunction();
closureExample(); // 输出:I am from outer!

在这个例子中,innerFunction 形成了一个闭包,可以访问外部函数 outerFunction 中的变量 outerVariable

2. 使用场景

2.1 保护变量

通过闭包,可以创建私有变量,使其在外部不可直接访问,提供了一种简单的封装机制。

1
2
3
4
5
6
7
8
9
10
11
12
function createCounter() {
let count = 0;

return function() {
count++;
console.log(count);
};
}

const counter = createCounter();
counter(); // 输出:1
counter(); // 输出:2

优点:

  • 提供了数据封装,防止全局污染。

缺点:

  • 每次创建闭包都会生成新的函数,可能占用更多内存。

2.2 模块模式

使用闭包可以创建模块,将相关的功能和变量封装在一起,避免全局命名空间的污染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const module = (function() {
let privateVariable = 'I am private!';

function privateFunction() {
console.log('This is private!');
}

return {
publicVariable: 'I am public!',
publicFunction: function() {
console.log('This is public!');
privateFunction();
}
};
})();

console.log(module.publicVariable); // 输出:I am public!
module.publicFunction(); // 输出:This is public! This is private!

优点:

  • 提供了一种简单的模块化机制。

缺点:

  • 可能导致过度封装,使得模块过于独立。

2.3 事件处理程序

闭包常用于事件处理程序,可以访问定义它们的作用域中的变量。

1
2
3
4
5
6
7
8
9
10
function setupButton() {
let count = 0;

document.getElementById('myButton').addEventListener('click', function() {
count++;
console.log(`Button clicked ${count} times.`);
});
}

setupButton();

优点:

  • 允许在事件处理程序中维护状态。

缺点:

2.4 回调函数

异步编程中,闭包常用于创建回调函数,以保持对外部变量的引用。

1
2
3
4
5
6
7
8
9
10
11
12
function fetchData(url, callback) {
fetch(url)
.then(response => response.json())
.then(data => callback(data))
.catch(error => console.error(error));
}

function processData(data) {
console.log('Data processed:', data);
}

fetchData('https://example.com/api/data', processData);

优点:

  • 保持了对异步操作的状态追踪。

缺点:

  • 可能导致回调地狱(Callback Hell)。

2.5 函数工厂

通过闭包可以创建函数工厂,动态生成函数。

1
2
3
4
5
6
7
8
9
10
11
function greetingGenerator(greeting) {
return function(name) {
console.log(`${greeting}, ${name}!`);
};
}

const sayHello = greetingGenerator('Hello');
const sayHi = greetingGenerator('Hi');

sayHello('John'); // 输出:Hello, John!
sayHi('Alice'); // 输出:Hi, Alice!

优点:

  • 动态生成函数,增加了灵活性。

缺点:

  • 可能导致函数数量爆炸,占用更多内存。