JS 函数的执行时机
前言
JS函数在执行的时候,函数所处的位置不同,执行的结果就会各不相同。
下面我们通过几个例子来看一下,函数的执行时机对结果会产生怎样的影响。
正文
例1
let a = 1 function fn(){ console.log(a) } 复制代码
唐僧: 徒儿,请问上例会打印出多少?
悟空:不知道,因为你压根没有调用函数fn。
例2
let a = 1 function fn(){ console.log(a) } fn() 复制代码
唐僧: 徒儿,请问上例会打印出多少?
八戒:很显然,是1。
例3
let a = 1 function fn(){ console.log(a) } a = 2 fn() 复制代码
唐僧: 徒儿,请问上例会打印出多少?
沙僧:因为fn在a=2后执行,故打印出2。
例4
let a = 1 function fn(){ console.log(a) } fn() a = 2 复制代码
唐僧: 徒儿,请问上例会打印出多少?
白龙马:对比一下例3我们可以知道,fn先调用的,故会打印出1。
前面几个例子都比较简单,相信大家都可以回答出来。那么我们现在升级一下难度,请看下面这个:
猛料
函数执行的时机不同,运行结果也不同
所谓时机不同,结果不同~
let i = 0 for(i = 0; i<6; i++){ setTimeout(()=>{ console.log(i) },0) } //结果输出6个6 复制代码
上面的代码中, console.log(i)
打印的结果是6个6,而不是0,1,2,3,4,5。 这里便用到了调用时机,下面有图有真相。
可以这样理解
- 因为JS是单线程的,单线程就意味着所有任务需要排队,前一个任务结束后,才会执行后一个任务。
- 同步任务:上一件事情没有完成,继续处理上一件事情,只有上一件事情完成了,才会做下一件事情 –> JS中大部分都是同步编程。
- 异步任务:规划要做一件事情,但是不是当前立马去执行这件事情,需要等一定的时间,这样的话,我们不会等着他执行,而是继续执行下面的操作。
所以在for
循环时
- 先赋值
i
的值为0 - 然后判断是否 i<6 ? 如果满足条件会继续进入下一个循环。
setTimeout()
代码到了这里会等一会执行,这里便会跳过继续执行i++
- 执行
i++
,此时i赋值为1. - 继续判断i<6,满足进入第二个循环.
- .
- .
- .
- 直到执行i++,i的值是6时,不满足i<6.
- 跳出循环
- 执行第一次循环的
setTimeout()
//打印出i
- 执行第二次循环的
setTimeout()
//打印出i
- 执行第三次循环的
setTimeout()
//打印出i
- 执行第四次循环的
setTimeout()
//打印出i
- 执行第五次循环的
setTimeout()
//打印出i
- 执行第六次循环的
setTimeout()
//打印出i
- 结束,打完收工现在可以看出,由于
setTimeout()
的执行时间为for
语句执行后,所以每次打印出的结果都为6
到了这里便需要介绍以下setTimeout()
setTimeout
函数用来指定某个函数或某段代码,在多少毫秒之后执行
代码到了这里时会把函数放在任务队列中,当JS引擎线程空闲时并达到指定时间时,才会把函数放到JS引擎线程中执行。
就算setTimeout()
的时间参数为0时,也必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。
for循环在主线程内,setTimeout是异步方法,在任务队列里面,只有主线程执行完后,任务队列才执行,当for执行结束时候,此时的i值已经是6,所以得到结果是个6。
那么如何输出0-5呢
for(let i = 0; i<6; i++){ setTimeout(()=>{ console.log(i) },0) }
这是JS后来为新人开发者改变的一个东西。让代码按着新人的思路来实现。 出了上面这种方法能够实现打印0-5
其他方法
1、可以通过自执行函数来实现
for (var i = 0; i <= 5; i++) { !(function (j) { setTimeout(function print() { console.log(j); }, 0); })(i); }
- 通过setTimeout第三个参数
for (var i = 0; i < 6; i++) { setTimeout((i) => console.log(i), 0, i); }