JavaScript 学习-24.函数闭包(closure)
前言
闭包是面试最喜欢问的一个问题了,面试官最喜欢问:
1.什么是闭包?2.闭包的作用是什么?
什么是闭包?
闭包(closure)是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰,即形成一个不销毁的栈环境。
闭包的特性:
- 函数嵌套函数
- 内部函数可以访问外部函数的变量
- 参数和变量不会被回收。
先看一个典型的闭包, 实现计数器的功能
function myCounter() {
var counter = 0;
function add() {
counter += 1;
console.log(counter);
}
return add;
}
var myAdd = myCounter();
myAdd();
myAdd();
myAdd();// 计数器现在为 3
在弄清楚闭包之前,我们需要弄明白一个概念:私有的作用域
变量作用域
var 声明的变量有2个作用域:全局作用域 和局部作用域。
let 和 const 声明的变量有3个作用域:全局作用域 和局部作用域, 还有个块作用域。块作用域这里就不讲了
全局作用域
在函数外部var 声明的变量就是全局作用域
var count = 0
// 每次调用fun() count 加1
function fun() {
count += 1;
}
fun();
fun();
fun();
console.log(count); // 3
函数局部作用域
在函数内部声明的变量是函数局部作用域
var count = 0
// 每次调用fun() count 加1
function fun() {
var count = 0;
count += 1;
}
fun();
fun();
fun();
console.log(count); // 0
虽然函数内部也声明了一个count变量,但是跟函数外部的count其实是2个不同的变量,所以每次调用函数,仅仅只是内部变量count加1了,但是外部的count还是0。
计数器问题
如果我们想设置一个计数器,当我们打开一个页面,只有5次点击机会,点完就不能再点击了。
<body>
<button type="button" id="btn"></button>
<script>
var counter = 5;
// 每次调用fun() count -1
function fun() {
counter -= 1;
if (counter <= 0){
document.getElementById('btn').disabled = true;
return
}
}
// 设置初始值
document.getElementById('btn').innerHTML = '还剩' + counter + '次';
// 每点一次减1
document.getElementById('btn').onclick = function () {
fun();
document.getElementById('btn').innerHTML = '还剩' + counter + '次';
}
</script>
</body>
实现效果
每点一次会减1
最后为0时不可被点击
看起来是没什么问题,但是这个会有一个安全隐患,由于counter变量是全局的,所以在其它任何地方都可以改变全局变量的值。
如果用户在控制台,改变了全局变量counter的值,比如我在console执行var counter=100
当我点一下按钮,就会变成99次
闭包的作用
为了解决这种全局变量导致的问题,所以我们需要在函数内部设置一个私有变量
<body>
<button type="button" id="btn"></button>
<script>
// 每次调用fun() count -1
function fun() {
var counter = 5;
document.getElementById('btn').innerHTML = '还剩' + counter + '次';
function inner(){
counter -= 1;
if (counter <= 0){
document.getElementById('btn').disabled = true;
}
return counter
}
return inner
}
mycounter = fun();
// 每点一次减1
document.getElementById('btn').onclick = function () {
var x = mycounter();
document.getElementById('btn').innerHTML = '还剩' + x + '次';
}
</script>
</body>
或者把counter作为一个变量传给函数,此时也不会影响
<body>
<button type="button" id="btn"></button>
<script>
// 每次调用fun() count -1
function fun(counter) {
document.getElementById('btn').innerHTML = '还剩' + counter + '次';
function inner(){
counter -= 1;
if (counter <= 0){
document.getElementById('btn').disabled = true;
}
return counter
}
return inner
}
var counter = 5;
mycounter = fun(counter);
// 每点一次减1
document.getElementById('btn').onclick = function () {
var x = mycounter();
document.getElementById('btn').innerHTML = '还剩' + x + '次';
}
</script>
</body>
因为 fun()函数只执行一次,所以会锁定局部变量counter。
计数器
如果是从0开始,每运行一次函数加1,那么可以优化成以下代码
<body>
<button type="button" onclick="myFunction()">计数!</button>
<p id="demo"></p>
<script>
// 自调用函数
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
function myFunction(){
document.getElementById("demo").innerHTML = add();
}
</script>
</body>
每点一次按钮会加1
闭包的作用
综上所述,闭包的作用就是在外部a函数执行后,闭包使得Javascript的垃圾回收机制不会收回a所占用的资源,因为a内部函数的变量要给到内部函数b继续使用。
那么闭包的好处有以下几点:
- 保护函数内的变量安全
- 在内存中维持一个变量(用的太多就变成了缺点,占内存) ;
- 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。
- 方便调用上下文的局部变量。
- 加强封装性,可以达到对变量的保护作用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2021-05-24 Airtest IDE 自动化测试6 - 断言存在( assert_exists)和 断言不存在(assert_not_exists)
2021-05-24 Airtest IDE 自动化测试5 - 运行脚本与查看测试报告
2018-05-24 python笔记16-执行cmd指令(os.system和os.popen)