初探js闭包
1.变量的作用域:全局变量、局部变量
- 函数内部可以直接读取局部变量
js代码
var n=2;
function fun(){
alert(n);
}
fun(); //2
- 函数外部不能读取函数内部局部变量
js代码
function fun(){
var n=2; //注意这里要使用var,否则声明的是全局变量
}
alert(n); //error
- 下面n声明为全局变量
js代码
function fun(){
n=2;}
alert(n); //2
2.如何从外部读取局部变量?
有时候需要得到函数内部的局部局部变量,正常情况下是办不到的,只有变通才有可能。在函数内部在定义一个函数
js代码
function fun(){
var n=2;
function f2(){
alert(n); //2
}
}
上面代码,f2函数包含在fun函数中,这时fun内部的局部变量对f2是可见的,而f2内部局部变量,对fun不可见,这就是javascript"链式作用域"结构
子对象会一级一级向上寻找所有父对象的变量,即父对象的所有变量对子对象都可见,反之则不成立
js代码
function fun(){
var n=2;
function f2(){
alert(n);
}
return f2;
}
var result=fun();
result(); //2
3.闭包概念
上面f2函数就是闭包
闭包简单理解就是:能够读取其他函数内部变量的函数,即将函数内部与函数外部连接起来的桥梁
4.闭包的用途
事实上,通过使用闭包,可以做很多事情。比如:模拟面向对象的代码风格;更优雅,更简洁地表达出代码;在某些方面提升代码的执行效率
注:根据上面知识可知,所有的变量如果不加关键字,则默认会添加到全局对象的属性上,临时变量加入全局变量有很多坏处,如:别的函数可能误用这些变量,造成全局对象过于庞大,影响访问速度(因为变量的取值需要从原型链上遍历)。
4.1匿名子执行函数
有些函数只需执行一次,其内部变量无序维护,比如UI的初始化,我们可以使用闭包
js代码
var datamodel={ table:[]; tree:[]; } (function(dm){ for(var i=0;i<dm.table.rows;i++){ var row=dm.table.rows[i]; for(var j=0;j<row.cells;i++){ drawCell(i,j); } } //build dm.tree })(datamodel);
创建一个匿名函数并立即执行它,由于外部无法引用它内部的变量,因此在执行完后会很快被释放,最主要的是这种机制不会污染全局变量
4.2 缓存
如果我们正在处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找的值即可。
闭包正是可以做到这一点,因为它不会释放外部的引用,函数内部的值可以得到保留。减少全局变量,常驻内存,可以实现缓存
js代码
function f1(){
var n=99;
nAdd=function(){
n+=1;
}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result();//99
nAdd();
result();//100
为什么f1中的局部变量n一直保存在内存中,没有在f1调用结束后自动清除?
f1是f2的父函数,f2被赋给全局变量,导致f2始终在内存中,而f2的存在依赖f1,因此f1也始终在内存中,不会再调用结束后,被垃圾回收机制回收
4.3 实现封装
4.3.1如下例子,在person之外的地方无法访问其内部的变量,通过提供闭包的形式来访问
js代码
var person=function(){
var name="jack";
return{
getName:function(){
return name;
},
setName:function(newName){
name=newName;
}
}
}();
alert(person.name); //直接访问结果为:undefined
alert(person.getName()); //jack
person.setName("lihua");
alert(person.getName()); //lihua
4.3.2
闭包另一个重要用途是实现面向对象中的对象,传统的语言都提供类的模板机制,这样不同的对象(类的实例)拥有独立的成员及状态,互不干涉。虽然javascript中没有类这样的机制,但通过使用闭包,可以模拟出这样的机制。
js代码
function person(){
var name="jack";
return{
getName:function(){
return name;
},
setName:function(newName){
name=newName;
}
}
};
var jone=person();
alert(jone.getName()); //jack
jone.setName("lihua");
alert(jone.getName()); //lihua
var miss=person();
alert(miss.getName());
miss.setName("wanghai");
alert(miss.getName());
以上代码中Jone和miss可以成为person这个类的实例,因为这两个实例对name这个成员的访问时独立的,互补影响。
5使用闭包应该注意问题
5.1内存泄露
闭包会使函数中的变量都保存在内存中,内存消耗大,故不能滥用闭包,否则造成网页性能问题(浏览器响应速度慢、降低用户体验甚至会造成浏览器无响应等现象),在IE中可能导致内存泄露
5.2上下文应用
jQuery代码
$(function(){
var con=$("div#panel");
this.id="content";
con.click(function(){
alert(this.id); //panel
});
});
this.id 显示的被赋值content,而在click回调中,形成的闭包引用到this.id,但这个alert会弹出"panel",原因:虽然闭包可以引用局部变量,但此处涉及到this,因为调用对象的存在,使得闭包被调用时(当这个panel的click事件发生时),此处的this引用的是con这个jQuery对象,而匿名函数this.id="content"是对匿名函数本身做的操作。
如果想在事件中处理函数中访问的这个值,则必须做些改变
$(function(){
var con=$("div#panel");
this.id="content";
var self=this;
con.click(function(){
alert(self.id); //content
});
});
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。