1.什么是闭包
首先假设有一个函数a和函数b,b定义在函数a里面
function a(){
var i=0;
function b(){
return i++;
}
b();
}
不是闭包的情况下,调用a,a里面调用b,完成工作,变量被收回。
但是现在有一个函数c,它想要调用a里面的变量i,但是i不在c的作用域链中。
而闭包的实现是:
a运行,不再立即执行b,而是返回b,也就是将b暴露出来,此时外部的c运行时得到返回的b,从而得到i值。
function a(){
var i=0;
function b(){
return i++;
}
return b;
}
var c=a();
c();
这就是闭包:子函数可以使用父函数的局部变量,实现原理就是作用域链(scope chain)
一句重要的话:JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。
javascript的回收机制决定了a和b执行完之后,函数内部变量会被回收,但是被第三方引用的情况下,不会被回收。
a运行完被释放,但是里面变量i还在被占用,所以不断执行c()会返回不断递增的i。
让c=null或者其他值,可以释放a的引用,让a里面的变量回收。
闭包只存储外部变量的引用,而不会拷贝这些外部变量的值。
注意: 如果运行a()(),第一次返回1,继续运行,不会递增。因为每运行一次a()(),都会执行var i=0,因此不会累加。
更加通俗的解释就是:
在爷爷的环境中执行了爸爸,爸爸中返回了孙子,本来爸爸被执行完了,爸爸的环境应该被清除掉,但是孙子引用了爸爸的环境,导致爸爸释放不了,这一坨就是闭包。
2.为什么用闭包
局部变量是无法共享和长久保存的,全局变量可能造成变量污染,所以希望有一种可以长久保存的变量又不会造成全局污染。
大项目需要模块化,变量私有化,可以用到闭包。
3.闭包特点
占用更多内存,不容易被释放
4.实际使用
- timer定时器
(function autorun(){
var x = 1;
setTimeout(function log(){
console.log(x);
}, 10000);
})();
//变量 x 会一直存活到定时器的回调执行或者 clearTimeout() 被调用
- 事件处理
function addEvent() {
var foo = 0;
document.getElementById('btn').addEventListener('click', function (e) {
console.log(foo++);
});
}
addEvent();
//不断点击按钮,会输出不断递增的foo
- Ajax
(function autorun(){
var x = 1;
fetch("http://").then(function log(){
console.log(x);
});
})();
//变量 x 将一直存活到接收到后端返回结果,回调函数被执行
5.多层嵌套
分析下面的代码会输出什么
var a = 1
function f(b) {
return function g(c) {
return function h(d) {
return a + b + c + d
}
}
}
var add2 = f(2)
var add4 = add2(2)
var add5 = add2(3)
var r1 = add4(10)
var r2 = add5(10)
console.log(r1,r2);