闭包及垃圾回收机制

闭包

概念作用
是一种函数,在函数中嵌套函数读取函数内部的变量
内层的作用域访问它外层函数作用域让这些变量始终保持在内存之中
变量作用域:全局作用域和局部作用域
  1. 函数内部可以使用全局变量
  2. 函数外部不可以使用局部变量
  3. 当函数执行完毕,本作用域的局部变量会销毁
function fn() {
	var num = 10;
	return function(){
		console.log(num);//10
	}
}
var f = fn();
f();//fn外部的作用域可以访问fn内部的局部变量
  • 闭包应用场景

    GC机制 : 在JavaScript中,如果一个对象不再被引用,那么这个对象就会被GC回收,否则这个对象一直会保存在内存中。

    1. 封闭作用域
      封闭作用域优点
      封闭空间1、不污染全局空间;
      小闭包2、内部所有的临时变量执行完毕都会释放不占内存;
      匿名函数自调3、可以保存全局数据;
      全局变量私有化4、更新复杂变量
      (function (){
          var age=18;
          console.log(age);
      })();//匿名函数不需要调用,进入到函数中就执行了,输出为18
      (function(){
          console.log(age);
      })();//报错了,age is not defined
      //虽然var age=18;是一个全局变量,但是在第二个函数中是拿不到的,因为它在一个封闭的作用域中。
      
    2. 作用域链

      嵌套之间的函数会形成作用域链,每次对变量的访问实际上都是对整条作用域链的遍历查找,先查找最近的作用域,最后再查找全局作用域,如果在某个作用域找到了对量就会结束本次查找过程。

    3. 保存作用域

      一句话:将数据绑定在指令上运行,让指令不再依赖全局数据。

      var A=function(){
          return function(){};
      }
      <button id="btn">点我</button>
      (function(document){
          var btn=document.getElementById("btn");
          var btn1=document.getElementById("btn");
          var btn2=document.getElementById("btn");
          var btn3=document.getElementById("btn");
      })(document)//通过第二个括号将全局变量传入到闭包中,使其变成局部变量,优化调用。也就是保存了全局的数据,局部直接使用。
      
    4. 高级排他
      <style>
              li{
                  background-color: #ccc;
                  border: 1px solid #000;
                  cursor: pointer;
              }
              .current{
                  background-color: red;
              }
      </style>
      <ul>
          <li class="current"></li>
          <li></li>
          <li></li>
          <li></li>
          <li></li>
          <li></li>
          <li></li>
          <li></li>
          <li></li>
          <li></li>
      </ul>
      普通写法:
      var allLis = document.getElementsByTagName("li");
      for (var i = 0; i < allLis.length; i++) { //第一次 遍历所有的li
        allLis[i].onmouseover = function () {
          for (var j = 0; j < allLis.length; j++) {//第二次 每触发一次鼠标事件都重新遍历所有的li
            allLis[j].className = "";//鼠标经过一次就清空所有行
          }
          this.className = "current";//再给当前的行加上类名
        }
      }
      提高性能:
      var allLis = document.getElementsByTagName("li");
      var lastOne = 0;
      for (var i = 0; i < allLis.length; i++) { //遍历所有的li
        (function (index) {
          allLis[i].onmouseover = function () {
            //每鼠标经过一次先清空上一次的
            allLis[lastOne].className = "";
            //再给当前的li添加
            this.className = "current";
            //记录当前的索引
            lastOne = index;
          };
        })(i);//利用闭包,传入每次循环的当前索引i,从而“锁定”鼠标经过事件时传入对应的索引值i
      }
      
    5. 参数传递
      //传统方法,使得图片移动
      var left = 0,
         right = 0,
         speed = 50;
       var lImg = document.getElementById("l");
       var rImg = document.getElementById("r");
       lImg.onmousedown = function () {
         left -= speed;
         this.style.marginLeft = left + "px";
       };
       rImg.onmousedown = function () {
         right += speed;
         this.style.marginLeft = right + "px";
       };
      //闭包(当两个事件同步的时候)
      function move(speed) {
         var num = 0;
         return function () {
           num += speed;
           this.style.marginLeft = num + "px";
         };
       }
       var lImg = document.getElementById("l");
       var rImg = document.getElementById("r");
       lImg.onmousedown = move(-50);
       rImg.onmousedown = move(50);
       //输入的参数是独立的,不会影响。
      
    6. 函数防抖与节流

      防抖:我们输入每一个字符时,都会发送一次请求,增大了服务器的压力

在这里插入图片描述

 节流:当稍微改变浏览器窗口大小,就会执行很多次业务,这样**浏览器性能将会受影响,可能会造成卡顿**

 ```js
 window.onresize = function(){
             console.log("执行了一些业务")
         }
 ```

在这里插入图片描述

 为了解决这样的问题,就需要实现防抖与节流了

 ##### 实现防抖:

 ```js
 function debounce(callback,delay){
             //定义一个计时器
             timer = null
             return function(...args){
                 //如果进来的时候有计时器
                 if(timer){
                     //清除计时器
                     clearTimeout(timer)
                 }
                 timer = setTimeout(()=>{
                     // callback(args[0])
                     callback.apply(this,args)
                 },delay)
             }
         }
 ```

 闭包的作用,我们把time放在了函数中,**避免了全局污染**

 ##### 实现节流:

 ```js
 window.onresize = throttle(fn,1000)
         function fn(){
             console.log("执行了一些业务")
         }
         function throttle(callback,delay){
             //判断是否正在定时
             let flag = false
             return function(...args){
                 if(flag == true){
                     return
                 }
                 //如果不在定时,则开始定时
                 flag = true
                  setTimeout(()=>{
                     callback.apply(this,args)
                     //执行完成,结束定时
                     flag = false
                  },delay)
             }
         }
 ```
  1. 延长局部变量的生命 (解决数据丢失的问题)
    var report = function (src) {
        var img = new Image();//当report函数调用结束后,Image对象随即被JS引擎垃圾回收器回收
        img.src = src;
    };
    report("http://www.xxx.com/getClientInfo"); //可能还没来得及发出http请求,所以可能导致此次上报数据的请求失败
    
    使用闭包把Image对象封闭起来,就可以解决数据丢失的问题:
    var report = (function() {
        var imgs = [];//在内存里持久化
        return function(src) {
            var img = new Image();
            imgs.push(img);//引用局部变量imgs
            img.src = src;
        }
    }());
    report('http://www.xxx.com/getClientInfo');//把客户端信息上报数据
    
8. ##### 占用内存

   函数执行完成后,函数内部的局部变量没有释放,占用内存时间会边长,容易造成内存泄露。

   ```js
   function fn() {
   	var num = 10;
   	return function(){
   		console.log(num);//10
   	}
   }
   var f = fn();
   f();
   f = null;//释放内存
   ```

   

9. ##### 垃圾回收机制(GC)

   JS中的内存的分配和回收都是自动完成的,内存在不适用的时候会被垃圾回收器自动回收。

   - 内存的生命周期

     1. 内存分配:声明变量、函数、对象时,系统自动分配内存
     2. 内存使用:即读写内存,也就是使用变量、函数
     3. 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存

   - 说明:

     全局变量一般不会回收(关闭页面回收)

     一般情况下局部变量的值,不用了,会被自动回收

   - 内存泄漏
     程序中分配的内存由于某种原因程序未释放或无法释放
  • 基本数据类型:string,number、boolean、undefined、null
    复杂数据类型(引用数据类型):Object、Array、Date等

  • 堆栈空间分配区别:

    1. 栈:函数参数值、局部变量的值等、基本数据类型放到栈里面

    2. 堆:复杂数据类型放到堆里面

      在这里插入图片描述

  • 简单数据类型 是存放在栈里面 里面直接开辟一个空间存放的是值

    • 复杂数据类型 首先在栈里面存放地址 十六进制表示 然后这个地址指向堆里面的数据

      • 引用计数法:

        • IE浏览器使用:看一个对象是否有指向它的引用,没有引用了就回收对象
          1. 引用一次,+1,多次引用会累加++
          2. 减少一个引用 -1 –
          3. 引用次数是0,则释放内存
          4. 循环引用:两个对象相互引用,引用次数永远不会是0,导致内存泄露
      • 标记清除法:

        1. 将 “不再使用的对象” 定义为 “无法到达的对象”
          1. 从根部(全局对象)扫描对象,能查到的就是使用的,查不到的就要回收
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值