JavaScript-彻底讲清楚什么是变量提升

前置知识:需理解JS的执行上下文概念。

理解执行上下文前,先理解什么是执行。

假如整个程序只有一行代码,能否执行?

如果这一行代码是一个函数声明,这个程序不会执行,而且也没有生成执行上下文对象。连全局上下文对象也没有。

顺便引申出,如果在一个A函数里,再申明一个B函数,只有这一个声明语句的话,这个B函数也不会保存在A函数的执行上下文里。

在这里,可以得出一个基本结论,在全局作用域和局部作用域里,函数声明是不可执行的语句

当有了可执行的代码,程序才会真正执行,并且在执行前产生执行上下文。

它是JS引擎虚拟出来的放来存放数据的对象。这个对象无法用代码访问,但是在代码执行过程中却真实存在。执行上下文对象统称为变量对象。

以谷歌浏览器为例:

全局作用域下的变量对象叫:Global Object ,简称GO。

局部作用域下的变量对象叫:Local Object,简称LO。《高级程序设计》一书中称为活动对象叫Activation Object,简称AO。

块作用域下的变量对象叫:Block Object,简称BO。

还有一个ES6后才有变量对象:Script Object,简称SO。SO对象仅仅用来保存在全局作用域下,let和const声明的变量。

先讨论全局作用域和局部作用域下的变量提升,块级作用域的变量提升会有不同的行为。

全局作用域下:

在可执行代码执行前,JS引擎会把变量声明和函数声明保存在GO对象中。GO对象就是全局的执行上下文。

变量声明的两种情况:

第一种使用var 声明变量:使用var声明的变量会被保存到GO对象里,同时widow对象里也存有一模一样的值。其初始值为undefined,在赋值操作前访问打印出来就是undefined。

第二种使用let或者const声明变量:他们声明的变量会保存在SO对象里,初始值也是undefined,但是由于暂时性死区的存在无法在初始化值前访问他们声明的变量。

所以基于以上的区别,从能否在初始化前访问变量的角度来说,只有var声明的变量才有变量提升效果。

但实际上,let和const声明的变量,在程序执行前,同样会保存到上下文对象里。

在局部作用域(也就是函数作用域)下:

函数体里的可执行代码执行前,会生成LO对象(AO对象)用来保存形参,实参传递进来的值,保存内部的变量声明值为undefined和函数声明值为函数本身,还有this的值。这个LO对象就是函数的执行上下文。

局部作用域下,浏览器怪异行为较少,没有太多的补丁感。所以代码实践中,都是先定义函数,在函数里玩各种骚操作。(比如闭包)

在块作用域下:

块作用域下的执行上下文对象叫BO,和其它两个作用域一样,会保存变量声明值为undefined,保存函数声明值为函数本身。里面没有this。

块作用域存在时,块作用域下的函数声明会先保存在GO对象里,且值为undefined。这跟其它两个作用域有重要区别。

执行到块作用域时,块作用域下的函数声明会再次执行(注意:之前我们得出结论是在全局和局部,函数声明不可执行。),且会把BO对象里函数变量名下的值同步给GO对象里的同名变量。

这种浏览器的怪异行为,是为了在ES6时代兼容ES5语法而设计的。

补充:

eval()调用内部存在第三种上下文。

总结:

  1. 只有函数的执行上下文才有this
  2. 多在函数作用域下写代码
  3. 块作用域下,函数声明是可执行语句
  4. 执行上下文是一个对象,用来保存代码执行前的一些初始化值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值