闭包(Closure)
闭包是个老生常谈的概念了,我们在开发的过程中也会经常遇到闭包。其实闭包并不复杂。只不过概念比较绕。
闭包概念及特性
- 闭包(Closure): 函数和其周围的状态(词法环境)的引用捆绑在一起形成闭包。
- 特性:可以在另一个作用域中调用一个函数的内部函数并且能访问到该函数中成员
代码示例 1
function makeFn() {
let msg = "hello fn";
return function () {
console.log(msg);
};
}
const fn = makeFn();
fn();
我们可以看到此处代码 const fn = makeFn()
执行后 fn
将会是函数 makeFn
中 reutrn
的那个函数(1),此时外部的 fn
对 makeFn
中返回的函数有引用,而在这个函数(1)中又访问了 makeFn
中的 msg
变量。此时就形成了闭包。
代码示例 2
// once 生成一个只执行一次的函数
function once(fn) {
let done = false; // 记录fn是否执行
return function () {
if (!done) {
done = true;
fn.apply(this, arguments); // arguments指的上一行这个函数的参数
}
};
}
// 测试
let pay = once(function (money) {
console.log(`支付了:${money} RMB`);
});
pay(5); // 打印 支付了:5 RMB, 此处的入参5就是上面 arguments
pay(6); // 不会执行
pay(3); // 不会执行
闭包的本质
函数在执行时会放到执行栈上,当函数执行完成之后会从执行栈上移除,但是堆上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问外部函数的成员
闭包案例
// 假设我们经常去求 一个数的次幂 ,假如 2次方
// 我们发现第二个参数 一直在出现并且是重复的,
// 我们可以写一个函数,去生成一个求 2/3/4 次方的函数
// Math.pow(4, 2)
// Math.pow(3, 2)
/**
* 生成一个求几次幂的函数
* @param {几次幂} power
*/
function makePower(power) {
return function (num) {
return Math.pow(num, power); // (2) 闭包发生的位置 power Closure
};
}
// 求平方
let power2 = makePower(2);
// 求3次方
let power3 = makePower(3);
console.log(power2(2)); // 4 (1)
console.log(power2(3)); // 9
console.log(power3(2)); // 8
我们让代码进入调试模式 当代码执行到 console.log(power2(2))
也就是标注 (1) 的位置时按下 F11
步入 到标注 (2)的位置,这时我们可以清楚看到在调试工具栏 Scope
中 出现了 Closure
字样。
所以当我们通过调试时可以清楚看到闭包发生位置和详细信息