了解JavaScript中的闭包

来自菜鸟教程
跳转至:导航、​搜索

介绍

在本文中,您将介绍什么是闭包的基础知识以及 JavaScript 为何受益于闭包。

先决条件

如果您想继续阅读本文,您将需要:

  • 熟悉 JavaScript 变量、函数、作用域和回调。

定义闭包

闭包可以定义为由变量保持的持久范围。 像 JavaScript 和 Ruby 这样的语言支持闭包,它允许变量即使在它们的编程块已经执行之后也可以引用它们的父作用域,只要这些变量在某个地方有引用,闭包就存在。

考虑以下闭包示例:

function outerFunction() {
 let delayed = veryLongOperation();

 let innerFunction = function() { someCallback(delayed); }
}

innerFunction 是一个能够访问其父作用域元素的函数——例如 delayed 对象。

使用闭包

既然我们已经了解了闭包是什么,那么它们为什么有用呢?

JavaScript 在执行方面可以是异步的。 当操作完成时,必须使用回调。 此回调现在必须在与其调用者/父对象相同的执行上下文中运行。 这意味着任何对父级可用的变量或函数现在也必须对回调可用。 如果这些回调没有闭包,那么我们必须手动绑定所需的范围成员。

闭包通过在后台为我们处理这件事让我们的生活变得更加轻松。 如果函数内部有一个函数,则内部函数在其整个生命周期内都可以访问其范围成员。

考虑以下类定义示例:

let classExample = function () {
  let randomNumber = Math.floor(Math.random() * 10);

  return function() {
    console.log(randomNumber);
  }
}

内部函数的作用域链被扩展为包括 classExample 函数的成员。

闭包发挥作用的另一个例子是在柯里化期间。 柯里化是返回多个函数以减少数量的过程。 它被用于函数式编程范式以减少状态变化。

闭包可以与 currying 一起使用来为 JavaScript 类引入私有成员。

function curryingExample() {
  let message = "Hello.";

  return {
    getMessage: function() { console.log('private message', message); }
  };
}

创建一个新的 curryExample() 实例:

let curry = new curryingExample();

尝试访问 message

console.log('undefined message', curry.message);

console.log(curry.message); 执行时,值为 undefined

尝试使用 getMessage()

curry.getMessage();

curry.getMessage(); 执行时,值为 "Hello."

结论

闭包是 JavaScript 的一个非常微妙但强大的特性,理解它们是成为一名认真的 JavaScript 开发人员的非常重要的一步。

这在 Kyle Simpson 的 关于 Closures 的优秀文章中有更详细的解释。