JS 函数的执行时机

106 阅读2分钟

函数的调用时机

每个函数都有它们的调用时机,调用的时机不同,得到的结果自然也会不同。

下面是几个常规的例子:

let a = 1
function fn(){
    console.log(a)
}
fn()

// 1
// 函数的一个常规调用,打印 “1”
let a = 1
function fn(){
    console.log(a)
}
a = 2
fn()

// 2
// 变量赋值为了2,所以执行函数的时候,打印 “2”
let a = 1
function fn(){
    console.log(a)
}
fn()
a = 2

// 1
// a 是在函数调用之后才赋值为2,所以函数调用时打印的是 “1”
let a = 1
function fn(){
    setTimeout(()=>{
    console.log(a)
},0)
}
fn()
a = 2

// 2

上面代码有一个 setTimeout 函数,它会设置一个定时器,这里时间设置为 0 意味着它会在当前事件队列任务执行完后马上执行。这里的事件就是 a = 2给 a 赋值,因此最后打印输出的值为2。

let i = 0
for(i = 0; i<6; i++){
    setTimeout(()=>{
        console.log(i)
    },0)
}

仍是一个类似的例子,这里的结果是会打印出 6 个 6。原因同上,setTimeout会在当前需要做的事——即for循环执行完毕之后再去执行自身内部的语句,for循环执行完毕之后,i 的值为 6,因此setTimeout会打印6次 i 的值,也就是 6 次 6。

下面这个例子,则会打印出 0、1、2、3、4、5:

for(let i = 0; i<6; i++){
    setTimeout(()=>{
        console.log(i)
    },0)
}

这又是为何,因为在 for 循环里,变量 i 是由 let 声明的,且不是在全局环境声明的,这里 i 只在循环的本轮中有效,每一次循环的 i 其实都是一个新的变量。而且for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

这使得上面的代码,就近似于:

(let i = 0){
    setTimeout(()=>{console.log(i)},0)
}

(let i = 1){
    setTimeout(()=>{console.log(i)},0)
}

....

因此会输出 0、1、2、3、4、5。

除了上面的例子外,还可以使用 const 关键字让函数打印出 0、1、2、3、4、5:

let i
for(i = 0; i<6; i++){
    const x = i
    setTimeout(()=>{console.log(x)})
}

使用 const 关键字声明的变量是不得改变的,再把 x 传递给 setTimeout 内的 function 参数,即可打印出 0、1、2、3、4、5。

(End)


参考链接:

[1] MDN let语句

[2] 网道 ES6教程