Skip to content

闭包

  • 有权访问另一个函数作用域中的变量的函数
  • 函数 A 内部有函数 B, B 可以访问到函数 A 变量,那么函数 B 就是闭包

存储在堆中

产生闭包

js
function fn() {
  var mty = 10;
  return function () {
    return mty;
  };
}

作用

  • 延续局部变量寿命、封装变量

应用场景

访问私有方法和变量

  • return 返回函数:无网、音量检测:调用之后,返回销毁方法、无网、音量

  • 柯里化: 函数可以分多次调用,从而得到结果,设置私有变量

  • bind:暂存 this context

  • 自执行函数:for 循环里面有异步操作,异步操作需要用到 index

  • 上报:

js
// report 函数调用结束后, img 随即被销毁,或许还没来得及发送 http 请求
var report = function (src) {
  var img = new Image();
  img.src = src;
};
report("http://xxx.com/getUserInfo");

// 改造后

var sendLog = (funtion(){
  var img = [];
  return (src) => {
    var img = new Image();
    imgs.push(img);
    img.src = src;
  };
})();
  • 封装变量:for 循环里面有异步操作,异步操作需要用到 index
js
for (var i = 0; i < 10; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i);
    }, 0);
  })(i);
}

document.getElementById

js
var getId = function (id) {
  return document.getElementById(id);
};
getId("div1");

我们也许思考过为什么不能用下面这种更简单的方式: var getId = document.getElementById; getId( 'div1' );

这是因为许多引擎的 document.getElementById 方法的内部实现中需要用到 this。这个 this 本来被期望指向 document,当 getElementById 方法作为 document 对象的属性被调用时,方法内部的 this 确实是指 向 document 的。

但当用 getId 来引用 document.getElementById 之后,再调用 getId,此时就成了普通函数调用, 函数内部的 this 指向了 window,而不是原来的 document。

js
document.getElementById = (function (func) {
  return function () {
    return func.apply(document, arguments);
  };
})(document.getElementById);
var getId = document.getElementById;
var div = getId("div1");

缺点

  • 使用不当容易造成内存泄露

内存泄露

  • 闭包,未用到变量未及时释放

  • 定时器未释放

  • dom 引用:dom 删除时,引用未解除

  • 意外全局变量

  • 监听事件:未正确销毁

  • dom 删除引用未解除

js
// 拿到待删除节点:
var self = document.getElementById("to-be-removed");
// 拿到父节点:
var parent = self.parentElement;
// 删除:
var removed = parent.removeChild(self);
removed === self; // true

为什么不创建一个全局变量来代替这个局部变量?

  • 因为全局变量会被污染或者被修改。
  • 闭包能够访问里面的变量,是由于作用域链,用到了函数嵌套中,内部函数能够访问父级函数作用域的变量这个理念。

内存泄漏排查

在 MIT 许可下发布