深入理解 JavaScript 闭包:理论与实践的完美结合

前言

JavaScript中的闭包的确是一个比较晦涩难懂的知识点,今天整理和总结了一篇文章,对闭包的分析和使用。

一、闭包的定义

MDN 对闭包的定义是:

闭包是指那些能够访问自由变量的函数。

什么是自由变量呢?

自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。

简单来说,

闭包 = 函数 + 函数能够访问的自由变量。

从理论角度讲,所有的 JavaScript 函数都是闭包。因为函数在创建时就会将上层上下文的数据保存起来,哪怕访问的是简单的全局变量,这也相当于访问自由变量,此时使用的是最外层的作用域。但从实践角度看,满足以下两个条件的函数才算是闭包:

  1. 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回);
  2. 在代码中引用了自由变量。

二、实践中的闭包分析 

let globalValue = "我是全局变量";
function outerFunction() {
    let localValue = "我是局部变量";
    function innerFunction() {
        return localValue;
    }
    return innerFunction;
}
let resultFunction = outerFunction();
console.log(resultFunction()); 

在这段代码中,当 outerFunction 执行完毕后,它的执行上下文会从执行上下文栈中弹出,按常理其内部的局部变量 localValue 应该被销毁。但由于 innerFunction 形成了闭包,它依然可以访问 localValue。这是因为 innerFunction 的执行上下文维护了一个作用域链,通过这个作用域链,它能够找到 outerFunction 作用域下的变量对象,从而读取到 localValue 的值。

三、闭包应用的常见问题及解决

在 JavaScript 开发中,变量作用域和闭包的相互作用可能会导致一些与预期不符的结果。以下面这段代码为例:

let numList = [];
for (let i = 0; i < 3; i++) {
    numList[i] = function () {
        console.log(i);
    };
}
numList[0]();
numList[1]();
numList[2]();

1、ES6+环境

在现代 JavaScript(ES6 及以上版本)环境下,这段代码会依次打印出 012。这是因为let声明的变量具有块级作用域,每次循环都会创建一个新的块级作用域,每个作用域中的i都是独立的。当把函数赋值给numList[i]时,函数捕获的i是当前循环迭代中块级作用域内的i,所以在调用numList[0]()numList[1]()numList[2]()时,会分别打印出对应迭代中的i值。

2、ES5环境

 如果在 ES5 环境下,使用var声明变量,情况就大不相同了。因为var声明的变量只有函数作用域和全局作用域,没有块级作用域。将上述代码中的let改为var

let numList = [];
for (var i = 0; i < 3; i++) {
    numList[i] = function () {
        console.log(i);
    };
}
numList[0]();
numList[1]();
numList[2]();

此时,i是在全局作用域下声明的,循环结束后i的值为3。当函数被调用时,在函数自身作用域找不到i,就会到全局作用域查找,所以打印结果会是 3 个3

为了在 ES5 环境下实现类似let块级作用域的效果,利用闭包可以解决这个问题:

let numList = [];
for (var i = 0; i < 3; i++) {
    numList[i] = (function (j) {
        return function () {
            console.log(j);
        };
    })(i);
}
numList[0]();
numList[1]();
numList[2]();

 

在这段代码中,每次循环都创建了一个新的匿名函数,并将当前的i值作为参数j传递给它。这个匿名函数的活动对象中保存了传入的j,当内部返回的函数执行时,会从这个匿名函数的活动对象中找到对应的j值,从而正确打印出012

在 ES6 及以上环境中,虽然let已经能满足需求,但理解这种闭包的应用方式,对于深入掌握 JavaScript 的作用域和闭包机制仍然非常有帮助。

四、总结

闭包是 JavaScript 中一个强大而灵活的特性,它让函数可以访问并操作外部作用域的变量,即使这些变量所在的上下文已经销毁。理解闭包的概念和工作原理。

如果你喜欢这篇文章,可以点赞、收藏。

关注我,及时了解最新文章消息~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十八朵郁金香

感恩前行路上有你相伴

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值