当前位置: 首页 > 知识库问答 >
问题:

javascript - 一个关于 for 和 onclick() 事件的问题?

武彭薄
2024-02-20

一个学习案例中遇到的问题

index.html

<div class="d-flex gap-3">    <div class="box"></div>    <div class="box"></div>    <div class="box"></div></div>

index.css

.box {    width: 100px;    height: 50px;    border: 1px solid;}

index.js

var boxs = document.getElementsByClassName('box');for (var i = 0; i < boxs.length; i++) {    boxs[i].onclick = function () {  // i = 1,2,3        console.log(i); // i = 3    }}

上述代码中, 我在循环里为每个 .box 添加点击事件, 但是在事件里的i始终是循环体执行完毕后的结果 3, 为什么会出现这种情况?

因为之前写代码在 onclick 事件里基本都是用 this, 或者用 let 来代替 var 来定义变量, 所以绕过了这个坑
但现在遇到了这样的 bug, 还是想问下导致这个问题的原因是什么?

补充

关于 var 的特性, 我能想到的是 全局污染和变量提升, 简单地说

  • 全局污染: for 中定义 var, 作用域会提升到全局
  • 变量提升: 允许先调用后定义

但是上述两个特性都不能合理的解释 -> 为什么在for循环中定义的onclick, 循环变量i在onclick事件中永远是for最终执行后的i?

共有5个答案

屠君墨
2024-02-20

补充下,这题知识点是js 的作用域, 点击按钮的时候打印i, js引擎会去找click函数外部的作用域,变量i 的值。然而触发click事件时,<script> 引入的代码早就执行完了【此时i 为3】。你打个断点去chorme debugger, 一步步执行下就可以理解了。js 知识内容建议去看《JavaScript忍者秘籍》。

鲜于峰
2024-02-20

这个循环转化这样就好理解了

var i = 0;{    boxs[i].onclick = function () {          console.log(i); // i = 3    }}++i;{    boxs[i].onclick = function () {          console.log(i); // i = 3    }}++i;{    boxs[i].onclick = function () {          console.log(i); // i = 3    }}

如果我们使用的是let,可以这样理解

{     let t = 0;    {            let i = t;        boxs[i].onclick = function () {              console.log(i); // i = 3        }    }    ++t;    {        let i = t;        boxs[i].onclick = function () {              console.log(i); // i = 3        }    }    ++t;    {        let i = t;        boxs[i].onclick = function () {              console.log(i); // i = 3        }    }}
陆昊
2024-02-20

补充一个比较好理解的

var i = 0;console.log(i); // 0var i = 3;

上述代码中输出的 i = 0, 因为 console.log() 是同步方法, 当它执行完毕后才会执行下面的 i = 3


var i = 0;boxs[i].onclick = function() {    console.log(i); // 3}var i = 3;

而在 onclick 中, 因为是一个异步方法, 所以执行到时不会发生阻塞, 而是同时执行了 i = 3, 导致最终 click 触发时 i 的值已经是 3 了

林玮
2024-02-20

你换个思路,在你的代码中有几个i呢,是不是只有一个,所有当for循环结束i的值也只能是一个,因为i一个变量不可能同时又是1又是2又是3

孔欣可
2024-02-20

这个问题的原因是 JavaScript 的变量提升(Hoisting)特性。

在 JavaScript 中,变量的声明会被提升到其所在作用域的顶部,但赋值操作不会。在你的代码中,var i 这一行代码被提升到了循环的顶部,但赋值 i = 0i = 1i = 2 这些操作并没有被提升。

所以,当你在循环中点击 .box 元素时,实际上 i 的值已经被设为了 3。这是因为循环结束后,i 的值已经变成了 3,并且这个值被赋给了每个 .box 元素的 onclick 事件处理器。

如果你希望在事件处理器中 i 的值是 012,你需要将 var i 改为 let i。使用 let 关键字声明的变量有块级作用域,并且不会发生变量提升。这样,每个 .box 元素的 onclick 事件处理器都会在其自己的作用域中获取到正确的 i 值。

以下是你的代码,其中将 var i 改为了 let i

<div class="d-flex gap-3">    <div class="box"></div>    <div class="box"></div>    <div class="box"></div></div>
.box {    width: 100px;    height: 50px;    border: 1px solid;}
var boxs = document.getElementsByClassName('box');for (let i = 0; i < boxs.length; i++) {    boxs[i].onclick = function () {  // i = 0,1,2        console.log(i); // i = 0,1,2    }}
 类似资料:
  • 上面是代码,想执行'person1.func3()'。 结果如下图

  • 我已经开始做一个游戏,有点类似于“花比尔盖茨的钱”,但我说过要稍微改变一下。我做了第一项,我试着用JS编辑它,所以每次你按“买”,它会从你身上移除1美元,或者,当你按“卖”给你1美元。问题是,每次我点击,它只算一次。另外,如果我按“买进”,然后按“卖出”,它会立即给我11美元,而不是10美元。文件名:index.html 文件名:script.js

  • 本文向大家介绍JavaScript中关于base64的一些事,包括了JavaScript中关于base64的一些事的使用技巧和注意事项,需要的朋友参考一下 base64 其实是一种编码转换方式, 将 ASCII 字符转换成普通文本, 是网络上最常见的用于传输8Bit字节代码的编码方式之一。 base64 由字母 a-z 、 A-Z 、 0-9 以及 + 和 / , 再加上作为垫字的 = , 一共6

  • 问题内容: 我在一个div内放了一个嵌入式Flash电影,在主div上放了一个javascript onclick事件处理程序,但是没有捕捉到点击,这是怎么回事? 码: 问题答案: 最好将所有swf都视为z阶无穷大。Flash是最重要的,只有很少的事情可以阻止它。另一方面,如果您可以访问SWF本身的代码,或者可以使用另一个SWF加载当前的SWF,则可以使用几个不同的Flash命令来处理页面的Jav

  • 背景:基于Element UI 开发的项目,使用到了Tree树型控件 需求:页面有一个tree控件(可选择)如图 这个控件在切换月份的时候可能存在二级节点下有新增的节点或者减少的节点, 如果 ‘一级2’被全部中了,那么切换其他月份如果 ‘一级2’节点下有新增的节点也要勾选上。 树的深度不确定(后端返的),数据量大的时候遍历判断会影响性能,求问各位大佬 有什么比较好的办法处理吗 找出两颗树的差异的节

  • 问题内容: 如果我想执行一个函数,我更喜欢做内联js: 因为它更容易调试。 但是,我听到有人说不要使用内联js,并这样做: 为什么建议使用js事件监听器? 问题答案: 基本上,它与整体有关,我认为一切都应分开。因此,将HTML / CSS / JS都分开。它使您的HTML更加整洁,并且我认为无需它就更易于浏览。 然后,当/如果需要进行较大的更改时,您必须有足够的空间来将内联JS转移到外部文件,或者