Javascript闭包(Closure)详解
来源:希尔网点击数:

先看下官方的解释:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
很是晦涩,那么我们再看另外一种解释:
JavaScript闭包就是在另一个作用域中保存了一份它从上一级函数或者作用域得到的变量,而这些变量是不会随上一级函数的执行完成而销毁。
似乎抓住了什么,别着急,更进一步:
闭包就是在函数内对外部作用域上的变量引用,使其常驻内存中,得不到释放。(当然这只是闭包的表现)
1、先看一段代码

复制代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

function funcTest(){

var tmpNum=100; //私有变量

//在函数funcTest内

//定义另外的函数作为funcTest的方法函数

function innerFuncTest(

{

alert(tmpNum);

//引用外层函数funcTest的临时变量tmpNum

}

return innerFuncTest; //返回内部函数

}

//调用函数

var myFuncTest=funcTest();

myFuncTest();//弹出100

上面代码中,注释已经写的清清楚楚。现在我们可以这么理解“闭包”:在函数体内定义另外的函数作为目标对象的方法函数(示例中就是在函数funcTest内定义另外的函数innerFuncTest作为funcTest的方法函数),而这个对象的方法函数反过来引用外层函数体中的临时变量(闭包是一种间接保持变量值的机制。示例中就是内部函数innerFuncTest引用外层函数funcTest的临时变量tmpNum,这里必须注意,临时变量可以包括外部函数中声明的所有局部变量、参数和声明的其他内部函数)。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包(示例中,调用函数的时候,myFuncTest实际调用的是innerFuncTest函数,也就是说funcTest的一个内部函数innerFuncTest在funcTest之外被调用,这时就创建了一个闭包)。
2、闭包的例子
<a href="#">点我测试</a>
<a href="#">点我测试</a>
<a href="#">点我测试</a>
<a href="#">点我测试</a>
(1)、因闭包而导致问题
上面的HTML标记片断中有4个<a>元素,现在要给后三个指定事件处理程序,使它们在用户单击时报告自己在页面中的顺序,比如:当用户单击第2个链接时,报告“您单击的是第1个链接”。为此,如果编写下列为后三个链接添加事件处理程序的函数:
复制代码

1

2

3

4

5

6

7

8

9

function badClosureExample(){

var as = document.querySelectorAll('a');

for (var i = 0; i <4; i++) {

as<i>.onclick = function(){

alert('单击第' + i + '个');

}

}

}

badClosureExample();</i>

看一下运行结果,此时单击任意一个链接,会看到警告框中显示什么信息呢?——全都是“您单击的是第4个链接”。是不是令你感到十分意外?为什么?
分析:因为在badClosureExample()函数中指定给element.onclick的事件处理程序,也就是onclick那个匿名函数是在badClosureExample()函数运行完成后(用户单击链接时)才被调用的。而调用时,需要对变量i求值,解析程序首先会在事件处理程序内部查找,但i没有定义。然后,又到 badClosureExample()函数中查找,此时有定义,但i的值是4(只有i大于4才会停止执行for循环)。因此,就会取得该值——这正是闭包(匿名函数)要使用其外部函(badClosureExample)作用域中变量的结果。而且,这也是由于匿名函数本身无法传递参数(故而无法维护自己的作用域)造成的。
那么这个例子的问题怎么解决呢?其实方法有很多(自己不妨写一下看看),我认为比较简单直接的代码:

复制代码

1

2

3

4

5

6

7

8

9

10

11

12

function popNum(oNum){

return function(){

alert('单击第'+oNum+'个');

}

}

function badClosureExample(){

var as = document.querySelectorAll('a');

for (var i = 0; i <4; i++) {

as<i>.onclick =new popNum(i);

}

}

badClosureExample()</i>

(2)、巧妙利用闭包绑定参数
还是上面的HTML片段

复制代码

1

2

3

4

5

6

7

8

9

10

11

function badClosureExample(){

var as = document.querySelectorAll('a');

for (var i = 0; i <4; i++) {

(function(i){

as<i>.onclick = function(){

alert('单击第' + i + '个');

}

})(i);

}

}

badClosureExample();</i>

3、javascript的垃圾回收原理
(1)、在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
(2)、如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。在js中使用闭包,往往会给javascript的垃圾回收器制造难题。尤其是遇到对象间复杂的循环引用时,垃圾回收的判断逻辑非常复杂,搞不好就有内存泄漏的危险。
其实,闭包就是读取scope罢了。

发表评论

请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。

条评论