JavaScript 系列之闭包(二)

323 阅读2分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

三、闭包的应用

3.1 利用闭包实现模块化开发

3.1.1 模块模式

封装了私有变量和方法,而只暴露了一个接口供外部调用。

function cool() {
  let a = "cool";
  let b = [1, 2, 3];
  function doA() {
    console.log(a);
  }
  function doB() {
    console.log(b.join("!"));
  }
  return {
    doA: doA,
    doB: doB,
  };
}
let foo = cool();
foo.doA(); //cool
foo.doB(); //1!2!3

cool() 返回一个用对象字面量语法表示的对象

这个对象中含有内部函数而不是内部数据变量的引用,保持内部数据变量是隐藏且私有的状态。

doA()doB()函数具有涵盖模块实例内部作用域的闭包,当通过返回一个含有属性引用的对象的方式来将函数传到词法作用域外时,已经创建了闭包。

  • 模块模式必须要有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)
  • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有状态。

3.1.2 单例模式

let foo = (function cool() {
  let a = "cool";
  let b = [1, 2, 3];
  function doA() {
    console.log(a);
  }
  function doB() {
    console.log(b.join("!"));
  }
  return {
    doA: doA,
    doB: doB,
  };
})();
foo.doA(); //cool
foo.doB(); //1!2!3

将模块函数转换成立 IIFE,立即调用这个函数并将返回值直接赋值给单例的模块实例标识符 foo

3.2 利用闭包模拟块级作用域

var data = [];
for (var i = 0; i < 3; i++) {
  (function () {
    var j = i;
    data[j] = function () {
      console.log(j);
    };
  })();
}
data[0](); //0
data[1](); //1
data[2](); //2

四、闭包的优缺点

4.1 优点

  1. 缓存。将变量隐藏起来不被 GC 回收。
  2. 实现柯里化。利用闭包特性完成柯里化。

我们可以通过闭包来实现一个计数器,而不用担心全局变量的污染:

function foo() {
  let num = 0;
  return function() {
    num++;
    console.log(num);
  };
}
const f = foo();
f(); // 1
f(); // 2

4.2 缺点

  1. 内存消耗。闭包产生的变量无法被销毁。
  2. 性能问题。由于闭包内部变量优先级高于外部变量,所以需要多查找作用域链的一个层次,一定程度影响查找速度。
function assignEvent() {
  let id = "123";
  document.getElementById("save-btn").onclick = function (event) {
    saveDocument(id);
  };
}

image.png