前端闭包,有你就够了

闭包的概念

我的理解为,就是函数嵌套函数,内部的函数可以访问到外部函数申明的变量和参数(闭包是函数里面的函数,并且它能记住周围的东西。)

特点:

1、让外部访问函数内部变量成为可能;
2、 局部变量会常驻在内存中;
3、 可以避免使用全局变量,防止全局变量污染;
4、会造成内存泄漏(有一块内存空间被长期占用,而不被释放)

闭包的创建

闭包可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。

闭包会发生内存泄漏,每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址

但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。

应用场景
例1
function f1(){
 var n = 999;
 function f2(){
   console.log(n); 
 }
 return f2;
}
var result=f1();
result(); // 999

这个比较简单,f2()为闭包函数,可访问fI()作用域下面的n

例2
function outerFn(){
  var i = 0; 
  function innerFn(){
      i++;
      console.log(i);
  }
  return innerFn;
}
var inner = outerFn();  //每次外部函数执行的时候,都会开辟一块内存空间,外部函数的地址不同,都会重新创建一个新的地址
var inner2 = outerFn();
inner();  // 1
inner2(); // 1
inner2(); // 2
inner();  // 2 
inner();  // 3

每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。所以说虽然inner和inner2都是innerFn(),但是都创建了新地址,都是自己的,互不干扰。

例3
var i = 0;
function outerFn(){
  function innnerFn(){
       i++;
       console.log(i);
  }
  return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2();     //1 2 3 4

变量 i是在outerFn外部声明的,是全局变量,所以引用的是同一个地址

例4
function fn(){
	var a = 3;
	return function(){
		return  ++a;                                     
	}
}
alert(fn()());  //4
alert(fn()());  //4 
var newFn = fn();
console.log(newFn());//4
console.log(newFn());//5
console.log(newFn());//6

fn()()的直接调用都是在开辟一个新的地址,但是把它赋给一个变量的时候,地址就不会再改变了,这个时候就会发现输出只是在递增的。

例5
(function() { 
  	var m = 0; 
  	function getM(){ 
  		return m; 
  	}
  	function seta(val){
  		m = val;
  	}
  	window.g = getM;
  	window.f = seta;
})();
f(100);
console.log(g());//100

(function(){ /* code */ })()是立即执行函数,当js执行到(function {// code})();时, 由于(function {// code})是表达式, js会去对它求解得到返回值, 由于返回值是一个函数, 故而遇到()时, 便会被执行。然后把getM和seta给到了window,这时候呢,我想到了大佬总结的一句话:闭包找到的是同一地址中父级函数中对应变量最终的值。

例6
function a() { 
  var i = 0; 
  function b() { 
  	console.log(++i);
  } 
  return b; 
} 
var c = a(); 
c();      //1 
c();      //2

注意。打印的是++i,是先加后返回,如果是i++,则返回的结果是 0,1

例7
function  fn(){
     var num = 223;
     var fn2 = function() {
           console.log(num);
     }
     num++;
     return fn2;
}
var  f1= fn();
fn();   //输出224
例8
function fun(n,o) {
    console.log(o);
    return {
         fun:function(m) {
               return fun(m,n);
         }
    };
}
var a = fun(0);  //undefined
a.fun(1);  //0  
a.fun(2);  //0  
a.fun(3);  //0  
var b = fun(0).fun(1).fun(2).fun(3);   //undefined  0  1  2
var c = fun(0).fun(1);  
c.fun(2);  
c.fun(3);  //undefined  0  1  1
变量a

第一个fun(0)是在调用第一层fun函数。第二个fun(1)是在调用前一个fun的返回值的fun函数,所以:

第后面几个fun(1),fun(2),fun(3),函数都是在调用第二层fun函数。

在第一次调用fun(0)时,o为undefined;

第二次调用fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

第三次调用fun(2)时m为2,但依然是调用a.fun,所以还是闭包了第一次调用时的n,所以内部调用第一层的fun(2,0);所以o为0

第四次同理;

即:最终答案为undefined,0,0,0

变量b

先从fun(0)开始看,肯定是调用的第一层fun函数;而他的返回值是一个对象,所以第二个fun(1)调用的是第二层fun函数,后面几个也是调用的第二层fun函数。

在第一次调用第一层fun(0)时,o为undefined;

第二次调用 .fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

第三次调用 .fun(2)时m为2,此时当前的fun函数不是第一次执行的返回对象,而是第二次执行的返回对象。而在第二次执行第一层fun函数时时(1,0)所以n=1,o=0,返回时闭包了第二次的n,遂在第三次调用第三层fun函数时m=2,n=1,即调用第一层fun函数fun(2,1),所以o为1;

第四次调用 .fun(3)时m为3,闭包了第三次调用的n,同理,最终调用第一层fun函数为fun(3,2);所以o为2;

即最终答案:undefined,0,1,2、

变量c

根据前面两个例子,可以得知:

fun(0)为执行第一层fun函数,.fun(1)执行的是fun(0)返回的第二层fun函数,这里语句结束,遂c存放的是fun(1)的返回值,而不是fun(0)的返回值,所以c中闭包的也是fun(1)第二次执行的n的值。c.fun(2)执行的是fun(1)返回的第二层fun函数,c.fun(3)执行的也是fun(1)返回的第二层fun函数。

在第一次调用第一层fun(0)时,o为undefined;

第二次调用 .fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

第三次调用 .fun(2)时m为2,此时fun闭包的是第二次调用的n=1,即m=2,n=1,并在内部调用第一层fun函数fun(2,1);所以o为1;

第四次.fun(3)时同理,但依然是调用的第二次的返回值,遂最终调用第一层fun函数fun(3,1),所以o还为1

即最终答案:undefined,0,1,1

例9
function fn(){
   var arr = [];
   for(var i = 0;i < 5;i ++){
	 arr[i] = function(){
		 return i;
	 }
   }
   return arr;
}
var list = fn();
for(var i = 0,len = list.length;i < len ; i ++){
   console.log(list[i]());
}  //5 5 5 5 5

看样子函数打印对应数字,1,2,3,4,5 实际不是,因为每个闭包函数访问变量i是fn执行环境下的变量i,随着循环的结束,i已经变成5了,所以执行每个闭包函数,结果打印5,5
怎么解决这个问题呢?看下面例子

例10
function fn(){
  var arr = [];
  for(var i = 0;i < 5;i ++){
	arr[i] = (function(i){
		return function (){
			return i;
		};
	})(i);  
  }
  return arr;
}
var list = fn();
for(var i = 0,len = list.length;i < len ; i ++){
  console.log(list[i]());
}  //0 1 2 3 4
使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值