Js中函数执行顺序
严格意义上来说,javascript没有多线程的概念,所有的程序都是单线程依次执行的。
1.什么是单线程?
通俗点说,就是代码在执行过程中,另一段代码想要执行就必须等当前代码执行完成后才可以进行。
for(var i=1;i<=3;i++){ setTimeout(function(){ console.log(i); //输出:4,4,4 },0) }
既然延时器时间设置为0,那么应该执行一遍循环就应该立即打印出一个i,但是最终的打印结果为:4,4,4。之所以会出现上面的结果,正是因为js代码是单线程应用。
在执行过程中,先遇到for循环,for循环先进入线程。当i=1时,循环走到setTimeOut后,此时的for循环还没有执行完成,setTimeOut就会被放入一个地方(线程池)等待执行。此时for循环继续执行,当i=2时,for循环仍没有执行完,这时的setTimeOut仍会被放在线程池中等待执行……依次类推,直到for循环转完3遍后,for循环执行完了,此时线程空闲了,在线程池中等待执行的setTimeOut依次执行打印i,而for循环执行完成后,i变成了4,所以打印出了三个4。
如果想要改变上面的状况可以运用以下代码:
//将var变为let for(let i=1; i<=3; i++){ setTimeOut(function(){ console.log(i); //输出的结果为1,2,3 },0); } //用自执行函数进行包裹 for(var i=1; i<=3; i++){ !function(i){ setTimeOut(function(){ console.log(i); //输出的结果为1,2,3 },0); }(i) }
1.for循环中的变量变成块儿作用域:
因为for循环头部的let不仅将i绑定到for循环快中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值重新被赋值。setTimeout里面的function()属于一个新的域,通过 var 定义的变量是无法传入到这个函数执行域中的,通过使用 let 来声明块变量,这时候变量就能作用于这个块,所以 function就能使用 i 这个变量了;这个匿名函数的参数作用域 和 for参数的作用域 不一样,是利用了这一点来完成的。这个匿名函数的作用域有点类似类的属性,是可以被内层方法使用的。
2.立即函数就是立即执行无需等待回调,代码加载就立即执行
使用立即执行函数,通过函数作用域可以保存变量的特性来将每次循环的变量 i 保存到函数中
3.如果给立即执行函数return一个function,setTimeout函数执行的就是这个function
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); return function() { console.log('回调') console.log(i) } })(i), i * 1000); }
先执行立即函数,再进行回调