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 的执行过程,帮助我在面试中表现更好。

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

相关推荐
地方地方4 小时前
JavaScript 类型检测的终极方案:一个优雅的 getType 函数
前端·javascript
加洛斯4 小时前
AJAX 知识篇(2):Axios的核心配置
前端·javascript·ajax
知其然亦知其所以然4 小时前
面试官一开口就问:“你了解MySQL水平分区吗?”我当场差点懵了……
后端·mysql·面试
Mintopia4 小时前
开源数据集在 WebAI 模型训练中的技术价值与风险:当我们把互联网塞进显存
前端·javascript·aigc
写不来代码的草莓熊4 小时前
vue前端面试题——记录一次面试当中遇到的题(3)
前端·javascript·vue.js
老马啸西风4 小时前
力扣 LC27. 移除元素 remove-element
算法·面试·github
洛克大航海4 小时前
Ajax基本使用
java·javascript·ajax·okhttp
用户916357440954 小时前
LeetCode热题100——11.盛最多水的容器
javascript·算法
凡大来啦4 小时前
v-for渲染的元素上使用ref
前端·javascript·vue.js