事件监听器作用域外无法获取数组对象值?原因及解决方法
问题分析与解决
问题重现
有两个带有data-id和data-price属性的按钮,通过addEventListener绑定点击事件,点击时向外部定义的arr数组添加对象。事件监听器内部能正常读取数组值,但外部读取时报错Cannot read properties of undefined (reading 'id')。
HTML代码
<button data-id="1" data-price="29">press1</button> <button data-id="2" data-price="60">press2</button>
JavaScript代码
const arr = []; const btn = document.querySelectorAll('button'); for(let button of btn){ button.addEventListener('click', (el) => { const id = el.target.dataset.id; const price = el.target.dataset.price; arr.splice(0,0, {'id':id, 'price':price }); // console.log( arr[0]['id'] ); // 点击后能正常输出对应id // console.log( arr[1]['id'] ); // 点击第二个按钮后能正常输出2 }); } console.log( arr[0]['id'] ); // 报错:Cannot read properties of undefined (reading 'id')
原因分析
核心问题是代码执行顺序:
- 页面加载完成后,JS代码自上而下执行,外部的
console.log(arr[0]['id'])会在用户点击按钮之前就运行。 - 此时
arr还是空数组,arr[0]的值是undefined,尝试读取undefined的id属性自然会抛出错误。 - 而事件监听器里的代码只有在用户点击按钮时才会执行,这时候数组已经被添加了元素,所以能正常读取。
解决方法
方法1:在事件触发后执行读取逻辑
把需要读取数组的代码放到点击事件的回调里,或者在数组更新后调用处理函数:
const arr = []; const btn = document.querySelectorAll('button'); for(let button of btn){ button.addEventListener('click', (el) => { const id = el.target.dataset.id; const price = el.target.dataset.price; arr.splice(0,0, {'id':id, 'price':price }); // 点击后立即读取并输出 console.log(arr[0]['id']); // 调用外部处理函数 handleArrayUpdate(); }); } // 封装数组处理逻辑 function handleArrayUpdate() { if(arr.length > 0) { console.log(arr[0]['id']); } else { console.log('数组为空'); } }
方法2:设置手动触发读取的时机
添加专门的按钮,点击后再读取数组内容:
<button data-id="1" data-price="29">press1</button> <button data-id="2" data-price="60">press2</button> <button id="readBtn">读取数组</button>
const arr = []; const btn = document.querySelectorAll('button'); const readBtn = document.getElementById('readBtn'); for(let button of btn){ button.addEventListener('click', (el) => { const id = el.target.dataset.id; const price = el.target.dataset.price; arr.splice(0,0, {'id':id, 'price':price }); }); } // 点击读取按钮时再访问数组 readBtn.addEventListener('click', () => { if(arr.length > 0) { console.log(arr[0]['id']); } else { console.log('还没有点击任何按钮'); } });
方法3:用Proxy监听数组变化
通过Proxy监听数组的修改操作,当数组更新时自动执行读取逻辑:
const arr = []; // 创建代理对象监听数组修改 const proxyArr = new Proxy(arr, { set(target, property, value) { target[property] = value; // 数组变化时自动读取 if(target.length > 0) { console.log('数组更新后第一个元素的id:', target[0]['id']); } return true; } }); const btn = document.querySelectorAll('button'); for(let button of btn){ button.addEventListener('click', (el) => { const id = el.target.dataset.id; const price = el.target.dataset.price; // 向代理数组添加元素 proxyArr.splice(0,0, {'id':id, 'price':price }); }); }
内容的提问来源于stack exchange,提问作者Atilla
相关产品推荐
相关产品推荐

