一.闭包函数的定义
在JavaScript在存在一个不会销毁的函数空间--闭包,在JavaScript中如果一个函数返回的是一个复杂数据类型,并且被外部的变量引用,那么这个函数空间在执行完成后也不会销毁。
闭包函数:在一个函数内部定义一个函数,并且这个内部函数还访问了外部函数定义的私有变量,在函数外部还有一个变量接收这个函数的返回值(内部函数),那么这个内部函数就称为闭包函数。
我们称这个不会被销毁的函数空间称为闭包(整个外部函数的执行空间),返回的函数叫做闭包函数。
1.函数内部返回一个函数
2.内部函数调用了外部函数定义的私有变量
3.外部有一个变量接收这外部函数返回的内部函数
function a() {
let num = 100
return function b() {
console.log(num)
}
}
let res = a()
// 从现在开始 res随时可以被调用
// 在我们的执行空间里 函数a里面的一切都不会被销毁
// 当res调用的时候,打印的是私有变量num的值
二.闭包的特点
闭包的特点(优点和缺点并存)
1 延长了变量的生命周期
+ 优点:因为执行空间不会销毁,变量也没有销毁
+ 缺点:因为执行空间不会销毁,所以变量一直存在内存中,占用内存
2 可以访问函数内部的私有变量
+ 优点:利用闭包函数可以访问函数内部的私有变量
+ 缺点:因为执行空间不会销毁,所以变量一直存在内存中,占用内存
闭包函数的缺点是:“致命的”
因为当一段内存中有一个不能被销毁的东西一直存在的时候
那么就会出现内存占用,如果过多的占用内存,就会导致内存溢出
也就是内存泄漏(内存泄漏(Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存,并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费,程序的运行需要内存。只要程序提出要求,操作系统或者运行时就必须供给内存。对于持续运行的服务进程,必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃)
三.闭包函数的应用场景
(1)柯里化函数
柯里化(Currying)是一种处理多元函数的方法。它产生一系列连锁函数,其中每个函数固定部分参数,并返回一个新函数,用于传回其它剩余参数的功能。在实际应用场景中,函数输入的参数有可能有一部分是相同的,其他的才需要多次输入参数,我们可以使用函数柯里化,将一部分参数固定,减少参数的定位和赋值。
需求:打印一个我们班学员的信息:我是xx学科 xx班的xxx xxx岁
// 需求:打印一个我们班学员的信息:我是xx学科 xx班的xxx xxx岁
function printInfo(xueke, banji, name, age) {
console.log(`我是${xueke} ${banji}班级 我叫${name} ${age}岁了`)
}
printInfo('计算机', '前端', '海绵宝宝', 18)
printInfo('计算机', '前端', '派大星', 19)
printInfo('计算机', '前端', '章鱼哥', 19)
// 改写我上面的这个不好用函数
// 对于学科和班级都是一样的我觉得没必要在作为参数传递了
// 写一个闭包
function printInfo2(xueke, banji) {
return function (name, age) {
console.log(`我是${xueke} ${banji}班级 我叫${name} ${age}岁了`)
}
}
// 生产出一个固定学科和班级的函数
let print2020 = printInfo2('计算机', '前端')
// 开辟了一个printInfo空间 xxff11
// 在这个空间里面进行形参的赋值
// let xueke = 计算机
// let banji = 前端
// 定义函数 function (name, age) {}
// 把执行空间里面定义的这个函数地址xxff1122 赋值给 print2020
print2020('海绵宝宝', 18)
print2020('派大星', 19)
print2020('章鱼哥', 19)
// 执行xxff1122函数 自己的name和age,没有看见xueke和banji
// 我就去上级作用域的执行空间里面xxff11找打了存储xueke和banji的变量
// 我就直接一起拿来用了
这里运用了闭包函数在自己的作用域中没有找到需要的值时,会去外部函数寻找,由于闭包函数的函数空间是不会销毁的,所以printInfo的函数执行空间不会销毁,闭包函数都可以访问到我们事先传入的xueke和banji。
(2)使用闭包模拟私有方法
在JavaScript
中,没有支持声明私有变量,但我们可以使用闭包来模拟私有方法。
var makeCounter= (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
})();
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */
上述通过使用闭包来定义公共函数,并令其可以访问私有函数和变量,这种方式也叫模块方式。
令makeCounter 等于 一个自执行函数,使其直接等于这个函数的返回值(闭包函数),闭包函数是一个对象,对象里面有多个方法,可以访问外部函数的方法,但因为是自执行函数,没有变量引用这个方法,所以无法使用函数内的changeBy方法,相当于只能通过闭包函数来使用这个方法,这就模拟了一个私有方法,只能内部的闭包访问changeBy方法,其他人无法访问。