闭包遇到的问题

闭包是指在一个函数内部定义的函数能够“记住”其所在的词法作用域,即使这个外部函数已经执行完毕。这意味着内部函数可以访问外部函数的变量,即使外部函数已经结束。重点是思想是:函数内部定义的函数可以访问外部函数的变量

举个例子,代码如下

function outerFunction() {
    let outerVar = 'I am outside!';
    function innerFunction() {
        console.log(outerVar);  // 访问外部函数的变量
    }
    return innerFunction;
}

const inner = outerFunction();
inner(); // 这会输出 'I am outside!'

在这个例子中,innerFunction 是在 outerFunction 内部定义的,并且它能够访问 outerFunction 中定义的 outerVar 变量。即使 outerFunction 执行完毕并返回后,innerFunction 仍然“记住”了 outerVar 的值,这就是闭包的特性

下面来分析一下实际项目中遇到的有关闭包的问题,代码如下(因为是直接从项目里截取出来的,所以格式有些问题)

            selectGroups.forEach(group => {
              // 每个组里面副标题只会有一个
              secondsTitle = group.querySelector('.arrow-right');
              // 让当前循环到的副标题展示出来
              secondsTitle.classList.toggle('allowAll'); 
              console.log(secondsTitle);  
              // 给所有副标题添加点击事件
              // 点击副标题展示当前副标题下的子内容
              secondsTitle.addEventListener('click',()=>{
                secondsTitle.classList.toggle('arrow-right'); 
                secondsTitle.classList.toggle('arrow-top');
                console.log(secondsTitle);
                items = group.querySelector('.select-items');
                items.classList.toggle('allowAll');
              });
            })
 

首先结构是循环每个selectGroups,每个selectGroups里都有一个secondsTitle,希望为当前这个secondsTitle添加点击事件。遇到的问题是,在当前只有两个selectGroup的情况下,每次点打印出来的都是第二个selectGroup里的secondsTitle。

排查了一会发现了问题所在。准确来说,问题是每次打印出来的都是最后一个selectGroup里的secondsTitle,也就是说每次循环的secondsTitle被覆盖,导致只有最后一个点击事件有效果。

我的理解:我看不出来有问题,理解中每一层循环secondsTitle,然后分别给当前secondsTitle添加点击事件,接下来一步步分析。

首先,secondsTitle没有定义,js引擎解析secondsTitle变量的时候,因为没有在当前作用域中找到相关声明,那么会向上寻找父级作用域直到全局作用域,如果还是没有就默认为全局变量var。

接下来,要知道闭包这个概念,前面已经说了它的一个特点,接下来是另一个特点,闭包会“记住”其所在的词法作用域中的变量引用,而不是变量的值。使用var声明的是全局变量,每次secondsTitle = group.querySelector('.arrow-right')实际上只改变了这个变量的值而没有改变引用,可以理解为在循环中给每个按钮(secondsTitle)分配了一个标记(点击事件)。但由于 var 的作用域特性,这些标记都指向同一个记号(secondsTitle 变量)。每次你循环时,记号上的内容(即变量的值)会被擦掉并写上新的内容,那么最后,只会有一个按钮,并且按钮上面的内容只会是最后一个循环到的文本,而不是预想中的每一个文本有一个按钮。

解决方法:为了让每个闭包捕获独立的变量,可以使用 let,因为它是块级作用域,每次循环都会创建一个独立的变量实例,闭包捕获的就是不同的变量引用,结合上面的例子思考就是创建了新按钮,并填写了当前文本,符合预期。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值