JavaScript 中的闭包是什么?你将如何/为什么要使用它?
主题
闭合JavaScript
在GitHub上编辑
TL;DR
在 Kyle Simpson 撰写的 "You Don't Know JS" (YDKJS) 一书中,闭包定义如下:
闭包是指一个函数能够记住并访问其词法作用域,即使该函数在其词法作用域之外执行
简单来说,函数可以访问它们在创建时在其作用域中的变量。 这就是我们所说的函数的词法作用域。 闭包是一个函数,即使外部函数执行完毕后,它仍然保留对这些变量的访问权限。 这就像函数记住了它最初的环境。
function outerFunction() {const outerVar = '我位于 innerFunction 之外';function innerFunction() {console.log(outerVar); // `innerFunction` 仍然可以访问 `outerVar`。}return innerFunction;}const inner = outerFunction(); // `inner` 现在持有对 `innerFunction` 的引用。inner(); // "我位于 innerFunction 之外"// 即使 `outerFunction` 已经执行完毕,`inner` 仍然可以访问在 `outerFunction` 内部定义的变量。
要记住的关键点:
- 当内部函数可以访问其外部(词法)作用域中的变量时,就会发生闭包,即使外部函数已经执行完毕。
- 闭包允许一个函数记住它被创建的环境,即使该环境不再存在。
- 闭包在 JavaScript 中被广泛使用,例如在回调、事件处理程序和异步函数中。
了解 JavaScript 闭包
在 JavaScript 中,闭包是一个捕获其声明的词法作用域的函数,允许它访问和操作来自外部作用域的变量,即使该作用域已关闭。
以下是闭包的工作原理:
- 词法作用域:JavaScript 使用词法作用域,这意味着函数对变量的访问由其在源代码中的实际位置决定。
- 函数创建:当创建一个函数时,它会保留对其词法作用域的引用。 此作用域包含在创建闭包时在其作用域中的所有局部变量。
- 维护状态:闭包通常用于以安全的方式维护状态,因为闭包捕获的变量在函数外部不可访问。
ES6 语法和闭包
使用 ES6,可以使用箭头函数创建闭包,这提供了更简洁的语法并以词法方式绑定 this
值。 这是一个例子:
const createCounter = () => {let count = 0;return () => {count += 1;return count;};};const counter = createCounter();console.log(counter()); // 输出:1console.log(counter()); // 输出:2
React 中的闭包
闭包无处不在。 下面的代码显示了一个在单击按钮时增加计数器的简单示例。 在此代码中,handleClick
形成一个闭包。 它可以访问其外部作用域变量 count
和 setCount
import React, { useState } from 'react';function Counter() {// 使用 useState 钩子定义一个状态变量const [count, setCount] = useState(0);// 此 handleClick 函数是一个闭包function handleClick() {// 它可以访问 'count' 状态变量setCount(count + 1);}return (<div><p>Count: {count}</p><button onClick={handleClick}>Increment</button></div>);}function App() {return (<div><h1>Counter App</h1><Counter /></div>);}export default App;
为什么使用闭包?
使用闭包提供以下好处:
- 数据封装:闭包提供了一种创建私有变量和函数的方法,这些变量和函数无法从闭包外部访问。这对于隐藏实现细节并以封装的方式维护状态非常有用。
- 函数式编程:闭包是函数式编程范例的基础,它们用于创建可以传递和稍后调用的函数,保留对创建它们的范围的访问权限,例如 部分应用或柯里化。
- 事件处理程序和回调:在 JavaScript 中,闭包通常用于事件处理程序和回调,以维护状态或访问在定义处理程序或回调时作用域中的变量。
- 模块模式:闭包在 JavaScript 中启用 模块模式,允许创建具有私有和公共部分的模块。