深究 JavaScript 中闭包的概念

深究 JavaScript 中闭包的概念

闭包是js的一个难点,也是它的一大特色,是我们必须掌握的js高级特性之一,那么什么是闭包呢?它又有什么用呢?在网上翻阅资料后,给我的感觉是,基本上大致都是一个意思,不难理解。那么接下来,博主将使用简洁并且形象的方式给大家来解释。

1、什么是闭包?

术语:闭包(closure)指有权访问另一个函数作用域变量的函数。我们可以理解为,一个作用域可以访问另一个函数作用域的局部变量。例如在全局作用域下访问局部作用域下的变量,又或者两个不同的局部作用域之间进行访问局部变量,在这种情况下我们不得不使用闭包函数。

形象比喻:通俗地讲就是别人家有某个东西,你想拿到但是因为和他家里人不熟,但是你可以和他家的孩子套近乎,通过他家的孩子拿到你想要的东西。

​ 这个家就是局部作用域,而你不是他家的人,所以外部无法访问内部的变量,而他家的孩子是返回对象,对家里的东西有权访问,那么我们就可以借助返回对象间接访问内部的变量。

总结:[函数] 和 [函数内部能访问到的变量](也叫环境)的总和,就是一个闭包。

2、闭包的特点

​ 通过上面形象而又不失节操的比喻,相信大家对闭包都有一定的了解了,那么闭包有什么特点或者闭包适应的环境是什么呢?

  • 在函数嵌套中发生
  • 内部函数可以引用外部函数的参数或者变量
  • 参数和变量不会被垃圾回收机制回收,因为内部函数还在引用
  • 函数作为返回值被return返回
  • 闭包函数可以作为参数传递

2.1 闭包经典案例

下面我们看一个闭包函数的经典案例,来加固大家对闭包概念的深刻理解,我们使用简单的函数来实现。

// 声明一个普通的函数
function ext(){
  // 声明一个局部变量,并赋值为1
	var a= 1;
  // 返回一个匿名函数
	return function(){
    // 内部的函数对外部的局部变量进行修改
		a++;
    // 打印到控制台
		console.log(a);
	}
}
// 调用ext函数,并返回一个匿名函数,使用变量接收
let inter = ext();
inter(); // 输出2
inter(); // 输出3
// 当然,我们还可以在内部函数中将a变量返回,使得全局变量可以对其进行直接的使用

分析:

一般情况下,函数ext执行完后,就应该连同函数体内部的局部变量一天被回收,但在这个例子中。匿名函数作为ext函数的返回值被赋值给全局作用域下的inter变量,并且匿名函数内部引用着ext函数中的变量a,导致变量a无法被回收,并且调用匿名函数后还进行了累加。那么这就形成了闭包,并且也显示出了闭包的不足,即存在内存消耗的问题

2.2 计时器经典面试题

for(var i=0;i<5;i++){
	setTimeout(function(){
		console.log(i)
	},100)
}
// 输出5个5

分析:

按照预期,它应该依次打印12345,但结果却输出了5个5,这是什么原因引起的呢?首先我们要知道js的执行机制,js是单线程的,所以会有异步任务和同步任务,同步任务先执行,而异步任务后执行。而异步任务包含计时器事件,那么也就意味for循环要比计时器先执行完成,然后再执行计时器任务。所以按照这个逻辑分析,当for循环执行完毕后局部变量i变成了5,然后计数器才执行5次,所以会输出5次5.

但是如果将变量声明关键字var换成let则会得到我们预取的效果,因为let会将当前的模块暂时性锁死,所以不会存在 var变量提升的问题。

注意:其实这个案例最重要的是涉及到了变量声明和变量提升的问题,而并非闭包。

2.3、闭包作为参数传递

var num = 15;
var fn1 = function(x){
    if(x>num){
      console.log(x);
    }
}
void function(fn2){
    var num =100;
    fn2(30);
}(fn1);

分析

在这段代码中,函数fn1作为参数传入立即执行函数中,在执行到fn2(30)的时候,30作为参数传入fn1中,这时候if(x>num)中的num取的并不是立即执行函数中的num,而是取创建函数的作用域中的num这里函数创建的作用域是全局作用域下,所以num取的是全局作用域中的值15,即30>15,打印30

3、闭包的优缺点

优点

  1. 保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突
  2. 在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)
  3. 匿名自执行函数可以减少内存消耗

缺点

  1. 被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏

    解决:可以在使用完变量后手动为它赋值为null;

  2. 由于闭包涉及跨域访问,所以会导致性能损失

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

4、总结

闭包总的来说不是太难,只要大家能明白作用域、变量提升、js的执行机制,我相信学习闭包是非常简单并且容易上手的。最后给大家说一下,void是js中的一个操作符,表示该函数没有返回值,或者说返回值为undefined

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御弟謌謌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值