“函数的调用时机不同,结果也会不同”我们先看几个例子理解一下这句话。
例1
let a = 1function fn(){console.log(a)}fn()a = 2
上面的代码会打印出1,因为调用是在a=2的赋值之前。
例2
let a = 1function fn() {setTimeout(() => {console.log(a)}, 0)}fn()a = 2
上面的代码会打印出2,因为setTimeout的意思是过一会再调用,他会在代码执行完再进行调用。
刚刚我们通过例1和例2中的setTimeout看到,代码的执行时机不一样所打印出的结果也不一样。那么看看下面的代码并思考一下,for循环为什么会打印出6 个 6。
let i = 0for(i = 0; i<6; i++){setTimeout(()=>{console.log(i)},0)}
setTimeout()实际上是设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。
因为事件循环机制的限制,以上代码的执行顺序是不可更改的,只要你使用了setTimeout,那就必须等到函数调用栈中可执行代码执行完毕之后,再去执行任务队列中的任务。也就是说setTimeout()会等for循环最后的条件判断运行完了之后再运行console.log(i),这时i的值为6,所以他会输出6个6。
用for let 配合可以解决这个问题
上面的代码没有成功用for let配合的原因是let的作用域没有到for里。
for (let i = 0; i < 6; i++) {setTimeout(() => {console.log(i)}, 0)}
上面的代码会打印出0、1、2、3、4、5,因为JS在for和let一起用的时候,每次循环多创建一个i。
那么除了使用 for let 配合,还有什么其他方法可以打印出 0、1、2、3、4、5呢?
1.闭包函数
闭包函数,每一次创建都会存在一个自己的空间来存储唯一的值,将i的每一次执行for循环的值,传给不同创建的闭包函数,这样每一个闭包函数里存储的i值,就都不会一样.所以就是达到我们的想要的结果.
for (var i = 1; i < 6; i++) {(function(i) {setTimeout(function() {console.log(i);}, 0);})(i);}
- 地址传递
用另外一个变量储存值,每执行一次函数 i , j 就加一次,所以执行到setTimeout的时候,就会调用 i 函数
function count() {var out = function(i) {setTimeout(function() {console.log(i);}, 0);};for (var j = 0; j < 6; j++) {out(j);}}count();
- 利用 setTimeout 的第三个参数,将i传进去
for (i = 0; i < 6; i++) {setTimeout((j) => {console.log(j)}, 0, i)}
4.使用立即执行函数
let ifor (i = 0; i < 6; i++) {! function(j) {setTimeout(() => {console.log(j)}, 0)}(i)}
总结
- 函数的调用时机不同,结果也会不同
- setTimeout()的执行时机
- for let 配合打印出 0、1、2、3、4、5
- 其他方法打印出 0、1、2、3、4、5
