深入理解JavaScript中的变量提升

17 篇文章 0 订阅

JavaScript中奇怪的一点是你可以在变量和函数声明之前使用它们。就好像是变量声明和函数声明被提升了代码的顶部一样。

JavaScript中奇怪的一点是你可以在变量和函数声明之前使用它们。就好像是变量声明和函数声明被提升了代码的顶部一样。

sayHi() // Hi there!
function sayHi() {    console.log('Hi there!')}
name = 'John Doe'console.log(name)   // John Doevar name

然而JavaScript并不会移动你的代码,所以JavaScript中“变量提升”并不是真正意义上的“提升”。

JavaScript是单线程语言,所以执行肯定是按顺序执行。但是并不是逐行的分析和执行,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段。

在编译阶段阶段,代码真正执行前的几毫秒,会检测到所有的变量和函数声明,所有这些函数和变量声明都被添加到名为Lexical Environment的JavaScript数据结构内的内存中。所以这些变量和函数能在它们真正被声明之前使用。

函数提升

sayHi() // Hi there!
function sayHi() {    console.log('Hi there!')}

因为函数声明在编译阶段会被添加到词法环境(Lexical Environment)中,当JavaScript引擎遇到sayHi()函数时,它会从词法环境中找到这个函数并执行它。​​​​​​​

lexicalEnvironment = {  sayHi: < func >}

var变量提升​​​​​​​

console.log(name)   // 'undefined'var name = 'John Doe'console.log(name)   // John Doe

上面的代码实际上分为两个部分:

  • var name表示声明变量name

  • = 'John Doe'表示的是为变量name赋值为'John Doe'。

var name    // 声明变量name = 'John Doe' // 赋值操作

只有声明操作var name会被提升,而赋值这个操作并不会被提升,但是为什么变量name的值会是undefined呢?

原因是当JavaScript在编译阶段会找到var关键字声明的变量会添加到词法环境中,并初始化一个值undefined,在之后执行代码到赋值语句时,会把值赋值到这个变量。​​​​​​​

// 编译阶段lexicalEnvironment = {  name: undefined}
// 执行阶段lexicalEnvironment = {  name: 'John Doe'}

所以函数表达式也不会被“提升”。helloWorld是一个默认值是undefined的变量,而不是一个function。​​​​​​​

helloWorld();  // TypeError: helloWorld is not a function
var helloWorld = function(){  console.log('Hello World!');}

let&const提升​​​​​​​

console.log(a)  // ReferenceError: a is not definedlet a = 3

为什么会报一个ReferenceError错误,难道let和const声明的变量没有被“提升”吗?

事实上所有的声明(function, var, let, const, class)都会被“提升”。但是只有使用var关键字声明的变量才会被初始化undefined值,而let和const声明的变量则不会被初始化值。

只有在执行阶段JavaScript引擎在遇到他们的词法绑定(赋值)时,他们才会被初始化。这意味着在JavaScript引擎在声明变量之前,无法访问该变量。这就是我们所说的Temporal Dead Zone,即变量创建和初始化之间的时间跨度,它们无法访问。

如果JavaScript引擎在let和const变量被声明的地方还找不到值的话,就会被赋值为undefined或者返回一个错误(const的情况下)。

举例:​​​​​​​

let aconsole.log(a)  // undefineda = 5

在编译阶段,JavaScript引擎遇到变量a并将它存到词法环境中,但因为使用let关键字声明的,JavaScript引擎并不会为它初始化值,所以在编译阶段,此刻的词法环境像这样:​​​​​​​

lexicalEnvironment = {  a: <uninitialized>}

如果我们要在变量声明之前使用变量,JavaScript引擎会从词法环境中获取变量的值,但是变量此时还是uninitialized状态,所以会返回一个错误ReferenceError。

在执行阶段,当JavaScript引擎执行到变量被声明的时候,如果声明了变量并赋值,会更新词法环境中的值,如果只是声明了变量没有被赋值,那么JavaScript引擎会给变量赋值为undefined。

tips:我们可以在let和const声明之前使用他们,只要代码不是在变量声明之前执行:​​​​​​​

function foo() {    console.log(name)}
let name = 'John Doe'
foo()   // 'John Doe'

Class提升

同let和const一样,class在JavaScript中也是会被“提升”的,在被真正赋值之前都不会被初始化值, 同样受Temporal Dead Zone的影响。​​​​​​​

let peter = new Person('Peter', 25) // ReferenceError: Person is not defined
class Person {  constructor(name, age) {    this.name = name;    this.age = age;  }}
let John = new Person('John', 25); console.log(peter) // Person { name: 'John', age: 25 }

     文末福利

     需要更多教程,微信扫码即可
      https://i-blog.csdnimg.cn/blog_migrate/4517cd309f54d754ce7f78e279438728.jpeg  

         别忘了扫码领取资料哦
      【高清学习线路图】和【全套学习视频及相关资料】

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值