程序员

javascript 闭包

2019-02-27  本文已影响0人  悠哈121

闭包:能够访问函数作用域的变量的函数。我们认真来分析这句话,它有三点(1.闭包是定义在函数中的函数 2.闭包访问包含函数的变量 3.即使包含函数执行完了,被闭包引用的变量也得不到释放)

这里我们主要是对第三个特点理解,js语言有自己的一套内存回收机制,一般情况下局部变量和对象使用完后就会被系统自动回收,不需要我们理会,一般函数执行完函数内部变量内存将会得到释放,但是函数空间并不会被摧毁,你可以这样理解 function a(){ var b = 1},由于a一直引用这函数{ var b = 1}这个空间,所以函数空间并不会摧毁,但是由于a()执行时var b =1是会放到栈内存的 ,当函数a()执行完毕之后,var b =1这个变量的内存将得到释放,但是函数的定义在,所以当我们再次执行的时候,会重新再次分配给var b = 1一个内存空间,(对象属性是直接放在堆内存空间的,而函数内部变量,则在调用时在栈内存中开辟)但是碰到闭包的情况这些变量和对象是不被回收的,我们直接来看例子吧 image.png
image.png

当我把a=null,发现仍可以调用b()打印4,这是由于b= a()之后,在栈内存开辟了私有作用域,而该私有作用域一直被b引用着,导致内存变量a一直存在

function fn(){
  var a = 1;
  //第一种情况
  function fn1(){
    //这个fn1算闭包么,其实这个看自己的理解,你可以把它理解成为一个闭包
   //只不过这个闭包没有访问外部函数的变量 ,那么也将不会存在闭包的第三个特点(闭包引用的变量不被释放问题)
   //只看这个函数是符合闭包的定义的,但是写这种函数并没有意义,
   //既然fn1和外面的函数没有关系,那我们何不直接把它提出来写称另一个函数呢
  }
  //第二种情况
  function fn2(){
   console.log(a);
    a =  3;
  //这个fn2算闭包么,这个方法的理解同上,该函数访问了外部函数的局部变量,但是并不会导致内存引用变量不被释放问题
  //该函数的作用就是将外部函数的变量值改变了,同理这个函数也没有意义,
  //既然只是单纯改变外部函数的局部变量的话,那我们可以直接将这个函数体拿出来放在外部函数体里面就好了
  }
  //第三种情况
  return function(){
      a++;
      console.log(a);
      //该函数将符合闭包的所有情况了,导致内存不被释放的原因是在全局下
     //我们有afn一直引用该闭包,该闭包一直引用着外部函数的a变量
     //因为函数也是一个对象,如果对象一直被引用的话,内存将得不到释放,当我们把afn = null的话,将释放内存
     
   }
   //第四种情况
   var dom = document.getElementById("btn");
   dom.onclick = function(){
    /*这里函数即使是空的也会导致内存泄漏
      这里发生了两种情况
      1.dom.onclick这个函数隐式的调用了dom(dom.onclick函数中的this就是dom)导致监听事件不手动清除的话会一直存在内存中,
        也就是为什么我们onclick事件执行过之后,在次点击仍然可以发生监听事件,这里发生了类似于对象的属性引用了带对象造成了引用计数不可能为0 
      2.监听事件不可能是局部作用域的,就算该函数fn执行完之后 ,该fn函数内存被释放,我们继续执行点击事件,依然会发生响应,正常情况下fn内存如果被释放了,
        里面的函数或者是变量应该都被回收了,所以onclick事件是注册在全局作用域下的
        而在onclick事件内部引用了变量dom(this),所以导致该外部函数fn的内存将得不到释放
        我们把dom = null之后也就是onclick事件不再引用该函数fn的变量dom(this),使其fn内存得到释放 
      最后:这里函数内部的dom和全局作用的监听事件onclick无关,我们即使在该函数将其置为null也只是把该函数内部对btn元素的引用置为null,但是onclick事件是注册在dom元素上的,所以只要该dom元素在页面存在,则监听事件将会一直存在
        但是全局的onclick事件还存在的,比如我们这个onclick事件写到全局并把dom = null;但是并不影响我们点击事件的发生如下图
        dom2中提供了removeEventListener来移除监听事件
*/
   }
  dom = null;
  fn1();
  fn2();
}
var afn = fn();
afn(); //2
afn(); //3
afn = null;
image.png

js闭包的应用场景:这里是我在其他的地方看到的,在网页中需要一个遮罩层,调用的时候就创建一个,但是我们没必要每次调用的时候创建,如果存在就不创建,如果不存在就创建新的

function fn(){
  var a;
  return function(){
     return a || (a  =document.body.appendChild(document.createElement('div')) )
  }
}
var f = fn();
var a = document.getElementById("a");
var b = document.getElementById("b");
a.onclick = function(){
  f()(); //在a点击的时候开始调用创建遮罩层
}
b.onclick = function(){
  f()();  //由于a的变量会一直存在内存中,所以将不会重新创建遮罩层
}
1.封装变量,延续局部变量的寿命(计算乘积的简单函数)引入 result变量作为缓存,如果计算过的将不进行计算,并将其作为局部变量封装
let muti = (function(){
  var result = {};
  let key = [].join.call(arguments)
  var calucFun = function(){
    var count = 1;
    for(let i = 0;i<arguments.length; i++){
      count = count *arguments[i]
    }
    return count
  }
  return function(){
    if(result[key]) return result[key]
    return result[key] = calucFun.apply(null,arguments)
  }
})()

console.log(muti(1,2,3))

javascript学习(ECMAScript,DOM,BOM)https://www.jianshu.com/p/b7136540b379

上一篇下一篇

猜你喜欢

热点阅读