JavaScript 面试笔记:作用域、变量提升、暂时性死区与 const 的可变性

JavaScript 面试笔记:作用域、变量提升、暂时性死区与 const 的可变性

在这篇笔记中,我将总结一些常见的 JavaScript 面试问题,特别是关于 作用域变量提升暂时性死区(TDZ)const 的可变性 这四个关键概念的理解。这些概念不仅是面试中的常考内容,也是日常编码中容易迷惑的地方。通过这篇笔记,希望我能理清这些思路,为今后的学习和面试打下更扎实的基础。

一、作用域的差异:varlet/const 的区别

在学习和面试中,作用域的差异是最常见的考点之一。通过不断的思考和总结,我逐渐理解了 varletconst 在作用域上的差异。

  • var 是函数级作用域 :即使它在一个块级结构(如 iffor)中声明,作用域仍然是整个函数,导致 var 声明的变量会泄漏到函数外面。因此,即使在 if 块内声明了 var 变量,它仍然在整个函数内有效。
  • letconst 是块级作用域:它们的作用范围只在声明它们的代码块内有效,也就是说,它们不会泄漏到外部。

通过这个思考,我得出以下结论:如果在一个 if 语句和一个函数内部声明变量,var 声明的变量会在整个函数作用域内可访问,而 letconst 只在 {} 这个块内有效,外部无法访问。这让我意识到 letconst 在维护变量作用域时的优势,尤其在处理复杂的代码块时,它们能有效减少变量泄漏的问题。

js 复制代码
// 使用 var
function testVar() {
    if (true) {
        var x = 5;  // var 在函数级作用域内声明
    }
    console.log(x);  // 输出 5,x 不仅仅在 if 块内有效,而是在整个函数内有效
}
testVar();
js 复制代码
// 使用 let
function testLet() {
    if (true) {
        let y = 10;  // let 在块级作用域内声明
    }
    console.log(y);  // ReferenceError: y is not defined
}
testLet();

通过这两段代码,我得到了对 varlet 作用域差异的直观理解。var 会导致变量泄漏,而 letconst 提供了更安全的块级作用域,避免了这种问题。

🌍 全局对象属性补充

在全局作用域下,var 声明的变量不仅会在全局范围内生效,还会被挂载到全局对象 上。

在浏览器中,这个全局对象是 window;在 Node.js 中,则是 global

js 复制代码
var a = 1;
let b = 2;
const c = 3;

console.log(window.a); // 1
console.log(window.b); // undefined
console.log(window.c); // undefined

通过这个实验我发现:

  • window.a 可以取到值 1;
  • window.bwindow.c 都是 undefined

这说明:

var 声明的变量会成为全局对象的属性,

letconst 则不会。

也就是说,var 不仅是函数级作用域,还会"污染"全局环境;

letconst 则保持更干净的作用域边界,更符合现代 JavaScript 的设计理念。

二、变量提升:varletconst 的行为差异

接下来,我思考了 变量提升 这个问题,特别是在我们声明变量之后,如果在声明之前访问它,会发生什么?

  • var 变量提升var 声明的变量会被提升到函数顶部(或全局作用域的顶部),但是它的赋值 不会被提升。因此,在声明之前访问 var 变量时,它会返回 undefined,而不是抛出错误。
js 复制代码
function testVar() {
    console.log(a);  // 输出 undefined,变量被提升但未初始化
    var a = 10;
    console.log(a);  // 输出 10
}
testVar();

我发现,如果我在函数后面声明 var 变量并尝试访问它,JavaScript 会将其声明提升到函数顶部 ,但不会提升赋值。这导致第一次访问该变量时,返回的是 undefined,而不是 ReferenceError

  • letconst 变量提升(暂时性死区)letconst 变量会被提升,但它们不会在声明之前初始化。也就是说,在声明之前访问它们,会进入 暂时性死区(TDZ) ,并抛出 ReferenceError 错误。
js 复制代码
function testLet() {
    console.log(b);  // ReferenceError: Cannot access 'b' before initialization
    let b = 20;
    console.log(b);  // 输出 20
}
testLet();

通过这个思考,我进一步理解了 letconst暂时性死区(TDZ) ,它是 JavaScript 引擎的一种保护机制,防止在变量声明之前访问变量,从而避免了潜在的错误。

三、暂时性死区(TDZ):是否在内存中"存在"?

关于 暂时性死区(TDZ) 是否在内存中"真正存在",我曾经有些疑惑。经过多次思考,我终于明白了,TDZ 本身并不是内存中的一个物理区域 ,而是 JavaScript 引擎的内部机制

  • 当我声明一个 letconst 变量时,JavaScript 引擎会为该变量分配内存位置,但是变量的值不会立即初始化。在变量声明之前,该位置的内存会标记为"未初始化 ",因此访问该变量会触发 TDZ 错误(ReferenceError)。
  • TDZ 是执行时的行为,而不是内存中实际存在的区域。它是 JavaScript 引擎控制变量访问的一种方式,确保我们在变量初始化之前不会访问它。
js 复制代码
function testLet() {
    console.log(b);  // 报错 ReferenceError: Cannot access 'b' before initialization
    let b = 20;
    console.log(b);  // 输出 20
}
testLet();

在这段代码中,变量 b 会被提升,但由于它处于暂时性死区,在访问之前,JavaScript 引擎会抛出 ReferenceError 错误。这让我更加清楚地认识到,TDZ 是 JavaScript 引擎的一种作用域管理策略,而非内存中的一个物理区域。

四、const 的可变性:引用不变,内容可变

最后,我想分享我对 const 变量可变性的理解。这个问题有时会让人感到困惑,因为 const 声明的变量不能重新赋值,但它并不代表值本身不能修改。

  • const 保证的是变量指向的引用不变 ,但对于对象和数组,引用的内容是可以修改的

例如,使用 const 声明数组时,数组的内容是可以修改的,但不能重新赋值给一个新的数组。

js 复制代码
const arr = [1, 2, 3];
arr.push(4);  // 这行代码有效,修改数组内容
console.log(arr);  // 输出 [1, 2, 3, 4]

arr = [5, 6];  // TypeError: Assignment to constant variable.

通过这段代码,我意识到 const 声明的数组内容是可以改变的,因为 const 保护的是引用本身 ,而不是数组内部的内容。重新赋值给整个数组会导致错误,因为 const 防止了对引用的修改。

五、总结

通过对 varletconstTDZ 的深入理解,我不仅能更好地写出清晰、易于维护的代码,还能在面试中展示出对 JavaScript 执行机制的深刻理解。每个细节,哪怕是暂时性死区的存在与否,都让我能更深入地理解 JavaScript 的执行过程,帮助我在面试中表现更好。

这篇笔记记录了我对这些概念的思考过程和理解,也让我在未来遇到相关问题时能更自信地应对。

相关推荐
kyriewen2 小时前
我用 50 行代码重写了 React Router 核心,终于搞懂了前端路由原理
前端·javascript·react.js
Asize4 小时前
HTML5 Canvas 基础:从按帧动画到 ECharts 数据可视化
前端·javascript·canvas
默_笙4 小时前
🎄 后端给我一堆扁平数据,我 10 行代码把它变成了树
前端·javascript
前端Hardy4 小时前
又一个 AI 神器火了!
前端·javascript·后端
PBitW5 小时前
GPT训练我的第二天,我表示不过如此!!!😕😕😕
前端·javascript·面试
kyriewen6 小时前
白宫直接给 OpenAI 下了限制令,GPT-5.6 不能随便放出来了
前端·javascript·面试
未秃头的程序猿10 小时前
Java 26正式发布!这3个新特性,让代码量直接减半
java·后端·面试
默_笙11 小时前
🍞 我用 CSS 画了一个会转的 3D 立方体,同事以为我学了 Three.js(这节课真的很神奇,我很喜欢)
javascript
sarasuki11 小时前
JavaScript的对象、new的机制与原型包装类
javascript·后端
weedsfly11 小时前
JavaScript 事件流:彻底搞懂捕获、冒泡与事件委托
前端·javascript·react.js