作用域初步(一):数据为什么会绑定错误

本文探讨了JavaScript中为多个DOM元素绑定事件时常见的作用域问题,包括如何避免因作用域变化导致的数据绑定错误,并提供了三种解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      需要同时为多个元素绑定事件并绑定数据,但如果没有理解透彻作用域的有效范围与事件响应机制,常常会发生数据绑定错误甚至代码执行错误。
      应用场景:从后台返回了多个指标类型(数量不定),需要将这些指标类型绑定到多个按钮上,每次点击按钮时,将该指标的id提交到后台,从而能获取到该类型的指标数据,假定后台返回的数据如下所示。

//  后台返回的数据 
var datas = [{
    id : 1,
    name : "CPU"
}, {
    id : 2,
    name : "内存"
}, {
    id : 3,
    name : "硬盘"
}]

      常用的操作方法都是遍历数据,然后依次创建按钮,并为按钮注册事件,如下:

var i = 0,
    btnInd = null;
for(var i = 0, len = datas.length; i < len; i ++) {
    btnInd = document.createElement('button');
    btnInd.textContent = datas[i].name;
    //  将按钮插入页面中,这里是body,业务看需求
    document.body.appendChild(btnInd);
    btnInd.addEventListener('click', function() {
        //  以alert来代替发送ajax请求
        alert(datas[i].id);
    })
}

      执行上述代码,结果令人惊讶,竟然是“Uncaught TypeError: Cannot read property ‘id’ of undefined”,为什么呢?原因是在用户触发点击事件时,i已经变为len了(这里是3)。
      那为何回调函数没有与主函数的作用域进行绑定呢?因为所有的按钮都共用一个变量i,而i值在方法的主进程里一直在发生变化,使用多个变量后代码如下。

 var i = 0,
    i0 = 0, //  声明多个变量
    i1 = 1,
    btnInd = null;
for(var i = 0, len = datas.length; i < len; i ++) {
    btnInd = document.createElement('button');
    btnInd.textContent = datas[i].name;
    //  将按钮插入页面中,这里是body,业务看需求
    document.body.appendChild(btnInd);
    //  也可以用if
    switch(i) {
        case 0:
            btnInd.addEventListener('click', function() {
                //  以alert来代替发送ajax请求
                //  也可以直接使用零
                alert(datas[i0].id);
            })
            break;
        case 1:
            btnInd.addEventListener('click', function() {
                //  以alert来代替发送ajax请求
                alert(datas[i1].id);
            })
            break;
        //  这样的代码实在是太low了,剩余的代码,不一一列举
        ……
    }

}

      上面的办法虽然解决了问题,但实在是太low了,并且需要知道迭代的次数,在数组容量稍微大一点的情况下,那就是程序员的噩梦。
      解决的办法有三种,分别列举如下:
一、 面向对象解决法, 充分利用this;

//在这里为按钮添加属性
btnInd.ajaxId = datas[i].id
btnInd.addEventListener('click', function() {
    //  以alert来代替发送ajax请求
    //  通过this来指向所需要的数据
    alert(this.ajaxId);
})

二、 函数解决法;

//  在循环外添加函数registerEvent
function registerEvent(btn, id) {
    btn.addEventListener('click', function() {
        //  以alert来代替发送ajax请求
        //  通过this来指向所需要的数据
        alert(id);
    })
}
//  循环内部的注册代码改为
document.body.appendChild(btnInd);
registerEvent(btnInd, datas[i].id)

三、 es6段作用域解决法;

//  修改for循环的迭代值声明方式,把var改为let
//  目前浏览器还不支持ES 6,需要编译后才能执行
for(let i = 0, len = datas.length; i < len; i ++) {
    //  其中的代码与第一段注册的代码完全相同
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值