引言
如果你正在学习 JavaScript ,那么闭包这个概念你一定绕不开。它是 JavaScript 中最核心、最强大的特性之一,也是面试中最高频的考点。那么,为什么闭包这么重要?
1. 它是理解 JavaScript 运行机制的关键
闭包的本质是函数能够记住并访问其词法作用域,即使函数在其定义之外被调用。
2. 它是模块化编程的基础
在 ES6 模块系统出现之前,闭包是实现数据私有化和模块化的主要方式。即使在今天,闭包仍然是编写高质量 JavaScript 代码的必备技能。
在深入闭包之前,我们需要先了解作用域链的概念,因为它是闭包的基础。
作用域链
1. 什么是作用域链?
JavaScript 在预编译时会创建一个调用栈,然后在调用栈中加入执行上下文,执行上下文中又有 变量环境 和 词法环境。每一个执行上下文的变量环境中都存在一个 outer 指针,用来指向外部的执行上下文。当 v8引擎 在查找一个变量时,在当前执行上下文中没有找到,就会顺着 outer 所指向的那个执行上下文查找,以此类推,直到找到全局为止,我们把这个查找的链条叫做 作用域链。
2. 作用域链的形成
当前执行上下文→ outer → 外部执行上下文 → outer → 全局执行上下文
闭包
1. 闭包是什么?
- 一个函数执行完毕后,它的执行上下文会被销毁
- 根据作用域的查找规则,内部函数一定可以访问外部函数中的变量
那么当一个外部函数的内部函数被拿到外部函数之外来执行,哪怕外部函数执行完毕了,被内部函数引用的那部分变量依然需要保留,我们把这部分变量的集合称之为闭包。
2. 闭包的形成条件
function foo(){
let a = 1;// 外部函数变量
function bar(){
console.log(a);//内部函数引用外部变量
}
return bar;//内部函数被拿到外部执行
}
const fn = foo();//fn就是一个闭包
fn();
foo执行上下文预编译完成后被删除,但其词法环境中的变量 a 被引用了,所以留下一个闭包,闭包中保留下 a = 1 。所以 闭包 = bar函数 + a 变量所在的词法环境。
3.闭包的生命周期
创建:外部函数执行时
存活:内部函数被外部引用期间
销毁:内部函数不再被引用时
闭包的优缺点
优点
1.数据私有化:封装变量,防止全局污染
2.模块化代码:创建独立的模块
3.记忆状态:保存函数执行的上下文
缺点 1.内存泄漏:变量不被释放 2.性能问题:过度使用会增加内存负担
闭包的使用场景
1.数据私有化
function add() {
let count = 0 //私有变量,外部无法直接访问
return function() {
count++
return count
}
}
//使用示例
const bar = add()
console.log(bar())// 1
console.log(bar())// 2
console.log(bar())// 3
console.log(add.count);//undefined (无法直接访问)
2.解决 var 循环问题
var arr = []
for(var i = 1; i <= 5; i++) {
arr.push(function(){
console.log(i);//输出: 5,5,5,5,5
})
}
for(var n = 0; n <arr.length; n++) {
arr[n]()
}
输出:5 5 5 5 5
原因:var 声明的 i 是全局变量,循环结束时 i = 5,定时器回调执行时访问的是同一个 i。
解决方案1:使用闭包
var arr = []
for(var i = 1; i <= 5; i++) {
function fn(j){//创建独立作用域
arr.push(function(){
console.log(j);//输出:1,2,3,4,5
})
}
fn(i)//将当前 i 作为参数传入
}
for(var n = 0; n <arr.length; n++) {
arr[n]()
}
原理:闭包捕获了每次循环的 j 值,每个闭包都有独立的 j ,互不影响
解决方案2:使用 let (ES6)
var arr = []
for(let i = 1; i <= 5; i++) {
arr.push(function(){
console.log(i);//输出:1,2,3,4,5
})
}
for(var n = 0; n <arr.length; n++) {
arr[n]()
}
原理:let 在循环中会为每次迭代创建新的绑定
总结
闭包是 JavaScript 中函数能够记住并访问其创建时词法作用域的特性。它源于作用域链的查找规则和 JavaScript 的垃圾回收机制,是实现数据私有化、模块化和状态保存的核心技术。