合肥千峰前端培训---javascript闭包详解

什么叫闭包
  • 闭包是函数的一种高级应用方式
那要怎么运用它呢
  • 要想形成闭包要有三个特点
    • 函数A内部直接或者间接返回一个函数B
    • 函数B内部使用函数A的私有变量
    • 函数A外面有变量接收函数B
  • 这个时候就形成了闭包,函数A的执行空间就叫做闭包空间,函数B就叫做函数A的闭包函数(刚刚说到的“直接或间接返回函数”、“执行空间”可能不好理解,我们下面来说)
什么是执行空间
  • 函数在调用的时候,在内存上开辟的一个用来执行代码的空间
  • 我们知道,函数有两个阶段,定义阶段和调用阶段;①定义阶段,由于函数是复杂(引用)数据类型,所以计算机会把函数代码保存在堆上面。计算机会在堆上面开辟一个函数空间,然后以字符串的形式将函数代码保存在这开辟的函数空间上;然后将这个函数空间的地址赋值给函数名,这函数名就保存在栈上面②调用阶段,语法:函数名(),计算机通过函数名来找到对应的函数空间,然后在内存上开辟一个执行空间,在这个空间里面进行形参赋值,变量预解析,将函数空间里面的代码复制一份过来执行,代码执行完,这个执行空间就销毁了。下次再调用这个函数的时候,就再开辟一个执行空间,一样的步骤 =>找到函数存储空间 =>形参赋值=>变量预解析 =>存储存储空间里的代码执行完毕 =>然后执行空间销毁
  • 不会销毁的执行空间
    • 刚刚说了,代码执行完毕,执行空间也就销毁了,那有没有执行空间不销毁的时候呢,有,但必须要满足两个条件
    • 两个条件:
      1. 函数里面返回一个复杂数据类型(对象,数组,函数……)
      2. 外面有个变量来引用里面的复杂数据类型
    • 举个例子
    function a(){
    	// 函数里面返回了一个复杂数据类型
    	return [1,2,3]
    }
    // 函数调用时,外面有个变量t来接收它
    var t = a()
    // 这个时候,调用a函数开辟的执行空间就不会销毁了,会一直占着内存
    
    t = null
    //当t被重新赋值时,不再指向那个a函数返回的复杂数据类型(我们知道复杂数据是保存在堆上面,赋值时,是传递的地址),那个开辟的执行空间才会销毁
    
什么叫直接或间接返回一个函数?
  • 直接返回一个函数,就是return 后面直接跟个函数
function a(){
	return function(){}
}
function b(){
	var t = function(){}
	return t
}
  • 间接返回一个函数,return后面跟一个对象或数组,这个对象或数组里面有一个函数
function a(){
	var fn = function(){}
	return [function(){},fn,……]
}
function b(){
	var a = 3
	var b = 4
	return {
		getNum1 : function(){console.log(a)},
		getNum2 : function(){console.log(b)},
		.
		.
		.
	}
}
  • 那么我们什么时候间接的返回函数呢?
    1. 当我么需要访问函数里多个私有变量时,就像上面的b函数
    2. 返回一个对象,对象里面有多个闭包函数,通常使用对象,因为对象的属性比数组的索引要强一点
闭包的特点
  • 作用
    1. 可以延长变量的生命周期
    2. 可以访问函数的私有变量
    3. 保护函数的私有变量
  • 缺点
    1. 因为执行空间不销毁,所以会一直占用着内存空间,当占用内存过多时,会引发内存溢出,最终会内存泄露
什么时候使用闭包
  1. 当你需要延长变量的生命周期的时候
  2. 当你需要访问函数内部的私有变量的时候
闭包的应用
  • 我们以前在写选项卡的时候,通过for循环给每个选项绑定一个点击事件,想要在事件函数里面,获得当前选项的索引就不好办了,因为for循环只是绑定事件,事件还没执行呢,我们是想要在执行时候获取当前索引,我们当时有三个解决方法(设置自定义属性、给元素对象添加个属性、用let声明变量)
// 获取页面元素
var aBtn = document.querySelectorAll('div')
for (var i = 0 ; i < aBtn.length ; i++){
	btn[i].onclick = function(){
		console.log(i)
		//当我们点击的时候,会都输出i (=== aBtn.length),因为循环只是进行了事件绑定,还没执行事件函数呢,等循环结束i就变成了aBtn.length
	}
}
  1. 给元素设置一个自定义属性或者给元素对象添加个属性
var aBtn = document.querySelectorAll('div')
for (var i = 0 ; i < aBtn.length ; i++){
	// 1给元素对象设置属性
	btn[i].index = i
	// 2给元素设置自定义属性
	btn[i].setAttribute('index',i)
	btn[i].onclick = function(){
		// 因为这是个事件绑定函数,this指向.前面的;即当i =0 时,this指向btn[0];当i =1 时,this指向btn[1]...
		console.log(this.index)
		
		console.log(this.getAttribute('index'))
	}
}
  1. 用let来声明变量
var aBtn = document.querySelectorAll('div')
for (let i = 0 ; i < aBtn.length ; i++){
	btn[i].onclick = function(){
		console.log(i)
	}
}
  • let是ES6提供的定义变量的关键字,下面运用闭包来实现let的作用原理
var aBtn = document.querySelectorAll('div')

function loop(index){
	// function(){} 是个事件函数,执行代码时会在内存里面开辟个函数空间用来保存代码,把这个函数的地址赋值给了btn[index].onclick
	// 这一个个闭包空间(也就是loop(i)的执行空间)就不会销毁,这一个个事件函数就是闭包函数
	// function(){}用到的index是通过形参传过来的,也算函数loop内的变量
	
	btn[index].onclick = function(){
		console.log(index)
	}
}

for (var i = 0 ; i < aBtn.length ; i++){
	//每次循环,调用loop函数,并开启一个个执行空间
	loop(i)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值