JavaScript|深入理解闭包

闭包是面试和开发中必定会用到的一个。

定义

有两个常见的理解方式,我个人倾向第一个理解方式,最好两个理解都掌握。
理解一:闭包就是能够读取其他函数内部变量的函数。
理解二:定义在一个函数内部的函数(多数人的理解)

本质上,闭包是将函数内部和函数外部连接起来的桥梁。

产生闭包的条件:
函数嵌套、内部函数引用了外部函数的数据(变量/函数)。

常见闭包

将函数作为另一个函数的返回值

  function f1(){
    var n=999;
    nAdd=function(){n+=1} 
    //f1执行时,将nadd定义到全局中nadd,
    //nadd引用了匿名函数中所指向的对象,所以f1函数执行完没有被回收,
    //可以在全局中直接使用nadd函数调用(读写)
    function f2(){
      alert(n);
    }
    
     function f3(){
      alert(n);
    }
    return f2;
  } // f1 结束符 
  
  var result=f1(); // f1只被执行一次,所有f2只被定义一次,返回了f2函数,调用result相当于调用f2函数
  result(); // 999
  nAdd();
  result(); // 1000

在这段代码中,result实际上指向闭包f2函数在堆中所指向的对象。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

原因就在于f1是f2的父函数,而f2函数被赋给了一个全局变量,这导致f2所指向的对象始终在(堆)内存中,但是在中的变量f2会在f1执行完后被回收,而f2的存在依赖于f1,因此f1指向的对象也始终在(堆)内存中,不会在调用结束后,被垃圾回收机制回收。其中f3函数指向的对象因为没有被引用,所以f1执行完后会被回收。

注意:
在函数中的变量如果没有使用 var 定义,就变成了全局变量。其中的nAdd就是。

将函数作为实参传递给另一个函数调用

function showDelay(msg, time){
	setTimeout(function(){
		alert(msg) // 闭包
	}, time)
}

showDelay("kkkk", 2000)

闭包的用途

  1. 使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
  2. 让函数外部可以操作(读写)到函数内部的数据(变量、函数)

注意点:

  1. 函数执行完后,函数内部声明堆局部变量一般时不存在的,存在于闭包中的变量才有可能存在。
  2. 在函数外部不能直接访问函数内部的局部变量,需要使用闭包来让外部操作。

闭包的生命周期

闭包创建于定义函数的时期,两种情况:

  1. 因为函数声明提升,在执行代码前的预处理时创建闭包
function fun2(){

}
  1. 执行使用表达式的那一行代码时创建
nadd = function(){

}

闭包结束,采用垃圾回收,使用null操作。

闭包应用

JS模块

定义:具有特定功能的JS文件。
将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包含n个方法的函数或对象。模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能。

例子:
model.js

functino myModel(){
	var msg = 'kk'
	functino showUpCase(){
		
	}
	function showLowerCase(){
	
	}
	return { //返回对象方便调用
		showUpCase:showUpCase
		showLowerCase:showLowerCase
	}
}

返回含有方法的对象,方便调用这个js模块。(记得使用script标签将其引入)
调用时只需要调用这个myModel函数

var md = myModel()
md.showUpCase()
md.showLowerCase()

model2.js 使用匿名函数方法,引入后可以直接调用。

(functino myModel(window){ //形参命名为window是为了方便代码压缩
	var msg = 'kk'
	functino showUpCase(){
		console.log(msg)
	}
	function showLowerCase(){
		console.log(msg)
	}
	window.myModel2 =  { //给window添加一个属性方便调用
		showUpCase:showUpCase
		showLowerCase:showLowerCase
	}
}
)(window) // 传入实参window

因为已经添加为window的方法,所有可以直接调用。

myModel2.showUpCase()
myModel2.showLowerCase()

闭包的缺点

函数执行完后,函数内的局部变量没有释放,占用内存时间变长,容易造成内存泄露。
解决:能不用闭包就不用闭包,及时释放。

通过实例加深对闭包的了解

var name ="the window"
var object1 = {
	name:"object1"
	getNameFunc:function(){
		return function(){
			return this.name
		}
	}
}
object1.getNameFunc()() // 输出the window 

上面的代码没有使用闭包,因为object1.getNameFunc()只是返回了一个匿名函数代码,通过再添加一个括号来调用。全局变量相当于window的属性,所以this指向的是window。

var name ="the window"
var object2 = {
	name:"object2"
	getNameFunc:function(){
		var that = this
		return function(){
			return that.name
		}
	}
}
object2.getNameFunc()() //输出object2

上面的代码有使用闭包,通过一个局部变量that(不是关键字于this不一样)将object2传进去,在返回的匿名函数中调用了其他函数的变量that。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值