深入理解JavaScript闭包:从基础概念到实际应用
本文深入解析JavaScript闭包的核心概念,通过嵌套函数、返回值及对象引用传递等实例,阐明内部函数如何访问外部函数变量。文章还探讨了闭包在数据隐藏、定时器应用及潜在内存泄漏问题中的表现,帮助开发者掌握这一关键特性。
JavaScript闭包基础概念
国内技术社区往往缺乏深入的教学氛围。为了彻底搞懂JavaScript中的闭包概念,我查阅了大量英文资料。直到在Stack Overflow上看到一篇高质量解答,我才确信这就是闭包的本质。
以下是该解答的中文翻译,仅供参考。
提问者 Peter Mortensen 提出:
正如爱因斯坦所言:“如果你无法向六岁的孩子解释清楚,说明你自己也没真正理解。”我尝试向一位27岁的朋友解释JS闭包(JavaScript closure),但彻底失败了。
你会如何向一个充满好奇心的六岁孩子解释这个概念?
注意:我看过Stack Overflow上的其他示例,但那些解释对我没有帮助。
用户 Ali 的回答:
当函数内部嵌套另一个函数时,内部函数可以访问外部函数的变量。
示例代码如下:
function foo(x) {
var tmp = 3;
function bar(y) {
alert(x + y + (++tmp));
}
bar(10);
}
foo(2) 无论执行多少次,结果都会弹出 16。这是因为 bar 函数可以访问 foo 的参数 x 以及变量 tmp。
但这还不是闭包。只有当返回的是内部函数时,才构成闭包。内部函数会“封闭”外部函数的变量,直到内部函数执行完毕。
示例代码如下:
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + (++tmp));
}
}
var bar = foo(2); // bar 现在是一个闭包
bar(10); 上述脚本最终也会弹出 16。虽然 bar 不直接处于 foo 的内部作用域中,但它仍然可以访问 x 和 tmp。
然而,由于 tmp 仍然存在于 bar 闭包的作用域内,它每次调用时都会自增 1。
(考虑到六岁孩子的理解限制:我们可以返回多个闭包方法,例如返回它们的数组,或者将它们设置为全局变量。它们都指向相同的 x 和相同的 tmp,而不是各自拥有副本。)
值传递与对象引用的区别
注意:现在来点稍微复杂一点的内容。
上面的 x 是一个字面量(值传递)。与 JS 中其他字面量一样,当调用 foo 时,实参 x 的值被复制了一份,作为 foo 的参数 x。
问题来了,JS 处理对象时使用引用传递。因此,当你调用 foo 并传递一个对象时,foo 函数返回的闭包将引用最初的那个对象!
示例代码如下:
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + tmp);
x.memb = x.memb ? x.memb + 1 : 1;
alert(x.memb);
}
}
var age = new Number(2);
var bar = foo(age); // bar 现在是一个引用了age的闭包
bar(10); 不出意料,每次运行 bar(10),x.memb 都会自增 1。需要注意的是,x 每次指向同一个对象变量——age。运行两次 bar(10) 后,age.memb 会变成 2。
这与 HTML 对象的内存泄漏有关,不过这可能超出了当前回答的范围。
用户 JohnMerlino 对 Ali 说:
这里有一个不使用 return 关键字的闭包例子:
示例代码如下:
function closureExample(obj, text, timedelay) {
setTimeout(function() {
document.getElementById(objID).innerHTML = text;
}, timedelay);
}
closureExample('myDiv', 'Closure is created', 500); 闭包的访问范围与变量维持
深夜 1:37,用户 John Pick 这样回答:
JS 中的函数可以访问它们的:
1. 参数
2. 局部变量或函数
3. 外部变量(环境变量),包括
3.1 全局变量,包括 DOM
3.2 外部函数的变量或函数。
如果一个函数访问了它的外部变量,那么它就是一个闭包。
注意,外部函数不是必需的。通过访问外部变量,一个闭包可以维持这些变量的存活。在内部函数和外部函数的例子中,外部函数可以创建局部变量,并最终退出;但是,如果任何一个或多个内部函数在它退出后却没有退出,那么内部函数就维持了外部函数的局部数据。
一个典型的例子就是全局变量的使用。
闭包的技术定义与实际应用
用户 mykhal 这样回答:
维基百科对闭包的定义如下:
In computer science, a closure is a function together with a referencing environment for the nonlocal names (free variables) of that function.
从技术角度来讲,在 JS 中,每个函数都是闭包,因为它总是能访问在它外部定义的数据。
Since scope-defining construction in Javascript is a function, not a code block like in many other languages, what we usually mean by closure in Javascript is a fuction working with nonlocal variables defined in already executed surrounding function.
闭包经常用于创建含有隐藏数据的函数(但并不总是这样)。
示例代码如下:
var db = (function() {
// 创建一个隐藏的object, 这个object持有一些数据
// 从外部是不能访问这个object的
var data = {};
// 创建一个函数, 这个函数提供一些访问data的数据的方法
return function(key, val) {
if (val === undefined) { return data[key] } // get
else { return data[key] = val } // set
}
// 我们可以调用这个匿名方法
// 返回这个内部函数,它是一个闭包
})();
db('x'); // 返回 undefined
db('x', 1); // 设置data['x']为1
db('x'); // 返回 1
// 我们不可能访问data这个object本身
// 但是我们可以设置它的成员 看了这么多国外高手的解答,不知道你懂还是不懂,反正我是懂了。
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38
怎么用JavaScript策略模式重构电商促销计算逻辑?
2026-11-05 14:01:09
JS使用constructor判断数据类型可靠吗
2026-11-05 13:49:41
