天天看点

JavaScript—闭包1.引入闭包2. 理解闭包3.常见的闭包4.闭包的作用5.闭包的生命周期6.闭包的应用 :定义js模块7.闭包的缺点及解决8. 扩散概念(内存溢出、内存泄露)

文章目录

  • 1.引入闭包
  • 2. 理解闭包
  • 3.常见的闭包
  • 4.闭包的作用
  • 5.闭包的生命周期
  • 6.闭包的应用 :定义js模块
  • 7.闭包的缺点及解决
  • 8. 扩散概念(内存溢出、内存泄露)

1.引入闭包

js中有两大神兽,一个是原型对象,一个是闭包

进行一个小练习:点击某个按钮,提示“点击的是第几个按钮”;
		
    <button>测试1</button>
    <button>测试2</button>
    <button>测试3</button>
 <script type="text/javascript">
    var btns=document.getElementsByTagName("button");   //获取到的元素是放在一个伪数组中;

//第一种:遍历加监听;	
    for(var i=0; i<btns.length;i++)
    //这样写,则需要每次去获取btns.length,下面的写法那就只需要获取一次btns.length;
  	for(var i=0,length=btns.length; i<length; i++){
  		var btn =btns[i];
  		btn.onclick =function (){
  			alert('第'+(i+1)+'个');
  		}
    }		
   //for循环执行完后,i=4了。而单击响应函数还没执行;
   //必须要单击才会执行,而此时i=4,则无法实现功能
  	  
//第二种:	        
    for(var i=0,length=btns.length; i<length; i++){
		btns[i].index=i;    
		//将数组btns的对应元素中分别保存一个下标index;
		btns[i].onclick =function (){
			alert('第'+(this.index+1)+'个');			
		}
   }
       //给数组中每个元素一个对应索引值,依次给数组中每个元素绑定事件,
       //this.index+1为元素的对应索引值+1,就是对应的第几个
       //遍历数组中的元素,分别给元素绑定事件;
   
   
//第三种:利用闭包        
     for(var i=0,length=btns.length; i<length; i++){
  		(function (i){           
  		   btns[i].onclick=function(){       
  		   	   alert('第'+(i+1)+'个') 
  		   }
  		})(i);         //立即执行函数
  	}	               
  	  //内部使用了外部的i,产生了对应的闭包,保存了对应的i值   
      //每次循环产生对应的闭包,闭包中保存了对应的值,单击触发用的是对应的i值。
    </script>
           

2. 理解闭包

1.如何产生闭包?
     :当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包。

 2.产生闭包的条件?
     *函数嵌套
     *内部函数引用了外部函数的数据(变量/函数) 
     *要执行外部函数;
       
 3.闭包到底是什么?
    *使用chrome调试查看
    *理解一:闭包是嵌套的内部函数(绝大部分人)     ==》内部函数
    *理解二:闭包是嵌套的内部函数中的对象(极少数人)  ==》内部函数中有一个对象Closure,引用的变量放在对象Closure中;
    *注意:若为理解二,则闭包存在于嵌套的内部函数中;
           
<script type="text/javascript">
    function fn1(){
    	var a=2;
    	function fn2(){       
    		console.log(a);   //引用了外部变量
    	}
    }
    fn1();      
    //执行外部函数,就会产生函数执行上下文对象,则会定义函数中的变量。
    //定义了变量(内部函数fn2),就产生闭包,不用调用内部函数。
</script>
           

3.常见的闭包

1.将一个函数作为另一个函数的返回值;
2.将函数作为实参传递给另一个函数调用;
           
<script type="text/javascript">
//1.将函数作为另一个函数的返回值;
     function fn1(){
        var a =2;
        function fn2(){
        	a++;
        	console.log(a);
        }
        return fn2;  
        //将一个内部函数,作为他的外部函数的返回值; 
        }    
        var f=fn1(); 
        //调用函数fn1,并将fn1()赋给f;
        //f()相当于fn1()(),而fn1()执行返回的是fn2,所以fn1()()就相当于fn2();
        //只调用了一次fn1,所以产生一个闭包;       
        //外部函数调用几次就产生几个闭包,就能产生几个内部函数对象;             
        f();   //3     
        f();   //4
        //闭包Closure中存储着引用变量a,a的值会在里面保存;
        //函数执行完a不会自动释放;


//2.将函数作为实参传递给另一个函数调用;
    function showDelay(msg,time){
        setTimeout(function(){
        	alert(msg);         //引用了外部函数的数据,产生闭包。
        },time);
       }
    showDelay('liufunan',2000);
 </script>
           

4.闭包的作用

1.使函数内部的变量在函数执行完后,仍然存活在内部中(延长了局部变量的生命周期)。
     (正常情况函数调用结束时上下文对象就会被自动释放,局部变量也会释放掉)
 2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)。       
           
<script type="text/javascript">
     function fn1(){
        var a =2;
        function fn2(){
        	a++;
        	console.log(a);
          }
        function fn3(){
        	a--;
        	console.log(a);
         }
        return fn2;    
      }
     var f=fn1(); 
     f();   //3     
     f();   //4    则执行完后,a没有释放还是存活在闭包中的,
     //这样调用,则是在函数外部去操作函数内部的数据。
     //根据返回的函数不同,可以就进行不同的操作。
 </script>
           

问题1:函数执行完后,函数内部声明的局部变量是否还存在 ??

答:一般不存在,存在于闭包中的变量才可能存在;但如果让包含闭包的函数成为了一个垃圾对象,则会被释放,闭包中的变量也会不存在了;

如上题中,局部变量fn2()、fn3()被自动释放了,而局部变量a还存在;n2()产生闭包,a被保存在闭包Closure里 ,不会释放;

问题2.在函数外部能直接访问函数内部的局部变量吗??

答:不能,但是我们可以通过闭包让外部操作它;

5.闭包的生命周期

1.产生:调用外部函数,进行预处理,定义内部函数时就产生了闭包。
               (执行上下文对象开始定义嵌套内部函数时,不是在调用内部函数时)
 2.死亡:在嵌套的内部函数成为垃圾对象时。
               (没有变量指向它、函数=null时,成为垃圾对象)
           
<script type="text/javascript">
     	function fn1(){
        	var a =2;           
        	function fn2(){        //此时闭包语句产生了(函数提升,定义函数时)
        		a++;
        		console.log(a);
        	}
        	return fn2;    
        }
        var f=fn1();
     	f();      
        f(); 
        f=null;   
       //包含闭包的内部函数成为垃圾对象,闭包死亡;
 </script>
           

6.闭包的应用 :定义js模块

定义js模块:
   *具有特定功能的js文件(操作数据)
   *将所有的数据和功能都封装在一个函数内部(私有的)
   *只向外暴露一个对象(里面包含n个方法的)或函数;
   *模块的使用者,只需要通过模块暴露的对象,调用对象中的方法来实现对应的功能;
           

封装的js模块:

function my (){
	//私有数据;
	var str="MY dog";
	//操作数据的函数;
	function some(){
		console.log("yes"+str);
	}
	function other(){
		console.log("no"+str);
	}
	//向外暴露对象(将方法提供给外部使用)
	return {
		some:some,
		other:other
	}
}

<script type="text/javascript" src="../js/bibao.js"></script>
//引入js模块
<script type="text/javascript">
	   	var a=my();        //返回的内容赋给a,是一个对象
	    a.other();         //使用js模块中的方法
</script>
           
//封装的js模块:
(function my (){
	var str="MY dog";     	 //私有数据;
	function some(){
		console.log("yes"+str);
	}
	function other(){
		console.log("no"+str);
	}
	window.bibao2={        //把要暴露的东西添加为window的属性;
  		some:some,         //在使用时,直接调用
		other:other
	}
})();    //匿名函数自调用(立即执行函数)

//引入js模块
<script type="text/javascript" src="../js/bibao2.js"></script>
<script type="text/javascript">
     bibao2.some();       //window的属性可以直接使用;
	//这种方法更加直接,引入js文件后,就直接可以使用window的属性;	   	
</script>

           

7.闭包的缺点及解决

1.缺点:  (延长局部变量的生命周期,既优点,也是缺点)
   *函数执行完后,函数内的局部变量没有释放,占用内存时间会变长    
   *容易造成内存泄露
      
 2.解决
  *能不用闭包就不用
  *用的时候要及时释放
           
<script type="text/javascript">
    	function fn1(){
    		var arr=new Array[];    //数组中放1000个位置
    		function fn2(){
    			console.log(arr.length);
    		}
    		return fn2;	
    	}
    	var f=fn1();
    	f(); 
    	f=null;       //让内部函数成为垃圾对象--》回收闭包  (及时释放)
    </script> 
           

8. 扩散概念(内存溢出、内存泄露)

1.内存溢出
    *一种程序运行出现的错误
    *当程序运行需要的内存超过剩余的内存时,就抛出内存溢出的错误;

 2.内存泄露       (不是程序报错)
    *占用的内存没有被及时的释放
    *内存泄露积累多了就容易导致内存溢出;
    *常见的内存泄露:
        1.意外的全局变量
        2.没有及时清理的计时器或回调函数
        3.闭包 
           
<script type="text/javascript">
   //1.内存溢出
  	var obj={ };
  	for(var i=0;i<10000;i++){
  		obj[i]=new Array[100000];     //给每个元素10000个位置
  		console.log("---");
  	}
       //内存溢出,程序运行出现的错误
       
       
  //2.内存泄露
        //意外的全局变量  
	    function fn(){
	    	a=3; 
	    //没有var定义的变量a,虽然是在函数里面,但是一个全局变量
	    //这样就容易忽略释放,意外的全局变量
	    	console.log(a);
	    }
	    fn();
	    //没有及时清理的计时器或回调函数 
	    var timer=setInterval(function(){  
	    	console.log("---")
	    },500);                //启动循环定时器,会不断的执行;
  	    clearInterval(timer);  //所以不用的时候应该及时的清理;
	   
	    //闭包
	    function fn1(){
    		var a=10;
    		function fn2(){
    			console.log(a);
    		}
    		return fn2;	
    	}
    	var f=fn1();   //产生闭包
    	f();  
    	//f=null; 
    	//如果不去释放的话,则闭包中的a会一直都在,则内存泄露
    </script>