关于闭包

316 阅读3分钟

子函数可以访问到其他函数作用域的变量,就叫做闭包

闭包指子函数可以访问外部作用域变量的函数特性,即使在子函数作用域外也可以访问。如果没有闭包那么在处理事件绑定,异步请求时都会变得困难。

JS中的所有函数都是闭包
闭包一般在子函数本身作用域以外执行,即延长作用域

最简单的闭包:

function hd() {
  let name = '后盾人';
  return function () {
  	return name;
  }
}
let hdcms = hd();
console.log(hdcms()); //后盾人

使用闭包返回数组区间元素:

let arr = [3, 2, 4, 1, 5, 6];
function between(a, b) {
  return function(v) {
    return v >= a && v <= b;
  };
}
console.log(arr.filter(between(3, 5)));

下面是在回调函数中使用闭包,当点击按钮时显示当前点击的是第几个按钮:

<body>
  <button message="后盾人">button</button>
  <button message="hdcms">button</button>
</body>
<script>
  var btns = document.querySelectorAll("button");
  for (let i = 0; i < btns.length; i++) {
    btns[i].onclick = (function(i) {
      return function() {
        alert(`点击了第${i + 1}个按钮`);
      };
    })(i);
  }
</script>

移动动画 计时器中使用闭包来获取独有变量(

1.点击抖情况,因为哪个时候left还定义在子作用域下面,相当于创建了很多个环境,即每点击一次加一次定时器和一个新的left,总是重置为1,当然会出现点击倒退来回抖动。

2.点击一直加速情况,此时虽然left在父环境下,但是定时器函数还在子环境中,也就是每次都创建一个新的定时器,比如设置为1000毫秒加一个速度,当我们点击十次时,此时加了是个定时器,相当于此时为100毫秒加一个速度 )

<body>
  <style>
    button {
      position: absolute;
    }
  </style>
  <button message="后盾人">houdunren</button>
  <!-- <button message="hdcms">hdcms</button> -->
</body>
<script>
  let btns = document.querySelectorAll("button");
  btns.forEach(function(item) {
    let bind = false;
    item.addEventListener("click", function() {
      if (!bind) {
        let left = 1;
        bind = setInterval(function() {
          item.style.left = left++ + "px";
        }, 100);
      }
    });
  });
</script>

闭包问题

1.内存泄漏

闭包特性中上级作用域会为函数保存数据,从而造成的如下所示的内存泄漏问题

<body>
  <div desc="houdunren">在线学习</div>
  <div desc="hdcms">开源产品</div>
</body>
<script>
  let divs = document.querySelectorAll("div");
  divs.forEach(function(item) {
    item.addEventListener("click", function() {
      console.log(item.getAttribute("desc"));
    });
  });
</script>

此时因为click事件回调函数是一直在监听的,不会被销毁,此时我们每次点击都会创建很多个回调函数的独立的内存,当程序很大很复杂时会影响性能,这就闭包带来的内存泄漏的问题

下面通过清除不需要的数据解决内存泄漏问题

let divs = document.querySelectorAll("div");
divs.forEach(function(item) {
  let desc = item.getAttribute("desc");
  item.addEventListener("click", function() {
    console.log(desc);
    console.log(item);//null
  });
  item = null;//把元素设置为null,空对象是不会占有内存的,此时清除了内存泄漏的问题,使性能变得更加的快速
});

2.this指向

this 总是指向调用该函数的对象,即函数在搜索this时只会搜索到当前活动对象。

let hd = {
  user: "后盾人",
  get: function() {
    return function(){
        return this.user;//
    }
  }
};
console.log(hd.get()()); //undefined

此时this在function下,是指向window的,此时return的是window的user属性,显然是输出undefined

解决方法: 1.在父级作用域下用变量保存this

2.用箭头函数,箭头函数的this指向上下文

1. let hd = {
      user: "后盾人",
      get: function() {
        let that = this;//这个是对象的方法,这个环境下this是指向hd对象的,即会有user属性 
        return function() {
            return that.user;//后盾人
        };
      }
    };
    console.log(hd.get()());//后盾人



2. let hd = {
      user: "后盾人",
      get: function() {
        return () => this.user;//后盾人
      }
    };
    console.log(hd.get()()); //后盾人