JavaScript 闭包

Posted sjpqy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript 闭包相关的知识,希望对你有一定的参考价值。

“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。在php、Scala、Scheme、Common Lisp、Smalltalk、Groovy、javascript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等语言中都能找到对闭包不同程度的支持。

函数作用域外调用函数作用域内变量的函数称为闭包。也就是其中一个函数是闭包。
严格意义上来讲,任意函数都可以称作为闭包。

 1 function B(){
 2     var i=0;
 3     return function(){
 4         alert(i++);
 5     }
 6 }
 7 
 8 var A=B();
 9 
10 //注意B();的返回值A();
11 
12 //0A();
13 
14 //1A是B函数作用外访问B函数作用内变量 i 的函数;

1、函数b嵌套在函数a内部;
2、函数a返回函数b。
这样在执行完var c=a( )后,变量c实际上是指向了函数b,再执行c( )后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b,就是说:
当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

简而言之,闭包的作用就是在a执行完并返回后,闭包使得Javascript的垃圾回收机制不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。这是对闭包作用的非常直白的描述,不专业也不严谨,但大概意思就是这样,理解闭包需要循序渐进的过程。
在上面的例子中,由于闭包的存在使得函数a返回后,a中的i始终存在,这样每次执行c(),i都是自加1后alert出i的值。
那 么我们来想象另一种情况,如果a返回的不是函数b,情况就完全不同了。因为a执行完后,b没有被返回给a的外界,只是被a所引用,而此时a也只会被b引 用,因此函数a和b互相引用但又不被外界打扰(被外界引用),函数a和b就会被回收。(关于Javascript的垃圾回收机制将在后面详细介绍)

B运行完毕后,等待javascript 垃圾回收就如同B运行完, 系统回收了笼子,小狗是不是 可以被释放了。 但此时系统发现A是一颗 大树,穿过笼子拴住了 小狗,系统无法顺利回收 笼子,小狗也被大树拴住 了,笼子不能被回收, 小狗也不能被释放。 要想回收笼子,释放小狗 就必须得先把大树毁掉,或者 解除绳索关联。

 1 function fun(n,o) {
 2   console.log(o)
 3   return {
 4     fun:function(m){
 5       return fun(m,n);
 6     }
 7   };
 8 }
 9 var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,?,?,?
10 var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
11 var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,?,?,?
12 //问:三行a,b,c的输出分别是什么?

1.使用闭包代替全局变量
2.函数外或在其他函数中访问某一函数内部的参数
3.在函数执行之前为要执行的函数提供具体参数
4.在函数执行之前为函数提供只有在函数执行或引用时才能知道的具体参数
5.为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点
6.暂停执行
7.包装相关功能

 1 function f1(){
 2     var test=111;
 3     tmp_test=function(){return test;} //tmp_test是全局变量,这里对test的引用,产生闭包
 4 }
 5 function f2(){
 6     alert("测试一:"+tmp_test());
 7     var test1=tmp_test();
 8     alert("测试二:"+test1);
 9 }
10 f1();//测试一:111
11 f2();//测试二:111
12 alert(tmp_test()); //111
13 tmp_test=null;

某些情况下,是无法为要执行的函数提供参数,只能在函数执行之前,提前提供参数。
有哪些情况是延迟执行?
如:
setTimeOut 
setInterval
Ajax callbacks
event handler[el.onclick=func 、 el.attachEvent("onclick",func)]

 1 //无法传参的情况
 2 var parm=222;
 3 function f1(){alert(111)}
 4 function f2(obj){alert(obj)}
 5 setTimeout(f1,500);//正确,无参数
 6 var test1=f2(parm);//执行一次f2函数
 7 setTimeout(f2,500);//undefined,传参失败
 8 setTimeout(f2(parm),500);//参数无效,传参失败
 9 setTimeout(function(parm){alert(parm)},500);//undefined,传参失败
10 document.getElementById("hello").onclick=f1;//正确
11 document.getElementById("hello").attachEvent("onclick",f1);//正确
12 //正确做法,使用闭包
13 function f3(obj){return function(){alert(obj)}}
14 var test2=f3(parm);//返回f3的内部函数的引用
15 setTimeout(test2,500);//正确,222
16 document.getElementById("hello").onclick=test2;//正确,222
17 document.getElementById("hello").attachEvent("onclick",test2);//正确,222

 

以上是关于JavaScript 闭包的主要内容,如果未能解决你的问题,请参考以下文章

Spark闭包与序列化

JavaScript ---- 闭包(什么是闭包,为什么使用闭包,闭包的作用)

JavaScript 闭包(随笔)

javascript中的闭包

Javascript中的闭包

Javascript中的闭包(转载)