- 为什么打印出的是2而不是1:
- 在JavaScript中,
setTimeout
是异步操作。当 asyncFunction
执行时,let a = 1;
声明并初始化 a
为1,接着 a = 2;
这行代码将 a
的值更新为2 。
setTimeout
的回调函数会在1000毫秒(1秒)后放入任务队列中等待执行。此时,asyncFunction
函数的同步部分已经执行完毕,a
的值已经变为2 。当回调函数执行时,它访问的是当前作用域中的 a
,此时 a
的值为2,所以打印出的是2 。
- 异步场景下作用域的工作原理:
- 在这个例子中,
setTimeout
的回调函数处于 asyncFunction
的作用域内。JavaScript使用词法作用域(静态作用域),这意味着函数的作用域在定义时就确定了,而不是在调用时确定。
- 回调函数虽然在1000毫秒后执行,但它依然可以访问
asyncFunction
作用域中的变量。由于 a
是用 let
声明的,它具有块级作用域,且在 asyncFunction
函数内有效。当回调函数执行时,它能访问到 asyncFunction
作用域中最新的 a
值,即2 。
- 与同步代码中作用域机制的不同之处:
- 同步代码:
- 在同步代码中,作用域按照代码的执行顺序依次创建和销毁。例如:
function syncFunction() {
let b = 1;
console.log(b);
{
let b = 2;
console.log(b);
}
console.log(b);
}
syncFunction();
- 这里在函数 `syncFunction` 中,首先声明 `b` 并赋值为1 ,打印1 。然后在块级作用域内重新声明 `b` 并赋值为2 ,打印2 。当块级作用域结束,内部声明的 `b` 销毁,最后再次打印外部的 `b` ,输出1 。作用域的变化与代码的同步执行紧密相关。
- 异步代码:
- 异步代码(如
setTimeout
回调)打破了这种顺序执行的模式。异步操作的回调函数在将来某个时间执行,此时同步代码可能已经执行完毕,作用域中的变量状态可能已经发生了变化。但回调函数依然基于其定义时的词法作用域来访问变量,而不是基于其执行时的“瞬间”作用域状态。例如 setTimeout
回调函数虽然执行晚,但它依据 asyncFunction
定义时的作用域来访问 a
变量,所以能获取到更新后的 a
值2 。