JavaScript跳棋游戏中循环变量绑定事件后值异常问题求助
问题分析与解决方案
你遇到的这个问题是JavaScript里非常经典的闭包与函数作用域变量陷阱,我来帮你拆解清楚:
为什么点击后i和j总是8?
你用var声明的i和j是函数作用域的变量,在整个CreateBoard函数里只有一个i和一个j的引用。循环执行的时候,你给每个td绑定的点击事件函数并没有立即执行,而是把对i和j的引用保存了下来。当循环结束时,i和j已经完成了所有迭代,最终值变成了8(因为循环条件是i < 8,最后一次循环后i自增到8,循环停止)。所以当你点击td触发函数时,它会去取这两个变量的最终值,也就是8。
而你在循环内部直接用alert(i)能正确显示,是因为那时候循环还在进行,i和j正处于当前迭代的数值。
解决方案
这里有几种简单有效的修复方式,你可以任选一种:
方案1:用let替换var声明循环变量
ES6的let是块级作用域的,每次循环迭代都会创建新的i和j变量,每个事件监听器都会绑定当前迭代的那个变量实例,这样点击时就能拿到正确的数值了。修改CreateBoard里的循环:
function CreateBoard() { var board = document.createElement("table"); board.cellSpacing = 0; // 把var改成let for (let i = 0; i < 8; i++) { var tr1 = document.createElement("tr"); // 这里的j也改成let for (let j = 0; j < 8; j++) { var td1 = document.createElement("td"); td1.setAttribute("id", "td" + i + j); td1.addEventListener("click", function () { CheckIandJForLater(i, j); }); // 后续的样式设置代码不变... tr1.appendChild(td1); } board.appendChild(tr1); } document.body.appendChild(board); }
方案2:用立即执行函数表达式(IIFE)创建独立作用域
如果你需要兼容ES6之前的环境,可以用IIFE把每次迭代的i和j封装到独立的作用域里:
function CreateBoard() { var board = document.createElement("table"); board.cellSpacing = 0; for (var i = 0; i < 8; i++) { var tr1 = document.createElement("tr"); for (var j = 0; j < 8; j++) { var td1 = document.createElement("td"); td1.setAttribute("id", "td" + i + j); // 用IIFE包裹,把当前的i和j作为参数传进去 (function(currentI, currentJ) { td1.addEventListener("click", function () { CheckIandJForLater(currentI, currentJ); }); })(i, j); // 后续样式代码不变... tr1.appendChild(td1); } board.appendChild(tr1); } document.body.appendChild(board); }
方案3:把i和j存在元素的dataset属性里
还可以在创建td的时候,把当前的i和j存到元素的data-*属性中,点击时从元素本身获取:
function CreateBoard() { var board = document.createElement("table"); board.cellSpacing = 0; for (var i = 0; i < 8; i++) { var tr1 = document.createElement("tr"); for (var j = 0; j < 8; j++) { var td1 = document.createElement("td"); td1.setAttribute("id", "td" + i + j); // 把i和j存到dataset里 td1.dataset.i = i; td1.dataset.j = j; td1.addEventListener("click", function () { // 从当前点击的元素中获取i和j var currentI = parseInt(this.dataset.i, 10); var currentJ = parseInt(this.dataset.j, 10); CheckIandJForLater(currentI, currentJ); }); // 后续样式代码不变... tr1.appendChild(td1); } board.appendChild(tr1); } document.body.appendChild(board); }
这三种方案都能解决你的问题,其中方案1是最简洁现代的写法,推荐优先使用。
内容的提问来源于stack exchange,提问作者user15577846
相关产品推荐
相关产品推荐

