一篇文章带你了解闭包,闭包,其实也没那么复杂
闭包的英文名是(Closure)翻译过来就是闭合的意思。闭包是个函数,它不仅能记住自己内部的变量,也能记住自己被创建所在的作用域。即使这个函数被传递到其他地方,它也仍然可以访问原来作用域中的变量。
闭包
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包 = 内部函数 + 外部函数的变量
** 示例一:简单的闭包**
function outerFunction(){ // 定义外部函数
let outerVariable="外部函数的变量" // 外部函数的变量
// 定义内部函数
function innerFunction(){
// 内部函数可以访问外部函数的变量
console.log(outerVariable);
}
return innerFunction // 闭包 外部函数的变量被内部函数引用,形成闭包
}
// 调用外部函数,返回闭包
const myClosure=outerFunction();
myClosure()// 外部函数的变量
在这个例子中:
-
outerFunction 定义了一个外部函数的变量outerVariable 和 一个内部函数 innerFunction
-
内部函数可以访问外部函数的变量,形成闭包
-
innerFunction 可以访问 outerFunction 定义的变量,形成闭包
-
即使outerFunction已经执行完毕
-
outerFunction返回了innerFunction,将其赋值给了变量myClosure
-
当调用myClosure()时,它仍然可以访问outerVariable,形成闭包,这就是闭包的作用
简单来说:
我们可以这样理解,闭包就像是个封闭的小盒子,你在盒子内放了一本日记,这个盒子有个功能:它能记住你写日记时的房间,,即使你把日记给了朋友,他也能看到那本日记
function noteBookMessage(){
let Book="这是我的日记"//这是日记的内容
// 这个函数就像是那个盒子,它能记住Book这个日记
function showMessage(){
console.log(Book);
}
// 把盒子(函数)给朋友
return showMessage;
}
// 你把盒子给了朋友
const myBook=noteBookMessage();
// 朋友打开盒子,看到日记
myBook();// 这是我的日记
在这个例子中:
-
noteBookmessage 是一个函数,它创建了一个日记内容Book。
-
showMessage 是一个内部函数,它能访问Book这个外部函数的变量,就像盒子能记住日记里的内容一样。
-
当你调用noteBookmessage()时,它返回了showMessage函数,这个函数就是一个闭包。
-
即使noteBookMessage 函数执行完毕,showMessage 函数仍然可以访问Book,因为它记住了创建它时所在的作用域。
示例二:数据封装和私有化
普通形式,统计函数调用的次数
let i=0
function fun2(){
i++
console.log(`函数被调用了${i}次`);
}
fun2()
fun2()
i是全局变量,很容易被修改
以i=100为例
闭包形式 统计函数调用的次数
闭包的作用:实现数据的私有化 函数调用,函数调用一次,就++
let z;
function count(){
let i=0
function fun(){
i++
console.log(`函数被调用了${i}次`);
}
return fun;
}
z= count();//创建闭包
z() // 1 使用闭包
z() // 2
z() // 3
刚才我们举例说到:
闭包就像是一个封闭的盒子,这里面放了你的日记或者你的其他一些秘密的东西。
但盒子上有几个特定的钥匙孔,你只能通过钥匙孔去取放盒子内部的东西,有的钥匙孔可以让你往盒子里放东西,有的可以让你往盒子里拿东西。
通过这种方法,i的值被封装在函数的内部,外部代码无法直接访问它,只能通过fun 方法来操作它。这就体现了数据的封装和私有化,保护了数据的安全性。
示例三:函数工厂
我们知道函数是一段反复调用的代码块,就像是一个模具:你只需要告诉卖家你需要什么样的模具,那你就可以一直生产这种模具所产生的工艺品。
function createMultiplier(multiplier) {
// 这个函数返回一个闭包
return function(number) {
return number * multiplier;
};
}
// 创建一个专门乘以2的函数
const double = createMultiplier(2);
// 创建一个专门乘以3的函数
const triple = createMultiplier(3);
console.log(double(5)); // 输出: 10
console.log(triple(5)); // 输出: 15
说明:
1.工厂函数 (createMultiplier):
- createMultiplier 是一个工厂函数,它接受一个参数 multiplier, 这个参数决定了返回的内部函数如何处理数字。
- 闭包 (返回的函数) :
- createMultiplier 返回了一个闭包函数。这个闭包函数捕获了 mutiplier参数,即使 createMultiplier 函数执行完毕,返回的函数仍然可以访问multiplier
示例四: 释放闭包
通俗解释: 我们之前说过闭包就像是个封闭小盒子,里面放了我们的日记,我们把盒子关上之后,但我们手里还有这个盒子的钥匙。这把钥匙就是闭包。
如果闭包一直存在,它所引用的变量(盒子里的东西)也就不会被清理掉,即使里面的日记不再使用了。这就导致了内存泄漏。
而避免闭包内存泄漏的方法可以释放闭包:
当你不再需要闭包时,将其设置为null或者其他值,以便垃圾回收站回收内存。
let z;
function count(){
let i=0
function fun(){
i++
console.log(`函数被调用了${i}次`);
}
return fun;
}
z= count();//创建闭包
z() // 1 使用闭包
z() // 2
z() // 3
z() // 4
z=null//不需要闭包了,清理它
z()// 无效
闭包的优缺点
优点:
- 代码整洁 : 闭包可以减少全局变量的使用,使代码更加整洁易于维护。
- 数据封装 : 闭包可以创建私有变量,防止外部直接访问,从而实现数据封装和私有化。
- 功能强大 :闭包可以实现很多强大的功能,如函数工厂,延迟计算等
缺点
- 内存泄漏:如果闭包不再需要被使用,就很可能导致内存泄漏。因为闭包的内部函数会捕捉外部函数的变量,如果这些变量不再需要,但闭包仍存在,就会占用内存。
- 难以理解:闭包的概念比较抽象,复杂,因为闭包通常涉及至少两个函数,对于初学者来说可能需要一段时间去理解
总结
闭包是javaSript中一个非常强大的特性,它可以让内部函数访问外部函数的作用域,从而实现数据封装,函数工厂等功能。但同时,使用闭包也要防止内存泄漏等问题。希望这篇文章能使你更好的了解闭包!