练习题-----可以很好地诠释js的运行机制
先看一下题:
现有如下html结构
1 <ul> 2 <li></li> 3 <li></li> 4 <li></li> 5 <li></li> 6 </ul>
运行如下代码:
1 var elements = document.getElementsByTagName('li'); 2 var length = elements.length; 3 for(var i=0;i<length;i++) { 4 elements[i].onclick = function() { 5 console.log(i) 6 } 7 }
依次点击四个li标签,下列选项哪个正确?
A、1234 B、0123 C、3333 D、4444
这个是选择D的,以下是解释:
这里考的是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!
有以下几个解决方法:(输出结果都是 0 1 2 3)
1、利用块级作用域
1 var elements = document.getElementsByTagName('li'); 2 var length = elements.length; 3 for(let i=0;i<length;i++) { 4 elements[i].onclick = function() { 5 console.log(i) 6 } 7 }
2、利用自执行函数,将i作为参数传入
1 var elements = document.getElementsByTagName('li'); 2 var length = elements.length; 3 for(var i=0;i<length;i++) { 4 (function(i) { 5 elements[i].onclick = function() {
console.log(i)
6 }
7 })(i)
8 }
3、利用promise
1 var elements = document.getElementsByTagName('li'); 2 var length = elements.length; 3 for(var i=0;i<length;i++) { 4 new Promise((resolve,reject)=> { 5 var j=i; 6 elements[j].onclick = function() { 7 console.log(j) 8 } 9 }) 10 }
4、利用async函数
1 var elements = document.getElementsByTagName('li'); 2 var length = elements.length; 3 async function foo() { 4 for(var i=0;i<length;i++) { 5 let result = await new 6 Promise((resolve,reject)=>{ 7 elements[i].onclick = function() { 8 resolve(i) 9 } 10 }); 11 console.log(result) 12 } 13 } 14 foo();