MST

星途 面试题库

面试题:复杂场景下 JavaScript 事件循环与变量作用域深度剖析

假设有如下 JavaScript 代码: ```javascript function outer() { let x = 10; setTimeout(() => { console.log(x); new Promise((resolve) => { console.log('Promise start'); resolve(); }).then(() => { console.log('Promise then'); }); x = 20; console.log(x); }, 0); x = 15; return function inner() { console.log(x); }; } let innerFunc = outer(); innerFunc(); console.log('Global code'); ``` 请详细分析该代码在 JavaScript 事件循环机制下的执行过程,以及变量作用域如何影响变量的值,解释每一步输出结果的原因。
40.9万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试
  1. 变量作用域分析

    • outer 函数内部,定义了变量 x 并初始化为 10let 声明的变量具有块级作用域,这里 x 的作用域是 outer 函数体。
    • setTimeout 回调函数和 inner 函数都在 outer 函数内部定义,它们形成了闭包,可以访问 outer 函数作用域中的 x 变量。
  2. 事件循环执行过程分析

    • 步骤1:执行全局代码
      • 首先,调用 outer 函数。在 outer 函数中,声明并初始化 x10,然后执行 setTimeoutsetTimeout 的回调函数被放入宏任务队列,其延迟为 0,但由于事件循环机制,它不会立即执行。
      • 接着,将 x 的值更新为 15,并返回 inner 函数,将其赋值给 innerFunc
    • 步骤2:执行 innerFunc
      • 调用 innerFunc。由于 innerFunc 形成了闭包,它可以访问 outer 函数作用域中的 x,此时 x 的值为 15,所以输出 15
    • 步骤3:执行全局代码剩余部分
      • 输出 Global code。此时,调用栈为空。
    • 步骤4:事件循环处理宏任务
      • 事件循环开始处理宏任务队列,取出 setTimeout 的回调函数放入调用栈执行。
      • setTimeout 回调函数中,首先访问 x,由于闭包,这里访问的 xouter 函数作用域中的 x,此时 x 的值为 15,所以输出 15
      • 接着创建一个 Promise,输出 Promise startPromise 立即执行并将其 then 回调放入微任务队列。
      • 然后将 x 更新为 20 并输出 20
    • 步骤5:事件循环处理微任务
      • 调用栈再次为空,事件循环开始处理微任务队列,取出 Promisethen 回调放入调用栈执行,输出 Promise then
  3. 总结输出结果

    • 首先输出 15,这是因为 innerFunc 访问了 outer 函数作用域中更新后的值 15
    • 接着输出 Global code,这是全局代码的正常输出。
    • 然后在 setTimeout 回调中输出 15,因为 setTimeout 回调访问的 xouter 函数作用域中的值,此时还未更新。
    • 输出 Promise start,这是 Promise 创建时立即执行的输出。
    • 输出 20,这是 setTimeout 回调中更新 x 后的值。
    • 最后输出 Promise then,这是 Promisethen 回调执行的输出。