JavaScript提升内部
引言
在这篇文章中,我们将学习另一个称为Hoisting
的基本JavaScript概念。
由于本系列的重点是了解每个概念的基础知识和内在原理,因此我们将在这里尝试探索起重的内在原理,而不仅仅是刮擦表面。
到目前为止我们知道什么
以下是本系列最后几篇文章的回顾:
- 在JavaScript中,源代码通常要经过几个阶段才能最终执行。这些阶段是标记化,解析和代码生成。
- 每当运行JavaScript代码时,它都会创建一个称为执行上下文的东西。它有助于确定当前正在运行的代码,并有助于为其途中发现的每个函数和变量声明分配内存。
在执行上下文中为函数和变量声明分配内存的机制称为提升(
Hoisting
)。
揭开一些神话的神秘面纱
在这里实际发生的情况下,“Hoisting
”一词令人困惑和误解。这就是为什么许多有关Hoisting
的解释都集中在Hoisting
变量和函数这一事实的原因之一。 用通俗的英语来说,“Hoisting
”意思是用绳索和滑轮抬起(某种东西)。在某个时候,人们可能会开始相信,通过JavaScript引擎将事物(变量和函数)的位置上移实际上已经将其Hoisting
。
嘿,放松,什么都没有发生!老实说,没有任何代码会被物理地吊起。这是关于如何在执行上下文的创建阶段为函数,变量声明分配内存的。
我们将在后面的示例中看到这一点。
变量提升(Variable Hoisting)
考虑以下简单代码:
1 | console.log('name is ', name); |
上面的代码的预期输出是什么?好吧,这很容易:
1 | name is undefined |
问题是,为什么?我们早在变量name
称被声明之前就已经对其进行了访问。像许多其他编程语言一样,我们应该有一个错误。但是,相反,我们得到undefined
。
在JavaScript中,代码执行上下文分为两个阶段:
- 创建阶段
- 执行阶段
在创建阶段,将为变量分配内存,并使用称为undefined
的特殊值进行初始化。
undefined
的含义是,已声明变量,但未分配任何值。
在上面的代码示例中,创建阶段通过为其分配内存来声明变量name
并将其标记为undefined
。这种现象在JavaScript中称为“变量提升
”。
在执行阶段的后期,将值tom
分配给变量name
,并执行控制台日志语句。当创建阶段发生在执行阶段之前时,我们发现变量已经声明,即在内存中创建(就像在提升中一样)。
函数提升(Function Hoisting)
函数提升遵循与可变提升类似的路径。在函数提升中,JavaScript 执行环境的创建阶段将函数声明放入内存中。让我们通过以下示例了解它:
1 | // Invoke a function, chase() |
执行上下文的创建阶段为函数chase()
创建一个内存,并且整个函数声明都已放入该内存中。在执行阶段,可以执行存储器的全部功能。
众所周知,函数创建自己的执行上下文(函数执行环境),其机制在函数执行上下文中保持不变。首先,为catch()
创建一个内存,并将声明放入其中。以后执行它。
提升规则(Hoisting Rules)
一般的经验法则是,始终在代码中使用函数,变量等之前定义它们。这可以帮助避免意外情况和调试噩梦。
JavaScript语言中已经存在一些准则和检查,以防止在未意识到的情况下使用Hoisting
的安全隐患。
JavaScript仅提升声明,而不初始化。这样,下面的代码将被打破:
1 | test(); |
由于将使用undefined
的值提升和初始化测试声明,因此将引发以下错误。从来没有假设它是一个函数。实际上,它是作为变量提升而不是函数提升来的。
也可以提升let
和const
声明,但是它们不会像var
一样初始化为undefined
。请参阅此处的示例:
1 | console.log(foo); |
这将引发以下错误,但是使用var
可以正常运行:
总结
如上所述,在代码中使用函数,变量等之前,请务必先定义它们。不要太依赖提升。同时,重要的是要理解为什么某些事物以特定方式表现的基本概念。