js循环中的闭包详解

函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性就叫做闭包——《javascript权威指南》。

    各大书籍中对于闭包的解释都有些晦涩难懂。而在我搜索过很多技术博客对于闭包的解释,对一些新手来说,理解起来似乎还是不太友好,所以今天写这篇博客,希望能让大家对闭包有个更深刻的认识。如有解释不当之处,烦请评论区指正。

    打算要理解js的闭包,首先我们要先知道闭包的一些特性

(1) 闭包是指有权访问打另一个函数作用域中的变量的函数。(这与当函数被调用是会创建一个执行函数和相应的作用域链。作用域链本质上是指向变量对象的指针列表,只引用,不实际包含变量对象)。

(2) 封闭性:外界无法访问闭包内部的数据,在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口; 持久性:一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,由于闭包结构依然保存在,所以函数内部变量无法被垃圾回收机制销毁。

    再此,使用一个经典的闭包面试题来解释闭包(循环中的闭包):

<script>
    for(var i=0;i<5;i++){
		setTimeout(function(){
			console.log(i);//再此会输出五次5
		},1000);
	}//此段代码不为闭包,此时console.log(i)为5
</script>

    最初我看到这段代码时,对于在控制台输出的是5次5非常迷惑,想必一些初学者应该跟我有差不多的体会。那么,要想理解接下来的闭包是如何解决连续输出5次5这个问题,我认为我们应该首先明白这里输出5次5的原因。这个原因就在于,我们都知道settimeout这个函数是个异步函数,那么当for循环执行第一次的时候,遇到setTimeout函数,会把setTimeout函数放入事件队列中(不知道事件队列的同学暂时把事件队列理解为一个数组,循环时遇到setTimeout,就把function(){console.log(i)}这个函数push到一个数组中去),而不是执行它当五次for循环结束之后我们可以认为事件队列里边已经放了五个function(){console,log(i)}函数,接着一秒钟以后,在事件队列中的函数依次执行,一共执行五次,而此时因为for循环过后我们的i这个变量已经是5了,所以会连续输出5次5。那么知道原因,我们就可以说一下如何使用闭包的特性来解决这个问题了。

    当外部函数运行结束,由于内部函数的变量对外部函数的变量有一个引用,从而使外部函数运行结束后,被引用的变量无法被回收。此时变形成了闭包。我们来看通过闭包解决连续输出5次5的代码

for(var i=0;i<5;i++){
		(function(i){ //若在此处console.log(i)会分别输出0,1,2,3,4,settimeout函数内的console.log(i)分别在这五个闭包内,所以,会输出正常值
			setTimeout(function(){
				console.log(i);
			},1000);
		})(i);
	}
	console.log(i)

这段代码,会先输出一次5,然后在输出0,1,2,3,4。这就是神奇的闭包,我们可以看到,立即执行函数我们把i作为参数传进去,由于立即执行函数执行结束过后,setTimeout函数中的console.log(i)对当前函数(也就是当前的这个立即执行函数的变量i)有引用,所以说变量i无法被销毁。由于for循环五次,建立了五个匿名自执行函数,所以就建立了五个闭包。

    闭包最难理解一个地方我认为就是他的持久性,这段代码仅能从一个方面来反映闭包的特性,我们会在下一篇博客在使用更为简单理解的案例,再次来解释一下闭包。

注:以上说明仅为博主个人观点,如有不当之处还请指正

       本篇闭包特性引用博主@公子小苏,对闭包特性的解释。如有不当,请联系我删除内容。



猜你喜欢

转载自blog.csdn.net/badmoonc/article/details/79919860
今日推荐