js中闭包的理解

闭包(closure)是js语言的一个难点,也是一大特色,很多高级应用都需要依靠闭包来实现;
《JavaScript高级程序设计》上对闭包的定义是:有权访问另一个函数作用域中的变量的函数。那么在js中如何才能访问另一个函数内部的变量呢?
这里首先要了解两个问题:变量的作用域(链式作用域) 和 垃圾回收机制。
1)在js中变量的作用域无非就是两种:局部作用域 和 全局作用域;
我们知道,在js函数内部可以直接读取全局变量,但是在正常情况下,函数外却没有办法访问其内部的变量;
那么,这里如何才能访问到其函数内部的变量呢?先来看下面一段代码
function outterFn(){
i = 10;
function innerFn(){
alert(i);//10
}
}
通过观察上面的代码,我在outterFn函数内部,又定义了一个新的内部函数innerFn,那么此时,外部函数outter内所有的变量,都是对内部函数
innerFn可见;但是反过来就不可以;这就是JavaScript语言特有的“链式作用域”结构。
那么总上所述,既然innerFn可以读取outterFn中的局部变量,那么只要把innerFn作为返回值,就可以在outterFn外部读取它的内部变量了!!
代码如下:
function outterFn(){
i = 10;
function innerFn(){//此时innerFn就是一个闭包函数
alert(i);
}
return innerFn;//返回的是一个函数(将闭包函数返回)
}

var result = outterFn();//执行outterFn()后的返回值,赋给变量result,那么此时这个result就是innerFn,也就是所谓的闭包函数
result();//执行闭包函数 页面弹出结果 10

这里,函数outterFn中的变量只能通过函数innerFn可以访问,而无法通过其它途径访问到,这样就保护了变量i的安全性;
根据上面说法,我们可以通过闭包,实现js中对象属性和方法的私有化(不能被外部访问)
提示:创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量

2) js中的垃圾回收机制
和Java一样,javascript同样具有垃圾回收机制,释放不需要的内存;例如:在一般情况下,函数调用结束,函数内的局部变量销毁,程序就
会自动启动垃圾回收机制来回收销毁的变量释放空间。
在JavaScript中,如果一个对象不再被引用,那么这个对象也就会被垃圾回收器(GC)回收;如果两个对象相互引用,而不在被第三者引用,那
么这两个相互引用的对象也会被回收;
所以闭包基于正常的垃圾回收机制,利用全局变量也就是第三者的引用,巧妙地让局部作用域的变量,在函数执行完毕之后依旧保存且不被垃圾回
收器处理;也就是说,闭包使局部作用域的变量常驻内存,这样会增大内存的使用量,使用不当容易造成内存泄漏。

使用闭包主要是为了设计私有的方法和变量。
闭包的优点是可以避免全局变量的污染(全局变量冲突)
理解Javascript的闭包是迈向高级JS程序员的必经之路,理解了其解释和运行机制才能写出更为安全和优雅的代码。

拓展:关于全局变量污染
JavaScript可以随意定义保存所有应用资源的全局变量,但全局变量却可以削弱程序的灵活性,增大模块之间的耦合性;
在多人协作时,如果定义过多的全局变量,有可能造成全局变量冲突,也就是全局变量污染问题

闭包在循环中的应用:
需求:页面中有10条列表新闻数据,当点击某一条数据时,对应的弹出“第几条新闻被点击了”,根据自己思路编写查看效果,发现不是自己想要的结果,原因是什么呢?
解析:for循环在当前作用域执行后就全部执行完毕,但是事件不同,它需要点击才能触发,这个时候i的值执行完循环,在条件的基础上+1了,如条件表达式位i < 10,
执行完循环后,i的值变成了10,循环不再执行,i的值也就固定了,无法改变了;那么如何保存这个循环的下标呢?
1. 基础的办法就是给元素节点自定义属性,来接收对应的循环下标
var lis = document.querySelectorAll('li');
for(var i = 0; i < lis.length; i++){
lis[i].index = i;
lis[i].onclick = function(){
//console.log("第"+(this.index+1)+"条新闻被点击到了")
alert("第"+(this.index+1)+"条新闻被点击到了") //此处this指代当前的事件对象
}
}
2. 使用闭包
var lis = document.querySelectorAll('li');
for(var i = 0; i < lis.length; i++){
(function(j){
lis[j].onclick = function(){
//console.log("第"+(j+1)+"条新闻被点击到了")
alert("第"+(j+1)+"条新闻被点击到了")
}
})(i)
}

解析:for循环每一次都执行一个IIFE(IIFE的全称是Immediately-invoked Function Expression,立即执行函数表达式),
每一次变量i被当作参数传入到IIFE中,那么这个自执行函数中创建了一个变量,也就是形参j,然后元素节点lis绑定一个onclick
事件,立即执行函数里面又需要用到这个参数j,但是并没有点击,那么这个遍历j也就没有被清理,一致在参数里面被保存者,每一个
立即执行函数(IIEF)都做一样的事情,所以这个时候就产生了闭包,而变量j没有被回收,也就可以拿过来使用。































猜你喜欢

转载自www.cnblogs.com/menglong1214/p/9847660.html