JS-闭包详解
来源:互联网 发布:java将字符串写入文件 编辑:程序博客网 时间:2024/06/05 07:44
闭包是JS中的一块硬骨头-不好啃,需要我们慢慢去咀嚼才能体会出其中的味道,嚼的不好,一不小心还容易崩牙!今天就让我们好好来咀嚼下这块硬骨头把!!!
1.什么是闭包?
所谓闭包:在一个函数作用域中 保留 它上级作用域的局部变量,这些局部变量不会随着上级函数的执行完毕而被销毁(个人理解)。
简单的理解闭包:子函数可以使用父函数的局部变量(包括参数,因为参数也是局部变量);
function test() { var i=0; function b() { alert(++i); } return b; }var c= test();c();
闭包是和JS的垃圾回收机制(以下简写GC)相关的,正常来说在test()函数调用完毕后,为局部变量i分配的空间就会被GC回收了. 但是这里在test函数里又定义了一个子函数b,并且在函数b的里面用到了 上级函数的局部变量i,然后在test()里return b. 这样一来就会导致:
当test()函数调用完毕,为局部变量i分配的空间也不会被GC收回。
所以在var c= test();这句执行完毕后,调用c()依然可以访问到局部变量i,所以就弹出了1.
function parent(num1) { var num2= 10; return function child() { alert(num1+num2); }}var fun = parent(10);fun();
在parent函数内部返回值也是一个函数,在return的这个child()函数里,用到了parent函数的参数num1和局部变量num2,因为形成了闭包,所以调用fun()的时候就可以访问到num1和num2.所以就弹出20了.
接着看一个稍微复杂的例子:
var name = 'zj';var obj = { name:'z3', getName:function () { return function () { return this.name; } }}alert(obj.getName()());// 弹出zj
为什么会弹出zj那?让我们一点点来看:
首选把alert里内容改成alert(obj.getName);看一下
直接alert(obj.getName)就相当于调用obj这个对象下的getName属性,弹出getName这个属性的值,所以弹出是一大段代码接着改写成alert(obj.getName( ) ),看下结果
直接alert(obj.getName( ) )就相当于直接执下面的代码:
function () { return function () { return this.name;}}
而执行这个函数执行之后返回值又是一个函数,所以alert(obj.getName( ) )还是一段函数代码
function () {return this.name;}接着把alert(obj.getName( ) )改成alert(obj.getName( ) ( )) ,到这里就相当于直接运行
function () { return this.name; }这段代码,此时的this代表的就是window这个对象,所以return this.name 就相当于return window.name ,所以就弹出zj了,到这里有没有看明白
其实alert(obj.getName( ) ( ))这种写法和前面的用一个变量来接受的写法是等价的,同样我们也可以用个变量来接受
var n = obj.getName();alert(n());
这样弹出的结果也是zj
2. 怎么样才能形成闭包?
在有函数嵌套的情况下,子函数用到了父函数的参数或者局部变量,一般都会形成闭包。
3. 闭包有什么好处?
好处1:具有封闭性,可以起到保护变量的作用
// 1级作用域function f(x){ //2级作用域var temp = x;return function(x){//3级作用域 temp += x; alert(temp); }}var a = f(50);a(5); // 55a(10);
我们在二级作用域里定义了一个局部变量temp,然后再三级作用域里用到了这个变量,这里就形成了闭包,把temp这个变量保护起来,不被GC所回收,方便在以后再使用temp这个变量。
当执行完这句话 var a = f(50);temp=50并且temp变量的值被保存了起来,接着调用a(5)此时temp的值就变成了55,所以会弹出55。完a(5)之后,temp里保存的值是55.接着a(10).所以弹出65。
好处2 :避免全局变量的污染
var a = 1;function test(){a++;alert(a);}test(); // 调用一次a就加1test();此时的a是全局变量,程序当中经可能的避免全局变量,此时把a提到内部,变成局部变量看一下
function test(){var a = 1;a++;alert(a);}test(); //2test();//2
我们发现不管调多少次结果都是2.这也是因为每调用一次test();都会重新定义变量a = 1,然后累加一次变成2,然后弹出就结束了;再调用test() 还是重新定义变量a = 1...,所以永远是弹出2;
这和js的GC有关,当函数test()执行完毕后,GC就会把为test的局部变量分配的空间收回,所以局部变量就不存在了;因为每次调用test()都是重新为局部变量分配空间;
这里就可以通过闭包就可以实现:test()调用完毕后不让GC把局部变量的空间收回,同时又避免了全局变量污染
function test(){ var a = 1; function b(){ a++; alert(a); } return b;}var c = test(); //2c();//2c();c();
再来看一个例子:
<ul> <li>000</li> <li>111</li> <li>222</li> <li>333</li> </ul>
有4个li,当点击第一个li弹出0,点击第二个弹出1,点第三个弹出2,点第四个弹出3,
<script> var aLi = document.getElementsByTagName('li'); for(var i=1;i<=aLi.length;i++){ aLi[i].onclick = function () { alert(i); } }</script>
这样写完之后发现每次点击弹出来都是4,因为当每次当我们点击li的时候,其实for循环已经循环结束了,所以每次都会弹出4。
这里就可以利用闭包在aLi[i].onclick = function () { alert(i);} 外面再套一个自执行函数,然后把for循环里的局部变量i当作参数传给这个函数,这样每次循环就会把i的值保存起来,这样外部函数执行完毕之后,并不会影响内部函数。
<script> var aLi = document.getElementsByTagName('li'); for(var i=0;i<aLi.length;i++){ (function (i) { // 把for循环的变量i当作参数 aLi[i].onclick = function () { alert(i); } })(i); }</script>所以这样每次点击第一个li就会弹出0,点击第二个弹出1,点第三个弹出2,点第四个弹出3了
4. 闭包会引起哪些问题?
闭包在IE下引发内存泄露
所谓内存泄露是指: 我们定义了一个变量,而这个变量会常驻在内存中不被释放,只有当关闭浏览器的时候才会被释放;
1. 什么条件下可能导致内存泄露?
当一个变量的属性,引用一个内部函数,而在这个函数内部 又 用到了函数外部的 变量的其他属性或者引用的时候,就会导致内存泄露
例如:
window.onload = function(){var oDiv = document.getElementById('div1');oDiv.onclick = function(){alert(oDiv.id);}}
var oDiv这个变量的 onclick属性引用了一个匿名函数,而在这个函数内部又用到了oDiv.id 的引用,因为oDiv是在这个匿名函数外部定义的,此时就会引起内存泄露;
2. 那怎么解决内存泄露问题那??
window.onload = function(){var oDiv = document.getElementById('div1');oDiv.onclick = function(){alert(oDiv.id);}window.onunload = function(){oDiv.onclick = null;}当页面跳转的时候把oDiv.onclick引用置为空就好了}
或者是
window.onload = function(){var oDiv = document.getElementById('div1');var id = oDiv.id;oDiv.onclick = function(){alert(id);}oDiv = null;}
在内部匿名函数引用之前把oDiv.id 通过一个变量接受一下,然后用完之后把oDiv置为空也可以解决内存泄露问题
- JS闭包详解
- JS-闭包详解
- 详解js闭包
- JS闭包详解
- 详解js闭包
- Js 闭包详解
- js闭包详解
- 详解js闭包
- 详解JS闭包
- 详解js闭包
- 详解js闭包
- 详解js闭包
- 详解js闭包
- 详解js闭包
- Js闭包详解
- 详解js闭包
- js 闭包详解
- 详解js闭包
- 串模式匹配之BF算法和KMP算法
- C语言 字符串循环右移
- std::function介绍
- Android中的轮播图
- Foundation 框架1
- JS-闭包详解
- weka实战003:apriori关联规则算法的实现
- fatal error: CCMenuItem.h: No such file or directory
- Tomcat 端口被占用问题.Several ports ( 8080, 8009) required by Tomcat v6.0
- c# 学习笔记1
- Mac 命令行大全
- 反射机制一
- sql文件解析
- 什么是KVC和KVO?