JavaScript 闭包

1 闭包是什么?

什么是JavaScript 闭包,当一个内部函数在其外部函数之外被调用,就形成了一个闭包。

function init() {
    var name = "Mozilla"; // name 是一个被 init 创建的局部变量
    function displayName() { // displayName() 是内部函数,一个闭包
        alert(name); // 使用了父函数中声明的变量
    }
    displayName();
}
init();

init() 内部有一个局部变量name 和一个函数displayName()。displayName() 没有自己的局部变量,它可以访问外部函数的变量name 。要注意的是,如果displayName() 有同名局部变量name ,将使用自己的同名局部变量。

 

2 闭包的写法

闭包的5种写法如下:

//第1种写法  
function Circle(r) {  
      this.r = r;  
}  
Circle.PI = 3.14159;  

//使用prototype 属性定义的对象方法是实例方法(非静态),只有在实例化后才能使用,在方法内部可以通过this.xxx 来引用对象自身中的其他属性。
Circle.prototype.area = function() {  
  return Circle.PI * this.r * this.r;  
}  
  
var c = new Circle(1.0);     
alert(c.area());

在JS 中一切都是对象,函数也是对象。此种写法只是给函数添加了一些属性。

//第2种写法  
var Circle = function() {  
   var obj = new Object();  
   obj.PI = 3.14159;  
     
   obj.area = function( r ) {  
       return this.PI * r * r;  
   }  
   return obj;  
}  
  
var c = new Circle();  
alert( c.area( 1.0 ) );

声明一个变量,将函数作为属性赋值给变量。

//第3种写法  
var Circle = new Object();  
Circle.PI = 3.14159;  
Circle.Area = function( r ) {  
       return this.PI * r * r;  
}  
  
alert( Circle.Area( 1.0 ) );

新建一个对象,然后给它添加属性和方法。

//第4种写法  
var Circle={  
   "PI":3.14159,  
 "area":function(r){  
          return this.PI * r * r;  
        }  
};  
alert( Circle.area(1.0) );

声明对象的同时把变量和方法也声明了。

//第5种写法  
var Circle = new Function("this.PI = 3.14159;this.area = function( r ) {return r*r*this.PI;}");  
  
alert( (new Circle()).area(1.0) ); 

呵呵,此种方式看着奇怪,亲测能用。

写法多种多样,看自己喜好。下面是一个有意思的demo,。

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

本质上讲,makeAdder 是一个函数工厂,创建了指定值和它的参数相加的函数。通过这个函数工厂创建了两个新的函数:一个将参数和5 求和,另一个将参数和10 求和。两个新函数都是闭包,共享相同的函数定义,但有各自不同的上下文环境。

3 闭包的用处

使用闭包,可以做很多事情,比如:实现面向对象,代码更优雅简洁,某些方面提高代码执行效率。

 

3.1 匿名自执行函数

JS 中所有的变量,如果不加上var 关键字,默认会添加到全局对象的属性。如果随意地加入很多全局变量会污染全局对象,造成开发维护上的困难。实际情况中有些函数只需执行一次,其内部变量无需维护,比如页面初始化,这里可用到闭包。

var data= {    
    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);    
       }    
    }    
       
})(data);

创建一个匿名函数,并立即执行,由于函数外部不能引用它的内部变量,执行完后会立即释放资源,不会污染全局对象。

 

3.2 缓存结果

有些耗费大量时间计算出来的数据可能会重复使用,为避免重复计算,可将其缓存起来。调用的时候如果还没计算则计算一次,并存储在内部变量,以后再调用则直接使用,无需再次计算。

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("input");

 

3.3 封装

var person = function(){    
    //变量作用域为函数内部,外部无法访问    
    var name = "default";       
       
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
}();    
     
print(person.name);//直接访问,结果为undefined    
print(person.getName());    
person.setName("abruzzi");    
print(person.getName());    
   
得到结果如下:  
   
undefined  
default  
abruzzi

函数内部变量不能在其外部被访问,通过return 方法的形式将内部变量暴露出来,实现面向对象的封装特性。

 

3.4 实现类和继承

function Person(){    
    var name = "default";       
       
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
    };   

    var p = new Person();
    p.setName("Tom");
    alert(p.getName());

    var Jack = function(){};
    //继承自Person
    Jack.prototype = new Person();
    //添加私有方法
    Jack.prototype.Say = function(){
        alert("Hello,my name is Jack");
    };
    var j = new Jack();
    j.setName("Jack");
    j.Say();
    alert(j.getName());

声明的Person 就像一个类,new 一个这样的对象,并调用其方法。Jack 继承Person,并添加了自己的方法。这样就实现了面向对象的特性。

posted on 2017-06-03 15:06  MojoJojo  阅读(72)  评论(0)    收藏  举报

导航