MST
星途 面试题库

面试题:复杂闭包场景下 JavaScript 作用域与变量提升的相互作用

假设有多层嵌套的函数形成闭包,且在不同层次存在同名变量声明(使用 `var`、`let` 和 `const` 混合声明),请分析在这种复杂场景下,JavaScript 的作用域链如何形成,变量提升是怎样进行的,以及可能会出现的问题,并通过编写具体代码示例进行详细解释。
23.6万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

作用域链形成

在JavaScript中,当多层嵌套函数形成闭包且存在同名变量声明时,作用域链是由内到外一层一层查找变量的。每个函数在定义时就创建了自己的作用域,作用域链包含了该函数的局部作用域以及所有外层函数的作用域,直到全局作用域。当在函数内部访问一个变量时,首先会在自身的局部作用域中查找,如果找不到,则沿着作用域链向上一层作用域查找,直到全局作用域。如果全局作用域也没有找到该变量,在非严格模式下会创建一个全局变量(如果是使用var声明的情况),严格模式下会抛出ReferenceError

变量提升

  1. var声明var声明的变量会提升到函数或全局作用域的顶部,但值不会提升,即变量声明被提升,初始化留在原地。所以在声明之前使用var声明的变量会得到undefined
  2. letconst声明letconst声明的变量存在暂时性死区(TDZ)。它们也会提升,但在声明语句之前访问这些变量会抛出ReferenceError,而不是像var那样得到undefined。这意味着在变量声明之前,它们在其作用域内是不可用的。

可能出现的问题

  1. 变量覆盖:由于不同作用域中同名变量的存在,可能会意外地覆盖外层作用域的变量,导致逻辑错误。
  2. TDZ相关错误:不小心在letconst声明之前访问变量会导致ReferenceError,特别是在复杂的嵌套函数和闭包场景中容易出错。

代码示例

function outer() {
    var x = 'outer var';
    let y = 'outer let';
    const z = 'outer const';

    function inner() {
        var x = 'inner var';
        let y = 'inner let';
        const z = 'inner const';

        console.log(x); // 'inner var',优先使用内层作用域的变量
        console.log(y); // 'inner let',优先使用内层作用域的变量
        console.log(z); // 'inner const',优先使用内层作用域的变量
    }

    inner();
    console.log(x); // 'outer var',内层函数没有改变外层的x
    console.log(y); // 'outer let',内层函数没有改变外层的y
    console.log(z); // 'outer const',内层函数没有改变外层的z
}

outer();


// 变量提升和TDZ示例
function test() {
    console.log(a); // undefined,var声明提升但值未提升
    console.log(b); // ReferenceError: b is not defined,let存在TDZ
    console.log(c); // ReferenceError: c is not defined,const存在TDZ

    var a = 'var value';
    let b = 'let value';
    const c = 'const value';
}

test();