Js中看似可有可无的闭包

Js中看似可有可无的闭包

大一暑假:2022-8-12

闭包已经是js中老生常谈的话题,可是闭包晦涩的概念一直让人捉摸不透,包括我自己在学习闭包的过程中显得非常吃力,看了那么多关于闭包的视频和文档,千篇一律的解释仍然使我很困惑,抽象的概念得到的却只是抽象的解释罢了,很是无奈。但终究要面对(面试可能要问),接下来分享一下一个小白对闭包的理解,纯纯的知其然而不知其所以然。

从我学习闭包的经历来看,我发现直接以抛出概念的方式去学习一个比较抽象的知识,接受程度不尽如人意,所以我尽可能地慢慢引导大家来了解什么是闭包?闭包的存在有什么意义?闭包的缺点及解决方案。

欧了,话不多说,直接进入正题。

老板有需求

既然在我的地盘,我就是一个老板,你们都是一个打工人(实际我以后也是了),我有这么一个需求,你们会如何实现?

你们是不是瞬间两眼发光,心想:加薪的机会来了!

咳咳…

开始搬砖…

需求案例

统计一个函数被调用的次数,每调用一次,次数就++

实际开始板砖

眨眼一看,这老板不纯纯欺负我的智商吗?于是,想给老板还以颜色,一分钟写完了代码。

let count = 0;
function fn(){
    count++;
    console.log(`函数第${count}次调用`);
}
fn(); //函数第1次调用
fn(); //函数第2次调用

push推到代码仓库后,你心安理得地开始摸鱼了…

过了三分钟,你被老板交到办公室,你心情大好地昂首阔步走进去,以为自己要被老板表扬真高效,可以加薪升职,果然,老板说你真搞笑,直接四个字给你“回去,重写!”,你一脸不解地低头走出去。

于是,你重新看着代码,一遍一遍地打开浏览器,关闭浏览器,看着控制台,输出一点没问题,纠结了2分钟,你仍然没有头绪,10分钟后…你突然发现了什么:

问题一count作为全局变量,如果count值被改变,那么该需求无法实现。

let count = 0;
function fn(){
    count++;
    console.log(`函数第${count}次调用`);
}
fn(); //函数第1次调用
count=1000;
fn(); //函数第1001次调用

于是,你一顿操作猛如虎,20秒完成了重写:

function fn(){
    let count = 0;
    count ++;
    console.log(`函数第${count}次调用`);
}
fn(); //函数第1次调用
fn(); //函数第1次调用

结果看着控制台出乎意料的打印结果,你又很快发现了问题。

问题二:每次调用函数,count变量都被初始化,没有被保留下来。

于是,你左思右想,如何能让count不被初始化,凭借多年的编程经验,你突然脑子一阵,想到了改进思路

为了避免count每次都被初始化,你在函数中创建一个内层函数,通过调用内层函数的次数,来代表外层函数的调用次数,这样,外层函数种的count就不会被初始化了。

觉得自己异常聪明的你30s完成了再一次重写:

function outer(){ //外层函数
    let count = 0; 
    function inner (){ //内层函数
        count++;
        console.log(`函数第${count}次调用`);
    } 
    return inner
}
const fn = outer(); //这里fn其实接受的是内层函数inner
fn(); //函数第1次调用
fn(); //函数第2次调用

这一次,看着控制台预料之中的结果,你满怀自信的重新push到代码仓库。

果不其然,这次你摸了一天的鱼…

你也得到了加薪…

恭喜你,你凭借你惊人的天赋和不懈的努力成功地写出了一个闭包

什么是闭包?

啊!什么,我写出了闭包

且听我慢慢分析…

看完上述案例,相信大家仍然感到对闭包很模糊,没关系,这下你就很快能明白并理解闭包了。

概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到外层函数的作用域。

看到这里,你心存疑惑,什么玩意?这是不是人话?

别急,我给你翻译一下:两个函数,一个外层函数,一个内层函数,内层函数使用外层函数中的变量。

这时,你回头看了下你的代码,一个外层函数outer(),一个内层函数inner(),内层函数inner()修改了外层函数outer()中的变量count。你这时突然豁然开朗,感觉明白了什么。

简单理解:内层函数+外层函数的变量

你的代码完全满足。

这时,你又会想,这有什么作用呢?

听我接下来分析。

闭包的作用

  • 1.隐藏变量,避免全局污染。保证数据的安全性数据私有化

解释:你第一次重写:将count变量放入函数中,成功使全局变量变为局部变量,外部不能随意修改,保证了数据的安全和私有化。

  • 2.函数变量可以保存在函数作用域内,变量在内存中不会被清除,延伸了变量的作用范围

解释:你第二次重写:为了保证count每次不被初始化,你利用内层函数可以访问外层函数变量的特点,创建内层函数,成功将count保存在外层函数作用域中,并且count的值也成功保存下来。

  • 3.可以读取函数内部的变量,方便调用上下文的局部变量。利于代码封装
const fn = outer(); //这里fn其实接受的时内层函数inner
fn(); //函数第1次调用
fn(); //函数第2次调用

解释:你把调用内层函数inner()的次数当作调用outer()的次数成功完成了需求。每次调用内层函数时,都可以读取到外层函数outer()中count的值,并成功打印在控制台上,成功完成函数的封装。

闭包的缺点

  • 函数执行完毕后,.闭包会常驻内存(比如函数内的局部变量),会增大内存使用量,闭包越大,造成内存消耗越大,导致内存泄露

解释:当我不想要这个需求了,我就不用调用闭包了,可是闭包依然存在并可以使用,一直在占着内存,无形地不断消耗着我们的内存空间。

解决方法:可以在使用完变量后手动为它赋值为null。例如count

  • 其次由于闭包涉及跨域访问(跨作用域),所以会导致性能损失。

解决方案:我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。

具体实现在下也不太清楚…

闭包的完美写法

这里,虽然你push到代码仓库的代码已经很完美了,但是作为一名合格的码农玩家,你必须精益求精!

function outer(){ //外层函数
    let count = 0; 
    return function(){ //内层函数 
        count++;
        console.log(`函数第${count}次调用`);
    } 
}
const fn = outer(); //这里fn其实接受的是内层函数
fn(); //函数第1次调用
fn(); //函数第2次调用

老板看了这么完美的代码,必须再加薪(实际老板看不懂)。

哈哈,以上情景完全是为了让大家更好地理解闭包哦!

最后,我简单一句话理解闭包:包裹的变量(纯属个人想法)

好啦,关于闭包的理解我就只能分享到这里了,再深入的我也不会啦。希望大家能认真读完,仔细思考…

关于闭包的讨论,我们来日再议…

大功告成,告辞!江湖有缘再见!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值