对于JSer来说,闭包是一个难懂,但必须征服的概念,今天我们就来谈谈这个闭包…
1. 变量的作用域 在函数内部生命的局部变量在函数外面是访问不到的
1 2 3 4 5 6 var func = function ( ) { var a = 1 ; console .log(a) }; func(); console .log(a)
函数内部的变量在函数体内找不到时,就会沿着作用域链
往外层搜索
1 2 3 4 5 6 7 8 9 10 11 12 var b = 1 ;var func1 = function ( ) { var c = 2 ; var func2 = function ( ) { var d = 3 ; console .log( c ); console .log( b ); }; func2(); console .log( d ) }; func1();
2. 变量的生存周期 对于在函数内用var声明的变量来说,退出函数时,这些局部变量就会被销毁,
1 2 3 4 5 var func3 = function ( ) { var e = 2 ; alert(2 ); }; func3();
然而对于闭包来说
1 2 3 4 5 6 7 8 9 10 11 var func4 = function ( ) { var a = 1 ; return function ( ) { a++; console .log(a); } }; var f = func4();f(); f(); f();
Why? 这是因为当执行var f = func4();时,f返回了一个匿名函数的引用,它可以访问到func4()被调用时产生的环境
,而局部变量一直处于这个环境中,既然局部变量所在的环境还能被外界访问,所以就有了不被销毁的理由
接下来我们来看一个闭包的经典应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!DOCTYPE html> <html > <head > <meta charset ="utf-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <title > </title > <link rel ="stylesheet" href ="" > </head > <body > <div > 1</div > <div > 2</div > <div > 3</div > <div > 4</div > <div > 5</div > </body > </html >
1 2 3 4 5 6 7 <!-- js --> var nodes = document .getElementsByTagName('div' );for (var i=0 ,l=nodes.length;i<l;i++){ nodes[i].onclick = function ( ) { console .log(i) } };
上面这个例子不管你点击哪个,它弹出的都是5,这是因为div节点的onclick事件是异步触发
的,当事件触发时,循环早已结束,此时的i就是5,那么如何来修改呢?看以下两种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!-- 方法一 --> var nodes = document .getElementsByTagName('div' );for (var i=0 ,l=nodes.length;i<l;i++){ (function (i ) { nodes[i].onclick = function ( ) { console .log(i) } })(i) }; var nodes = document.getElementsByTagName('div'); for(var i=0,l=nodes.length;i<l;i++){ nodes [i ].onclick = (function (i ){ return function (){ console.log (i ) } })(i ) }
1 2 3 4 5 6 7 8 var mult = function ( ) { var a = 1 ; for (var i=0 ,l=arguments .length;i<l;i++){ a *=arguments [i] } return a; }; console .log( mult(2 ,3 ,4 ) )
上面的方法我们还可以加入缓存机制
,这样重复的就不用在计算了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var mult = (function ( ) { var cache = []; var calculate = function ( ) { console .log(arguments ) var a = 1 ; for (var i=0 ,l=arguments .length;i<l;i++){ a *=arguments [i] } return a; }; return function ( ) { var arg = [].join.call(arguments ,',' ); if (cache[arg]){ return cache[arg] } return cache[arg] = calculate.apply(null ,arguments ) } })() console .log( mult(2 ,3 ,4 ) );
1 2 3 4 5 var report = function (src ) { var img = new Image(); img.src = src; } report('http://7xqjce.com1.z0.glb.clouddn.com/weixin.jpeg' )
如上情况,图片数据会丢失,因为并非每个http请求都是成功的,,又因为img是局部变量
,当函数调用完后,img就会销毁
,此时数据还没有传输完整 使用闭包修正如下:
1 2 3 4 5 6 7 8 9 var report = (function ( ) { var imgs = []; return function (src ) { var img = new Image(); imgs.push( img ) img.src = src; } })(); report('http://7xqjce.com1.z0.glb.clouddn.com/weixin.jpeg' )
3>. 闭包与面向对象设计
对象以方法的方式包含了过程,而闭包在过程中以环境形式包含了数据,通常用面向对象能实现的功能,用闭包也能实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!-- 闭包形式 --> var extent = function ( ) { var val = 0 ; return { call : function ( ) { val ++ console .log(val) } } }; var extent = extent();extent.call(); extent.call(); extent.call();
1 2 3 4 5 6 7 8 9 10 11 <!-- 面向对象形式 --> var extent = { val : 0 , call :function ( ) { this .val++; console .log(this .val) } }; extent.call(); extent.call(); extent.call();
<
续谈闭包
call AND apply
>