MST

星途 面试题库

面试题:JavaScript作用域链与提升机制复杂场景剖析

以下面这段复杂的JavaScript代码为例: ```javascript function A() { var a = 'A1'; function B() { console.log(a); var a = 'B1'; console.log(a); function C() { console.log(a); a = 'C1'; console.log(a); } C(); console.log(a); } B(); console.log(a); } A(); ``` 请详细分析整个执行过程中变量的作用域、提升机制以及每次`console.log`输出的值,并且说明如果将`var`全部替换为`let`,代码的执行结果和作用域变化会有哪些不同。
19.5万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试
  1. 使用var时的分析
    • 变量作用域和提升机制
      • 在JavaScript中,var声明的变量存在函数作用域,并且会发生变量提升。这意味着变量的声明会被提升到函数的顶部,但初始化(赋值)不会提升。
      • 在函数A中,var a = 'A1'; 变量a会提升到函数A的顶部,初始值为undefined,然后再赋值为A1
      • 在函数B中,var a = 'B1'; 变量a同样提升到函数B的顶部,初始值为undefined,然后再赋值为B1。这里的a和函数A中的a是不同的变量,因为B函数有自己的函数作用域。
      • 在函数C中,a = 'C1'; 这里操作的a是函数B中的a,因为C函数作用域内没有声明自己的a,会沿着作用域链向上查找,找到函数B中的a
    • 每次console.log输出的值
      • 当执行A()时,进入函数Aa被初始化为A1。然后调用B()
        • 进入函数Ba提升为undefined,第一个console.log(a); 输出undefined,因为此时a虽然声明提升,但还未赋值。
        • 接着a被赋值为B1,第二个console.log(a); 输出B1
        • 调用C(),在Ca是函数B中的a,此时aB1,所以第三个console.log(a); 输出B1
        • 然后a被赋值为C1,第四个console.log(a); 输出C1
        • C()执行完毕,回到B(),第五个console.log(a); 输出C1,因为aC中已经被修改为C1
        • B()执行完毕,回到A(),第六个console.log(a); 输出A1,因为这里操作的是函数A中的a,函数B中的操作不会影响到它。
  2. var全部替换为let时的分析
    • 变量作用域和提升机制
      • let声明的变量存在块级作用域,并且不存在变量提升。它会形成暂时性死区(TDZ),在变量声明之前访问该变量会抛出ReferenceError
      • 在函数A中,let a = 'A1'; 变量a有函数A的块级作用域。
      • 在函数B中,let a = 'B1'; 变量a有函数B的块级作用域,和函数A中的a是不同的变量。
      • 在函数C中,由于C函数作用域内没有声明自己的a,会沿着作用域链向上查找,找到函数B中的a
    • 执行结果变化
      • 当执行A()时,进入函数Aa被初始化为A1。然后调用B()
        • 进入函数B,由于let没有变量提升且存在暂时性死区,第一个console.log(a); 会抛出ReferenceError,因为在声明let a = 'B1';之前访问了a。后续代码不会再执行。

所以,使用varlet在作用域和执行结果上有明显的不同。使用var可能因为变量提升导致一些不易察觉的问题,而let通过块级作用域和暂时性死区提供了更严格的变量作用域规则。