你不知道的JavaScript系列——变量提升的概念(Hoisting)

138 阅读4分钟

大家好,我是 kada,我将从今天开始每日更新《你不知道的JavaScript系列》,我们将一起探索 JavaScript 的核心概念,揭开其内部运作的秘密,并帮助你成为一名更自信、更有能力的开发者。 作为一名初学者,可能我发表文章的内容还不够完善,甚至可能存在一些误解。希望通过互相交流和学习,共同进步,收获许多。

今天要学习的内容是JavaScript——变量提升的概念(Hoisting)

一. var声明的变量

先给大家看一段代码,在这个例子中,大家觉得会输出什么?

console.log(x); 
var x = 10;

有人可能会猜输出 10,因为他们可能认为 JavaScript 的行为与其他编程语言类似。而有些学了一些其他语言的同学可能会说会 报错,因为他们习惯于那些语言中严格的作用域规则。

但实际上,当你运行这段代码时,控制台会输出 undefined。为什么呢?

这是因为虽然 var x 的声明被提升到了作用域的顶部,但赋值操作 x = 10 仍然保留在原来的位置。所以在 console.log(x) 执行时,x 已经被声明了,但由于还没有执行到赋值部分,所以它的值是 undefined

通过这个例子我们就引出了变量提升的概念,在js里,变量提升(hoisting)是一种机制,指的是在代码执行之前,变量和函数的声明会被移动到当前作用域的顶部。这意味着你可以在变量或函数声明之前使用它们

总结一:

var 声明的变量——当使用 var 声明变量时,变量会被提升到当前作用域的顶部,但不会被赋值。在变量声明之前访问该变量,它的值是 undefined

二. 函数声明

再来看一段函数声明的js代码:

foo(); // 输出: "Hello, World!"
function foo() {
    console.log("Hello, World!");
}

当你第一次看到这段代码时,可能会感到困惑:为什么在声明 foo 函数之前调用它,程序还能正常输出 "Hello, World!" 呢?

实际上,这是因为 JavaScript 的变量提升机制对函数声明有特殊处理。不仅函数的声明被提升到了作用域的顶部,连函数体也被一起提升了。因此,即使你在函数声明之前调用它,JavaScript 引擎也能找到并执行这个函数。

总结二:

函数声明不仅会被提升到作用域的顶部,其整个函数体也会被移动到作用域的顶部。这意味着你可以在函数声明之前调用该函数,代码仍然能够正常运行。

这对我来说是一个非常有趣的发现。刚开始学习 JavaScript 时,我曾以为这样的代码会抛出一个错误,因为我在调用函数时还没有定义它。但事实证明,JavaScript 的函数声明提升机制比我想的要强大得多。

三. let 和 const 声明的变量

console.log(y); // 抛出 ReferenceError
let y = 20;

当你第一次看到这段代码时,可能会以为它会像 var 那样输出 undefined。但实际上,如果你尝试在声明之前访问 y,你会遇到一个 ReferenceError 错误。

这是因为 letconst 声明的变量虽然被提升了,但在进入其作用域后直到声明语句之前都无法访问。这段时间被称为暂时性死区(TDZ)。JavaScript 引擎设计这个机制是为了防止一些潜在的编程错误,确保我们在使用变量之前必须先声明它们。

总结三:

实际上,letconst 声明的变量也会被提升,但它们存在一个被称为暂时性死区(Temporal Dead Zone,TDZ)的概念。在变量声明之前访问 letconst 声明的变量会导致 ReferenceError 错误。

四. 总结与预告

变量提升是 JavaScript 的一种机制,在代码执行前,变量和函数声明会被移到当前作用域顶部。var 声明的变量会提升但初始值为 undefined;函数声明会完全提升,可在声明前调用;而 let 和 const 声明的变量虽也提升,但存在暂时性死区,在声明前访问会引发 ReferenceError

在接下来的文章中,我们将深入研究暂时性死区,探讨它的具体工作原理以及如何在实际开发中避免相关的错误。感谢大家的阅读!