首页 > 试题广场 >

依次点击4个li标签,哪一个选项是正确的运行结果?

[单选题]
现有如下html结构
<ul>
 <li>click me</li>
 <li>click me</li>
 <li>click me</li>
 <li>click me</li>
</ul>
运行如下代码:
var elements=document.getElementsByTagName('li');
var length=elements.length;
for(var i=0;i<length;i++){
    elements[i].onclick=function() {
        alert(i);
    }
}
依次点击4个li标签,哪一个选项是正确的运行结果?
  • 依次弹出1,2,3,4
  • 依次弹出0,1,2,3
  • 依次弹出3,3,3,3
  • 依次弹出4,4,4,4
我来一个专业解释吧:这里考的是JS的运行机制! 事件(click,focus等等),定时器(setTimeout和setInterval),ajax,都是会触发异步,属于异步任务;js是单线程的,一个时间点只能做一件事,优先处理同步任务; 按照代码从上往下执行,遇到异步,就挂起,放到异步任务里,继续执行同步任务,只有同步任务执行完了,才去看看有没有异步任务,然后再按照顺序执行! 这里for循环是同步任务,onclick是异步任务,所以等for循环执行完了,i变成4了,注意:这里因为i是全局变量,最后一个i++,使得i为4(后面的onclick函数,最后在循环外面执行,不受i<length限制); 所以for循环每执行一次,onclick事件函数都会被挂起一次,共4次; for循环结束后,点击事件 触发了4个onclick函数,接着输出4个4! 对大家有用的话,希望点个赞👍!
发表于 2017-10-17 12:55:32 回复(47)
闭包
(1)概念
闭包是指有权访问另一个函数作用域中的变量的函数。
(2)特性
闭包只能取得包含函数中任何变量的最后一个值。
(3)创建方式
在一个函数内部创建另一个函数。
(4)原理
在一个函数内部中定义的函数会将包含函数(外部函数)的活动对象添加到其作用域中,直至解除对内部函数的应用,内部函数被销毁,外部函数的活动对象才会被销毁。
(5)实例
function closure(){
    var result=[];
    for (var i = 0; i < 4; i++) {
        result[i]=function(){
            console.log(i);
            return i;
        }
    };
    return result;
}
var result=closure();
for (var i = 0; i < 4; i++) {
    result[i]();
};
4 4 4 4
发表于 2016-07-29 11:12:49 回复(6)
var elements=document.getElementsByTagName('li');
    var length=elements.length;
    for(var i=0;i<length;i++){
        elements[i].onclick=(function(a){
            return function(){
            alert(a);
            }          
        })(i);
    }
//用此代码可以依次弹出 0,1,2,3(闭包可以“包养”外部函数变量)
编辑于 2015-08-17 16:49:10 回复(6)
M.J头像 M.J
D

因点击为匿名函数,其执行到这里的时候 i 已是 4,即不会改变~
编辑于 2020-07-12 13:18:53 回复(18)
个人理解:这个题涉及到了作用域链,在函数内部要alert(i)但是函数中并没有变量i,此时只能延伸作用域链,向上一级去找。此时for循环已经执行完毕,因为最后是i++,所以结果是4;最终输出的都是4。然而仍存在疑问:为啥到上一层找的时候for循环就已经执行完毕了呢?
别人的解读:还涉及到了JS的运行机制:事件(click,focus等),定时器(setTimeout,setInterval),ajax都会触发异步,属于异步任务;JS是单线程的,一个时间只能做到一件事,有先处理同步任务,然后再按照顺序执行。这里for循环是同步任务,onclick是异步任务,所以等for循环执行完了,i变成了4。(这个就解决了为什么到上一层找 i 的时候for循环就已经执行完毕了)。
发表于 2019-04-11 23:55:03 回复(0)
闭包问题,点击相当于调用了其中的闭包函数,闭包把i保存在作用域链中,同一个作用域中的不同闭包共用这个i,所以返回i最后保存的值
发表于 2016-05-04 10:46:04 回复(0)
答案:D
这是一个闭包。
把for(var i=0;i<4;i++){
   elems[i].onclick=function(){
      alert(i);
   };
}
拆成这样来看就明白
for(var i=0;i<4;i++){
   function(){
      alert(i);  //这里输出的是4,因为被循环了
   };
}
和这个差不多
var i = 0;
function(){
     alert(i);   //这里输入的是0;
}
alert的i,其实还是for里面的那个i,因为i循环了4次,所以alert的i累加了4次,就成了4,就是你说的总是输出最大值,
发表于 2015-01-29 16:49:16 回复(0)
怎么错误的我就不说了,把var 改成Let就可以了。
发表于 2021-08-27 20:21:16 回复(0)
还没学到闭包,倒是在红宝书第三章讲的var let const 的不同中,提到了这个问题
发表于 2021-01-18 00:15:31 回复(0)
这是典型的JS中的函数闭包问题。表面上看,似乎每个函数都应该返自己的索引值,即位置 0 的函数
返回 0,位置 1 的函数返回 1,以此类推。但实际上,每个函数都返回 4。因为每个函数的作用域链中
都保存着 函数的活动对象,所以它们引用的都是同一个变量 i 。当函数返回后,变量 i 的值是 4,此时每个函数都引用着保存变量 i 的同一个变量对象,所以在每个函数内部 i 的值都是 4。但是,我们可以通过创建另一个匿名函数强制让闭包的行为符合预期,如下所示。、

    for(var i=0;i<length;i++){
        elements[i].onclick=function(num){
            returnn function(){
            return num;
};
    }(i);


发表于 2018-07-14 09:26:42 回复(0)
仔细看代码就明白,不是点一次触发一次循环,而是即使不触发onclick,for循环已经走完了,当触发时alert出的i肯定就是最终值了,而因为跳出循环的前提条件必定是i=4才能跳出来,所以不选C,选D
发表于 2018-03-16 15:03:43 回复(0)
function(){
        alert(i);
}
在这个function里并没有定义i,所以就会沿着作用域链向外查找父级有没有i。而在全局作用域中, for(var i=0;i<length;i++) 循环已经执行完毕了,i为最后一个4了,所以click后,都会alert出4咯~
发表于 2017-03-26 11:50:05 回复(0)
闭包 (1)概念 闭包是指有权访问另一个函数作用域中的变量的函数。 (2)特性 闭包只能取得包含函数中任何变量的最后一个值。 (3)创建方式 在一个函数内部创建另一个函数。 (4)原理 在一个函数内部中定义的函数会将包含函数(外部函数)的活动对象添加到其作用域中,直至解除对内部函数的应用,内部函数被销毁,外部函数的活动对象才会被销毁。 (5)实例 function closure(){     var result=[];     for (var i = 0; i < 4; i++) {         result[i]=function(){             console.log(i);             return i;         }     };     return result; } var result=closure(); for (var i = 0; i < 4; i++) {     result[i](); }; 4 4 4 4
发表于 2017-03-07 20:01:19 回复(0)
我觉得其实这个可以看成是一个闭包,事件函数中没有变量i,因此到作用域链上找,在上一级作用域中找到了i。至于为什么此时的i值为4,是因为只有当js加载解析好了之后,li才添加了点击事件,则此时i必然为4
发表于 2016-01-14 20:38:33 回复(0)
D  典型的“循环closure”问题
发表于 2015-01-26 11:01:41 回复(0)
我的理解是:每个li标签的onclick事件执行时,本身onclick绑定的function的作用域中没有变量i,i为undefined,则解析引擎会寻找父级作用域,发现父级作用域中有i,且for循环绑定事件结束后,i已经赋值为4,所以每个li标签的onclick事件执行时,alert的都是父作用域中的i,也就是4。这是作用域的问题。
编辑于 2015-08-20 21:48:27 回复(30)
执行过程是相当于创建了4个onclick函数elements[0].onclick=function(){}....elements[3].onclick=function(){} 然后点击了那个元素再执行对应函数,此时的i是4
发表于 2022-03-09 13:37:57 回复(0)
D,要1234弹应该把var改成let
发表于 2022-02-24 19:44:51 回复(0)
var 没有作用域,let 有
发表于 2021-08-21 16:46:10 回复(0)
js中的单线程中的ajax、settimeout、onclick等等都是属于异步任务,js优先处理同步任务例如for循环等等,所以等i循环到4才开始执行异步中的onclick,自然点击出来的都是4
发表于 2021-06-27 12:30:48 回复(0)