JavaScript编程
闭包
闭包是一种技术,它将函数与其周围的变量(词法环境)捆绑在一起。通常,闭包在像 JavaScript 这样的函数式编程语言中实现,并用于支持柯里化(Currying)。
词法环境
首先,我们展示函数如何访问其词法环境。这本身并不是闭包。
"use strict";
function incrementByCertainValue(param) {
return param + certainValue;
}
let certainValue = 10;
alert(incrementByCertainValue(8)); // 输出:18
certainValue = 100;
alert(incrementByCertainValue(8)); // 输出:108
在这个例子中,函数 incrementByCertainValue
将其参数 param
与变量 certainValue
的值相加。由于 incrementByCertainValue
和 certainValue
都在同一个代码块中定义,函数可以访问该变量。每次调用函数时,它会读取 certainValue
的值并使用它来计算返回值。
闭包
为了将上述例子扩展为闭包,我们需要将一个(内部)函数与其词法环境中的变量绑定在一起,并通过返回这个内部函数来保存这个状态。
"use strict";
function incrementByCertainValue(param) {
// 声明一个执行操作的函数(仅声明,暂未调用)
const add = function (certainValue) {
// 访问词法环境中的变量 'param' 以及其参数 'certainValue'
return param + certainValue;
}
return add;
}
const incrementByFive = incrementByCertainValue(5);
alert(incrementByFive(8)); // 输出:13
const incrementBySix = incrementByCertainValue(6);
alert(incrementBySix(8)); // 输出:14
alert(incrementByFive(10)); // 输出:15
解析
外部函数 incrementByCertainValue
:
- 接受一个参数
param
; - 定义一个内部函数
add
,该函数接受另一个参数certainValue
; - 内部函数除了可以访问自己的参数外,还可以访问词法环境中的变量
param
; - 返回内部函数
add
。
到目前为止,函数只是声明,还没有实际运行。
当在第 12 行定义变量 incrementByFive
时,它被初始化为 incrementByCertainValue(5)
的返回值。这是代码运行以及闭包技术生效的关键点。在 incrementByCertainValue
内部,参数 5
被绑定到变量 param
上。接着,创建了内部函数 add
,并在其词法环境中保留了对 param
的引用。这个内部函数接受一个参数 certainValue
,稍后需要调用时提供。incrementByCertainValue
的返回语句返回了内部函数 add
,这个函数绑定了值 5
。
注意:函数名 add
是内部定义的,在 incrementByCertainValue
外部不可见。
当第二次调用 incrementByCertainValue
时,传入参数 6
,它将与另一个单独的函数绑定。
运行逻辑说明
-
定义闭包:
外部函数incrementByCertainValue
的作用是创建并返回一个内部函数。这个内部函数能够访问外部函数的参数param
。 -
保存状态:
每次调用incrementByCertainValue
时,传入的参数param
都会绑定到一个新的内部函数中,并保存在闭包的词法环境里。这样,每个调用都会生成一个独立的函数实例,它们各自的param
值互不干扰。 -
闭包实例:
- 第一次调用
incrementByCertainValue(5)
,返回的闭包函数绑定了param=5
。 - 第二次调用
incrementByCertainValue(6)
,返回的闭包函数绑定了param=6
。
- 第一次调用
-
执行闭包:
调用incrementByFive(8)
时:- 内部函数
add
读取它的词法环境中的param=5
。 - 接受调用参数
certainValue=8
,计算并返回5 + 8 = 13
。
调用
incrementBySix(8)
时:- 内部函数
add
读取它的词法环境中的param=6
。 - 接受调用参数
certainValue=8
,计算并返回6 + 8 = 14
。
- 内部函数
优势与应用场景
-
状态保存:
闭包能够保存外部函数作用域中的状态变量,即使外部函数已经执行完毕。 -
数据隔离:
每次创建的闭包实例是独立的,互不影响,这种特性特别适合多实例管理的场景。 -
回调与异步操作:
在事件处理或异步操作中,闭包能够有效地捕获并保存上下文信息。 -
函数式编程:
闭包是实现函数柯里化和高阶函数的重要工具,广泛应用于函数式编程风格中。
例如:实现一个计数器的闭包:
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter1 = createCounter();
console.log(counter1()); // 输出:1
console.log(counter1()); // 输出:2
const counter2 = createCounter();
console.log(counter2()); // 输出:1
在这个例子中,每次调用 createCounter
都会创建一个新的闭包,保存独立的 count
状态变量。
总结
闭包是 JavaScript 中强大的编程工具,能够帮助我们在复杂场景下管理数据状态、实现函数式编程,并提升代码的可读性和复用性。