Skip to content
On this page

闭包

闭包其实就是一个可以访问其他函数内部变量的函数。

使用场景

  • 创建私有变量/方法
  • 延长变量的生命周期
  • 柯里化函数
  • 确保函数只执行一次(vue 中的 once 方法)
  • 数据缓存

防抖函数

js
function debounce(f, wait) {
  let timer
  // 返回的函数是闭包,保留了timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}

缓存数据

利用闭包特性,缓存数据

js
function cached(fn) {
  var cache = Object.create(null)

  return (function cachedFn(str) {
    var hit = cache[str]

    return hit || (cache[str] = fn[str])
  })
}

确保函数只执行一次

利用闭包特性,存储状态

js
function once(fn) {
  var called = false
  return function () {
    if (!called) {
      called = true
      fn.apply(this, arguments)
    }
  }
}

const fn1 = once(function () {
  console.log('无论你怎么调用,我只执行一次')
})

fn1()  // 无论你怎么调用,我只执行一次

fn1() // 不输出
fn1() // 不输出
fn1() // 不输出

创建私有变量/方法

JavaScript中,没有支持声明私有变量,但我们可以使用闭包来模拟私有方法

下面举个例子:

js
var makeCounter = (function () {
  var privateCounter = 0;

  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment: function () {
      changeBy(1);
    },
    decrement: function () {
      changeBy(-1);
    },
    value: function () {
      return privateCounter;
    }
  }
})();

var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

上述通过使用闭包来定义公共函数,并令其可以访问私有函数和变量,这种方式也叫模块方式

两个计数器 Counter1Counter2 是维护它们各自的独立性的,每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境,不会影响另一个闭包中的变量

延长变量的生命周期

一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用, 即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到延长变量的生命周期的目的

下面例子:

在页面上添加一些可以调整字号的按钮

js
function makeSizer(size) {
  return function () {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

柯里化函数

函数柯里化实现

其他

例如计数器、延迟调用、回调等闭包的应用,其核心思想还是创建私有变量和延长变量的生命周期