官方链接:<HarmonyOS第一课>ArkTS语法介绍-华为开发者学堂
问题介绍:
function f(): () => number {
let count = 0;
let g = (): number => { count++; return count; };
return g;
}
let z = f();
z(); // 返回:1
z(); // 返回:2
闭包
闭包是由函数及声明该函数的环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。
在上例中,f函数返回了一个闭包,它捕获了count变量,每次调用z,count的值会被保留并递增。
介绍是这样,但是会有人看不懂,可以说服自己第一个会返回一,但是不知道为什么第二次count值保留,而不是重新let为0
解决:
让我们通过内存模型分析来彻底理解这个闭包行为:
function f(): () => number {
let count = 0; // 步骤1:在堆内存创建count变量
let g = (): number => { // 步骤2:创建函数对象g(携带作用域引用)
count++;
return count;
};
return g; // 步骤3:返回g时保留对count的引用
}
// 执行阶段
let z = f(); // 此时内存状态:
// - 创建执行上下文A
// - A.count = 0
// - A.g = 函数(携带对A.count的引用)
z(); // 调用时:
// 1. 通过g的作用域链找到A.count
// 2. A.count → 1
// 3. 保持对A.count的引用
z(); // 再次调用:
// 修改的是同一个A.count → 2
关键原理表格
阶段 | 内存变化 | 变量状态 |
---|---|---|
f() 被调用时 | 创建新的执行上下文(包含count=0) | 生成新的闭包环境 |
g 函数创建时 | 记录当前词法环境(包含对count的引用) | 形成闭包 |
f() 执行完成时 | 执行上下文A不会销毁(因为g仍引用A.count) | 闭包维持A环境存活 |
第一次调用 z() | 通过闭包找到A.count → 0 → 自增为1 | A.count = 1 |
第二次调用 z() | 仍然访问同一个A.count → 1 → 自增为2 | A.count = 2 |
重新执行 let z2 = f() | 创建新的执行上下文B(新count=0) | z和z2操作完全独立的count变量 |
理解误区澄清
误区:"每次调用z()都会重新初始化count" 真相:只有当f()
被重新调用时才会创建新环境。z始终指向第一次f()
调用产生的闭包环境。
通过调试工具验证
在Chrome DevTools中执行以下操作:
- 设置断点在
count++
处 - 观察每次调用时闭包中的count值变化:
Closure (f) // 首次调用后保持活跃 count: 0 → 1 → 2
类比理解
把闭包想象成一个保险箱:
f()
每次调用会新建一个保险箱(新的执行上下文)- 放入初始现金count=0
- 给你一把钥匙
g
(函数引用) - 每次用钥匙操作(调用z())都是在操作同一个保险箱里的钱
// 计数器工厂 function createCounter(start = 0) { let count = start; return { increment: () => ++count, get: () => count }; } const counter = createCounter(10); counter.increment(); // 11 ← 闭包维持count状态 counter.increment(); // 12
经典应用场景
通过理解闭包机制,就能掌握JavaScript中状态保持的核心原理。这正是React Hooks(如useState)实现的基础原理之一