全面理解Javascript闭包

什么是闭包

闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。闭包的特点:

  1. 作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
  2. 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。

简单的说,Javascript允许使用内部函数—即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

闭包的几种写法

  1. 给函数添加一些属性
//第1种写法  
function Circle(r) {
    
      
      this.r = r;  
}  
Circle.PI = 3.14159;  
Circle.prototype.area = function() {
    
      
  return Circle.PI * this.r * this.r;  
}  
  
var c = new Circle(1.0);     
alert(c.area());
  1. 将一个函数当作值赋给变量
//第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 ) );
  1. 这种方法最好理解,就是new 一个对象,然后给对象添加属性和方法
//第3种写法  
var Circle = new Object();  
Circle.PI = 3.14159;  
Circle.Area = function( r ) {
    
      
       return this.PI * r * r;  
}  
alert( Circle.Area( 1.0 ) );
  1. 这种方法使用较多,也最为方便。var obj = {}就是声明一个空的对象
//第4种写法  
var Circle={
    
      
   "PI":3.14159,  
   "area":function(r){
    
      
       return this.PI * r * r;  
    }  
};  
alert( Circle.area(1.0) );
  1. 这种写法我是没用过,也不便于理解,大家可以了解一下
//第5种写法  
var Circle = new Function("this.PI = 3.14159;this.area = function( r ) {return r*r*this.PI;}");  
  
alert( (new Circle()).area(1.0) );  
总的来说,上面几种方法,第2中和第4中较为常见,大家可以根据习惯选择。

Javascript闭包的用途

  1. 匿名自执行函数
    我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,
    比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。
    除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,
    比如UI的初始化,那么我们可以使用闭包:
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);
//ES5里的var没有块状作用域
//ES5里只有函数才有作用域的概念,通过闭包解决i的作用域问题
for(var i=0;i<3;i++){
    
    
	(function(i){
    
    
		btn[i].addEventListener('click',function(){
    
    
			//处理逻辑
		})
	})(i);
}

//ES6里的let具有块状作用域,每次循环的i都具有独立的空间
//而这种写法var是做不到的,var以最后一次循环i=2作为所有事件最终处理逻辑
for(let i=0;i<3;i++){
    
    
	btn[i].addEventListener('click',function(){
    
    
		//处理逻辑
	})
}

我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在函数执行完后会立刻释放资源,关键是不污染全局对象。

  1. 结果缓存
    开发中有时需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留
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");
  1. 封装
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
var mymath = (function () {
    
    
	var counter = 0;
	return  {
    
    
		myadd:function(){
    
    
			return counter += 1;
		},
		mydeduct:function(){
    
    
			return counter -= 1;
		}
	}
})();
function add(){
    
    
	document.getElementById("demo").innerHTML = mymath.myadd();
}
function deduct(){
    
    
	document.getElementById("demo").innerHTML = mymath.mydeduct();
}
  1. 实现类和继承
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());

猜你喜欢

转载自blog.csdn.net/wo240/article/details/112876821