闭包的理解
1.闭包的定义、用途及目的
1.1 定义
1.能够读取其它函数内部变量的函数
2.在本质上,闭包就是将函数内部与函数外部连接起来的一座桥梁
3.在构造函数体内定义另外的函数作为目标对象的方法函数,而这个对象的方法函数反过来引用外层函数体中的临时变量。这使得只要目标
对象在生存期内始终能保持其方法,就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束,临时变量的名称也都消失了,但在目
标对象的方法内却始终能引用到该变量的值,而且该值只能通这种方法来访问。即使再次调用相同的构造函数,但只会生成新对象和方法,新的临时变量只是对应新
的值,和上次那次调用的是各自独立的。
4. 所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分
例1:function aa(){
var a=20;
function b(i){
alert(++a+i);
}
return b;//或是return function(){alert(++a+i);}
}
//aa()(2);//第二个括号执行函数b
//var c=aa();
//c(5);//当函数aa()的内部函数b()被函数aa()外部的变量引用的时候,创建了一个闭包
例2:window.onload=function(){
var li=document.getElementsByTagName("li");
for(var i=0;i<li.length;i++){
li.onclick=(function(n){
return function(){
alert(n);
}
})(i);
}
}
1.2 用途
1.读取函数内部的变量
2.让这些变量的值始终保持在内存中。
1.3 目的
闭包的典型框架是jquery。应用闭包主要是为了:设计私有的方法和变量
2.闭包的优缺点与使用闭包的注意点
2.1 优点
1.保护函数内变量的安全,加强了封装性
2.在内存中维持一个变量
2.2 缺点
1.常驻内存,会增大内存使用量,使用不当很容易造成内存泄露
2.3 注意点
1.由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2.
闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public
Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
3.全局变量与局部变量
function outerFun(){
var a=1; //这是局部变量,,,去掉var是全局变量
alert(1);
}
var a=4;
//outerFun(); 结果1
//alert(a); 结果4
3.作用域(scope)与作用域链(scope chain)
3.1 作用域(scope)
1.在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。
2.函数作用域分为词法作用域和动态作用域。
3.
词法作用域是函数定义时的作用域,即静态作用域。当一个函数定义时,他的词法作用域就确定了,词法作用域说明的是在函数结构的嵌套关系下,函数作用的范
围。这个时候也就形成了该函数的作用域链。作用域链就是把这些具有嵌套层级关系的作用域串联起来。函数的内部[[scope]]属性指向了该作用域链。
4.
动态作用域是函数调用执行时的作用域。当一个函数被调用时,首先将函数内部[[scope]]属性指向了函数的作用域链,然后会创建一个调用对象,并用该
调用对象记录函数参数和函数的局部变量,将其置于作用域链顶部。动态作用域就是通过把该调用对象加到作用域链的顶部来创建的,此时的[[scope]]除
了具有定义时的作用域链,还具有了调用时创建的调用对象。换句话说,执行环境下的作用域等于该函数定义时就确定的作用域链加上该函数刚刚创建的调用对象,
从而也形成了新的作用域链。所以说是动态的作用域,并且作用域链也随之发生了变化。再看这里的作用域,其实是一个对象链,这些对象就是函数调用时创建的调
用对象,以及他上面一层层的调用对象直到最上层的全局对象。
5.譬如全局环境下的函数A内嵌套了一个函数B,则该函数B的作用域链就是:函数B
的作用域—>函数A的作用域—>全局window的作用域。当函数B调用时,寻找某标识符,会按函数B的作用域—>函数A的作用域—
>全局window的作用域去寻找,实际上是按函数B的调用对象—>函数A的调用对象—>全局对象这个顺序去寻找的。也就是说当函数调
用时,函数的作用域链实际上是调用对象链。
3.2 作用域链(scope chain)
当定义函数a的时候,js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。
4.函数的执行环境
当执行函数a的时候,a会进入相应的执行环境(excution context)。
5.活动对象与JavaScript的垃圾回收机制
5.1 活动对象
然后执行环境会创建一个活动对象(call
object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a
的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。
5.2 JavaScript垃圾回收机制
在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对
象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。
6.匿名自执行函数
var datamodel={
table:[],
tree:{}
};
(function(dm){
for(var i=0;i < dm.table.rows;i++){
var row=dm.table.rows;
for(var j=0;j < row.cells;j++){
drawCell(i,j);
}
}
})(datamodel)
7.缓存
var CachedSearchBox=(function(){
var cache={},count=[];
return {
attachSearchBox:function(dsid){
if(dsid in cache){ //如果结果在缓存中
return cache[dsid]; //直接返回缓存中的对象
}
var fsb=new uikit.webctrl.SearchBox(dsid); //新建
cache[dsid]=fsb; //更新缓存
if(count.length > 100) //保证缓存的大小<=100
{
delete cache[count.shift()];
}
return fsb;
},
clearSearchBox:function(dsid){
if(dsid in cache){
cache[dsid].clearSelection();
}
}
};
})();
CachedSearchBox.attachSearchBox("input1");
8.实现封装
var person=(function(){
name="songmin"; //变量作用域为函数内部,外部无法访问
return {
getName:function(){
return name;
},
setName:function(newname){
name=newname
}
}
})();
print(person.name);
print(person.getName());
print(person.setName("zs"));
print(person.getName());
9.实现面向对象中的对象
function Person(){
name="songmin";//变量作用域为函数内部,外部无法访问
return {
getName:function(){
return name;
},
setName:function(newname){
name=newname
}
}
};
var john=Person();
print(john.getName());
john.setName("aa");
var jack=Person();
print(jack.getName());
jack.setName("aa");
10.排序
var arr=[2, 4, 7, 8, 5, 1, 9, 0, 6, 3];
var $=function(id){return document.getElementById(id)};
var Sort={
Insert:function(){
for(var i=1;i<arr.length;i++){
for(var j=0;j<i;j++){
if(arr<arr[j]){
arr=[arr[j],arr[j]=arr][0];
}
}
setTimeout((function(){
var m=[];
for(var j=0;j<arr.length;j++)
{
m[j]=arr[j];
}
return function(){$("proc").innerHTML += m + "<br>"}
})(),i*500);
}
return arr;
}
}