“函数的调用时机不同,结果也会不同”我们先看几个例子理解一下这句话。
例1
let a = 1
function fn(){
console.log(a)
}
fn()
a = 2
上面的代码会打印出1,因为调用是在a=2的赋值之前。
例2
let a = 1
function fn() {
setTimeout(() => {
console.log(a)
}, 0)
}
fn()
a = 2
上面的代码会打印出2,因为setTimeout的意思是过一会再调用,他会在代码执行完再进行调用。
刚刚我们通过例1和例2中的setTimeout
看到,代码的执行时机不一样所打印出的结果也不一样。那么看看下面的代码并思考一下,for循环为什么会打印出6 个 6。
let i = 0
for(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 i
for (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