JS 在函数定义中使用 for 循环变量,在迭代过程中函数不会改变

JS Use for loop variable in function definition, without the function changing during iterations

我正在编写一个游戏,其中用户必须 select 从后端发送的其中一张卡片,但在收到之前我不知道是什么卡片或多少张卡片。我必须为每个按钮制作一个按钮,并将选择的按钮发送到后端。因为我不知道会有多少张卡片,所以我在for循环中定义了每个按钮的动作。

由于我使用循环变量访问必须发送到后端的数字代码,因此在每次循环迭代后,函数发送的数字代码都会发生变化。

我是这样解决问题的:

(cards是一个包含卡片的数组,对于其中的任何一张卡片,card['code']是它的数字代码。sendJSON中的11表示发送的信息是选择的卡片)

for (var i = 0; i < cards.length; i++) {
    container.innerHTML += `<button><img src="Cards/${cards[i]['code'].toString()}.png" width="346" height="529" id="${cards[i]['code'].toString()}"></button>`;

    // Here is the problem:
    eval('document.getElementById(' + cards[i]['code'].toString() + ').onclick = async function f() {await sendJSON(11, ' + cards[i]['code'] + ')}')
}

问题是 eval() 因为它不仅不安全,而且性能也很差,所以我想知道...

有更好的方法吗?

您可以将代码存储为 data-* 属性 并对每个元素使用相同的事件处理程序(或使用注释中提到的事件委托)。我还建议不要将 innerHTML 用于这种简单的标记,尤其是不要在循环内使用(请参阅 Barmar 的回答以了解为什么它无论如何都不起作用):

function handler(event) {
  sendJSON(11, event.dataset.code);
}

for (var i = 0; i < cards.length; i++) {
  const img = document.createElement('img');
  img.src = `Cards/${cards[i]['code']}.png`;
  img.dataset.code = 'code';
  img.width = 346;
  img.height = 529;
  img.onclick = handler;

  const button = document.createElement('button');
  button.appendChild(img);
  container.appendChild(button);
}

因为你把代码放在 id 属性中,事件监听器可以简单地使用 this.id 来获取它,你不需要将它复制到函数中。

您还需要使用 insertAdjacentHTML() 而不是连接到 innerHTML。连接时,容器中的所有 HTML 都将被重新解析,之前图像上的所有事件侦听器都将丢失。 insertAdjacentHTML() 只解析新的 HTML 并保留所有旧元素不变。

for (var i = 0; i < cards.length; i++) {
  container.insertAdjecentHTML('beforeend', `<button><img src="Cards/${cards[i]['code']}.png" width="346" height="529" id="${cards[i]['code']}"></button>`);

  document.getElementById(cards[i]['code']).addEventListener("click", async function f() {
    await sendJSON(11, this.id)
  });
}

也不需要所有 toString() 调用。代入模板文字会自动将其转换为字符串。