JavaScript闭包

一些常见的闭包题目:

1.经典题目


//1秒后同时输出5个5,每次允许后i++,最后一次i=5;
 for (var i = 0i < 5i++) {
   setTimeout(function() {     
    console.log(i);
   }, 1000); //若为1000*i,开始输出一个 5,然后每隔一秒再输出一个5,一共5个5
 }

2.解决办法


//要想1秒后,同时输出0到4,解决办法一般有两个:
//1,setTimeout函数外面加一层闭包,闭包后变量立即执行。
 (function(i) {
   setTimeout跑这里
 })(i);

//2,把var改为let

2.1. let 不能写在外面

let i; // 如果 let 放在外面, 与 var 无异
for (i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i); // 同时输出 5 个 5
  }, 0);
}
 

3. 立即执行函数要传参,不然无效

(function() { // 不传参 i,无效
    想要运行的函数
  })(i);

4.使用return的情况, 相当于把立即执行函数写进里面


//1秒后同时输出5个5:
  for (var i = 0i < 5i++) {
   setTimeout(function() { // 同上,这里传进参数i,就可以使1秒后,同时输出0到4
     return function() {
       console.log(i);
     }
   }(i), 1000); // 若为1000*i,则开始时输出一个5,然后每隔一秒再输出一个,一共5个5
 }

5.console.log后面绑定i参数,会报错,因为setTimeout的第一个参数需要是函数,而立即执行函数,返回的是undefined


//马上输出0到4,只要console.log后面绑定i参数,永远是立刻输出0到4
 for (var i = 0i < 5i++) {
   setTimeout(function(i) {//有没有参数i都没影响
     console.log(i);
   }(i),1000); //无论这个是1000*i还是多少,都无效
 }

6.promise的执行顺序


//立刻输出2,3,5,4,1
   setTimeout(function() {
       console.log(1)
     }, 0);
   new Promise(function executor(resolve) {
     console.log(2);
     forvar i=0 ; i<10000 ; i++ ) {
       i == 9999 && resolve();
     }
     console.log(3);
   })
   .then(function() {
     console.log(4);
   });
   console.log(5);

 

 

7.深入地理解闭包


 function f1(){
      var n=999;
      function f2(){
        alert(n);
      }
      return f2;
    }
    var result=f1(); //如果直接运行f1()是无效的,必须把return值保存到变量中。
    result(); // 999
//f2可以读取f1中的局部变量,那么只要把f2作为返回值,就可以在f1外部读取它的内部变量了

 

 闭包就是能够读取其他函数内部变量的函数。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中:


  function f1(){
    var n=999;
    nAdd=function(){  //全局变量,匿名函数
            n+=1;
        }
    function f2(){
      console.log(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

//这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
//f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回

8.闭包的弊端:

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

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


function assignHandler(){
    var element=document.getElementById('someElementId');
    element.onclick=function(){ //闭包
        alert(element.id);  //调用了element,它会一直存在于内存中,不会被垃圾回收机制回收掉,所以导致了内存泄漏
    }
}

解决办法:尽量引用包含函数的值类型变量,不引用包含函数的引用类型变量,且必须在包含函数的最后将引用变量的值设置为null,断开变量名与对象之间的连接,这样对象便可正常回收。


function assignHandler(){
    var element=document.getElementById('someElementId');
    var id=element.id;
    element.onclick=function(){
        alert(id);
    }
    element=null;
}

9.几个例子理解:


function Foo() {
    var i = 0;
    return function() {
        console.log(i++);
    }
}
 
var f1 = Foo(),
    f2 = Foo();
f1();//0
f1();//1
f2();//0 与f1是不同的

 


  var val1=0;
  var val2=0;
  var val3=0;
  for(var i1=1;i1<=3;i1++){ //i1最终为4
      console.log('i1='+i1);
      var i2=i1;            //i2最终为3
      console.log('外面i2='+i2);
      (function(){
          console.log('里面的i2='+i2);
          var i3=i2;       //i3只存在闭包里,不被销毁
          console.log('i3='+i3);  //执行到这里后回到上面的i1,循环,直到i1条件不满足
          setTimeout(function(){ //1秒后,连续执行3次里面的函数
            console.log(i1);//4,4,4
            console.log(i2);//3,3,3
            console.log(i3);//1,2,3
             val1+=i1;
             val2+=i2;
             val3+=i3;
         },1);
    })();
 }
 
 setTimeout(function(){
     console.log(val1); //12
     console.log(val2); //9
     console.log(val3); //6
 },100)

 

posted @ 2017-03-15 18:27  森森森shen  阅读(156)  评论(0编辑  收藏  举报