闭包的由来
JS中的闭包是由于函数作用域和作用域链的特性而产生的。
在JS中,每次定义一个函数时,都会创建一个新的函数作用域。函数内部可以访问函数外部的变量和函数,但是函数外部无法直接访问函数内部的变量和函数。这种作用域的嵌套关系形成了作用域链。
当一个函数内部定义了一个函数,并且内部函数引用了外部函数的变量或函数时,内部函数会持有对外部函数作用域的引用。即使外部函数执行完毕后,这个引用仍然存在于内部函数中,形成了闭包。闭包使得内部函数可以继续访问和操作外部函数的变量,即使外部函数已经不处于活动状态。
闭包的定义
来看看mdn上是怎么说的
闭包 (closure)是一个函数以及其捆绑的周边环境状态(lexical environment ,词法环境)的引用组合。
比如,当一个函数返回另一个函数时,如果被返回的函数引用了其父函数中的变量,那么这个被返回的函数就成为了一个闭包。
js
//函数作为返回值
function test() {
const a = 11;
return function () {
return `${a} closure`;
};
}
const t = test();
console.log(t()); //这里就形成了闭包 输出11 closure
// 函数作为参数
function test1(fn) {
const b = 33;
fn();
}
const b = 22;
function fn() {
console.log(`${b} closure`);
}
test1(fn);// 22 closure
//`fn`函数在定义时引用了外部的变量`b`。当`test1`函数调用`fn`函数时,`fn`函数仍然可以访问并使用定义时所引用的外部变量`b`。因此,`fn`函数形成了闭包。
由例子也可以看出,闭包会随着函数的创建而被同时创建。
闭包两个特点
- 函数嵌套函数
- 内层函数可以访问外层函数的变量和参数,包括可以访问其定义时所处作用域以及父作用域变量
闭包两个作用
- 防止变量和参数被垃圾回收机制(变量持久化)
- 防止变量和参数被外部污染(变量只在闭包内部可访问)
闭包风险
- 可能有内存泄露的风险
⭐⭐️⭐️⭐⭐️️️相关面试回答
(1) 闭包 (closure)是一个函数以及其捆绑的周边环境状态(lexical environment ,词法环境)的引用组合,包括可以访问其定义时所处作用域以及父作用域中的变量。
(2) 闭包一般是函数嵌套,一个函数返回另外一个函数,内部函数访问外部函数的变量就形成了一个闭包
(3) 闭包的优点是可以私有化变量,将变量私有化到函数内部,并在私有化的基础上进行数据保持
(4) 闭包的缺点是容易造成内存泄露,因为闭包创建的变量会一直存在内存中,需要及时置空,否则会造成内存泄露,影响程序性能
举例作用
(5) 闭包在防抖节流函数柯里化,都应用里数据保持(变量持久化)这个特性
比如在防抖函数中,第一次点击的时候,我们会let一个time一个定时器,如果不采用闭包的话,下次触发函数会重新创建一个新的定时器,两个定时器的引用不同,是没有关联的,使用闭包可以直KKKK接在内存中找到之前创建的计时器,调用就可以直接拿到对应的定时器的时间
闭包经典问题
js
var arr = [];
for (var i = 0; i < 3; i++) {
(function(i) {
arr[i] = function() {
console.log(i);
};
})(i);
}
arr[0]();//1
arr[1]();//2
arr[2]();//3
var res1 = [];
for (let i = 0; i < 3; i++) {
res1[i] = function () {
console.log(i);
};
}
res1[0]();//1
res1[1]();//2
res1[2]();//3
解决问题可以使用立即执行函数来创建一个独立作用域,让每个函数都能捕获到对应的 i 的值。或者用let创建块级作用域
面试题解析
js
function foo(n, o) {
console.log(o); //第一次undefined
return {
fun: function (m) {
return foo(m, n); // 1, n根据作用域链往上找 n = 0
},
};
}
var a = foo(0); //undefined
a.fun(1); //0
a.fun(2); //0
a.fun(3); //0
第二个
js
function foo(n, o) {
console.log(o); //undefined // 0 // 1 // 2 // 3
return {
fun: function (m) {
return foo(m, n); //上级作用域的参数一直在改变 //(1,0) // (2,1) //(3,2)
},
};
}
var a = foo(0).fun(1).fun(2).fun(3); //undefined 0 1 2
第三个自己做一下吧
js
function foo(n, o) {
console.log(o);
return {
fun: function (m) {
return foo(m, n);
},
};
}