Skip to content

闭包及其使用场景

什么是闭包

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)

也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域

  • 示例
js
function init() {
  var name = "Mozilla"; // name 是一个被 init 创建的局部变量
  function displayName() {
    // displayName() 是内部函数,一个闭包
    alert(name); // 使用了父函数中声明的变量
  }
  displayName();
}
init();

displayName()没有自己的局部变量。然而,由于闭包的特性,它可以访问到外部函数的变量

闭包的使用场景

数据封装和私有变量

闭包允许在对象外部隐藏变量,只通过定义在闭包中的函数来访问这些变量,实现了数据的封装和私有化。

js
function createCounter() {
  let count = 0;
  return {
    increment: function () {
      count++;
    },
    decrement: function () {
      count--;
    },
    getCount: function () {
      return count;
    },
  };
}

const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 输出: 1
counter.decrement();
console.log(counter.getCount()); // 输出: 0

模拟私有方法

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();
console.log(counter1.value()); // 输出: 0
counter1.increment();
counter1.increment();
console.log(counter1.value()); // 输出: 2

回调函数中的状态保持

在异步编程中,闭包允许回调函数访问外部函数中的变量,即使外部函数已经执行完毕。

js
function asyncGreeting(name) {
  setTimeout(function () {
    console.log("Hello, " + name);
  }, 1000);
}

asyncGreeting("Alice"); // 1秒后输出: Hello, Alice

函数柯里化(Currying)

闭包允许部分应用一个函数的参数,返回一个新的函数,等待接收剩余的参数。

js
function multiply(a, b) {
  return a * b;
}

function curriedMultiply(a) {
  return function (b) {
    return multiply(a, b);
  };
}

var double = curriedMultiply(2); // 创建一个新函数,这个函数将一个数乘以2
console.log(double(3)); // 输出: 6

循环中创建闭包

在循环中使用闭包解决经典的“循环变量问题”,确保循环体内的异步操作能够获取到正确的索引值。

js
for (var i = 0; i < 3; i++) {
  (function (index) {
    setTimeout(function () {
      console.log("Index: " + index);
    }, 1000);
  })(i);
}

闭包的注意事项

如果不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响

例如,在创建新的对象或者类时,方法通常应该关联于对象的原型,而不是定义到对象的构造器中。

原因在于每个对象的创建,方法都会被重新赋值

js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function () {
    return this.name;
  };

  this.getMessage = function () {
    return this.message;
  };
}

如有转载或 CV 的请标注本站原文地址