红宝书第六讲:作用域链与闭包:厨房里的调味料架原理
资料取自《JavaScript高级程序设计(第5版)》。
查看所有教程:红宝书学习大纲
一、什么是作用域链?(洋葱层级结构)
想象你在厨房做菜,每个调料瓶按使用频率由近到远摆放:
- 当前厨房台面:手边的盐、酱油 → 相当于函数内部变量(局部作用域)
- 冰箱里的辣椒酱:常用但稍远 → 外层函数的变量(父级作用域)
- 储藏室的大米:最远的储备 → 全局变量(全局作用域)
作用域链的查找顺序:
function 厨房() {
const 盐 = "适量盐"; // 第二层作用域
function 炒菜() {
console.log(盐); // ✅ 能找到冰箱里的盐
console.log(大米); // ✅ 能找到储藏室的大米
}
炒菜();
}
const 大米 = "5kg"; // 第三层(全局)
厨房();
二、闭包:私人调料罐
闭包就像你出门前把常用调料装进便携罐带走:
- 定义:内部函数保留外部变量的引用,“保鲜”这部分变量的值。
- 关键特点:即使外层函数已执行完毕,闭包仍然能访问其变量。
代码示例:
function 制盐机() {
let 盐堆存量 = 1000; // 盐堆存量被内部函数“锁住”
return function 取盐(需要量) {
盐堆存量 -= 需要量; // 每次取盐减少存量
console.log(`剩余盐量:${盐堆存量}g`);
};
}
const 我的取盐动作 = 制盐机();
我的取盐动作(100); // 剩余盐量:900g
我的取盐动作(200); // 剩余盐量:700g
- 分析:
制盐机()
执行后,本应销毁的盐堆存量
被取盐
函数保留,形成闭包1。
三、闭包的两面性
益处:
数据保护:避免变量全局污染,如封装计数器:
function 创建计数器() { let count = 0; return () => ++count; // 只能通过闭包修改 } const 计数器 = 创建计数器(); 计数器(); // 1
潜在问题:
- 内存泄漏:如果闭包引用大型数据且未被释放,可能影响性能:
风险示例:
function 加载大数据() {
const 大数据 = new Array(1000000).fill('🚗'); // 内存大户
return function 处理函数() {
// 即使不再需要大数据,闭包仍保留引用
};
}
let 保存闭包 = 加载大数据();
保存闭包 = null; // ✅ 需手动解除引用
四、生活中的闭包场景
- 按钮点击统计:记录用户点击按钮次数。
- 接口请求防重复:闭包保存最后一次请求标识。
模块化开发:借助IIFE(立即执行函数)封闭作用域:
const 模块 = (function() { const 内部数据 = "私有"; return { get数据: () => 内部数据 }; })(); console.log(模块.get数据()); // '私有'
目录:总目录
上篇文章:红宝书第五讲:函数声明与表达式小白详解
下篇文章:红宝书第七讲:this绑定与强制类型转换详解(小白指南)
总结与练习
- 理解步骤:先画作用域层级 → 再看闭包如何抓取变量。
- 练习:实现一个缓存计算结果的闭包函数。
重要提醒:闭包虽强大,但控制引用范围,避免内存包袱过重!
- 3 ↩
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。