本文深入解析JavaScript闭包的核心机制,通过作用域链和Active Object对象的变化过程,详细阐述闭包如何延长外部变量生命周期。针对经典的循环引用错误案例,分析其成因并提供基于立即执行函数的标准解决方案,帮助开发者掌握闭包的原理与实战技巧。
什么是闭包:
闭包是一个存在内部函数的引用关系。
该引用指向的是外部函数的局部变量对象(前提是内部函数使用了外部函数的局部变量)
闭包的作用:
延长外部函数变量对象的生命周期
使用闭包能够间接的从函数外部访问函数内部的私有变量一、常见的闭包
function outer() {
var a = 1
function inner() {
console.log(a) //1
}
inner()
}
outer()
二、实例详解
function createFunc() {
var result = new Array()
for (var i = 0; i < 10; i++) {
result[i] = function () {
console.log(i)
}
}
return result
}
var result = createFunc()
result[0]() //10
result[1]() //10
result[2]() //10
result[3]() //10
result[4]() //10
result[5]() //10
result[6]() //10
result[7]() //10
代码执行前,系统会创建一个全局对象,包含全局属性,并将其置于全局上下文作用域链的顶端。同时,该全局对象也会被放入每一个函数的作用域链顶端。以本例为例,具体结构如图所示。

初始化阶段结束后,代码开始执行。此时会创建一个新的对象,称为Active Object(活动对象),其中存放函数参数等信息,并将其压入createFunc函数的作用域链中。

由于在createFunc函数内部仍然定义了函数result[i]...,因此在代码执行前,该内部函数会形成相应的作用域链。

随后开始执行createFunc函数。当执行完毕后,createFunc的作用域链状态如图所示。此时result为一个数组,且Active object已从createFunc作用域链的顶部移除。

接着开始执行result[0](此处以result[0]为例,其他元素同理)。在调用result[0]之前,系统会创建一个新的Active object对象,并将其放入result[0]执行作用域栈中。具体结构如图所示。

在函数执行过程中,需要访问变量i,但Active object中并不存在i。因此,系统沿着作用域链向上查找,在createFunc中找到i,其值为10。因此,最终打印的值均为10。在createFunc执行完毕后,其创建的对象并未被垃圾回收,因为result[0]中的i仍然保持对该对象的引用。
该问题的解决方法如下所示:设置一个立即执行函数,每个下标对应的函数均为立即执行函数。当立即执行函数执行时,每个函数的上下文对象中都会存在正确的下标值。
function createFunc() {
var result = new Array()
for (var i = 0; i < 10; i++) {
result[i] = (function (num) {
return function() {
console.log(num)
}
})(i)
}
return result
}
var result = createFunc()
result[0]() //0
result[1]() //1
result[2]() //2
result[3]() //3
result[4]() //4
result[5]() //5
result[6]() //6
result[7]() //7
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注本站的更多内容!