浅析JavaScript中的变量提升


Posted in Javascript onJune 01, 2022

前言:

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

sayHi() // Hi there!

function sayHi() {
    console.log('Hi there!')
}
name = 'John Doe'
console.log(name)   // John Doe
var 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 defined
let a = 3

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

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

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

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

举例:

let a
console.log(a)  // undefined
a = 5

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

lexicalEnvironment = {
  a: <uninitialized>
}

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

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

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

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

Class提升

letconst一样,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(John) // Person { name: 'John', age: 25 }

到此这篇关于浅析JavaScript中的变量提升的文章就介绍到这了,更多相关JS变量提升内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Javascript 相关文章推荐
获取URL地址中的文件名和参数的javascript代码
Sep 02 Javascript
jquery获取被勾选的checked(选中)的那一行的3列和4列的值
Jul 04 Javascript
使用javascript实现有效时间的控制,并显示将要过期的时间
Jan 02 Javascript
jQuery中removeProp()方法用法实例
Jan 05 Javascript
jquery实现鼠标滑过后动态图片提示效果实例
Aug 10 Javascript
javascript中window.open在原来的窗口中打开新的窗口(不同名)
Nov 15 Javascript
一篇文章搞定JavaScript类型转换(面试常见)
Jan 21 Javascript
使用bootstrap-paginator.js 分页来进行ajax 异步分页请求示例
Mar 09 Javascript
js实现鼠标拖拽缩放div实例代码
Mar 25 Javascript
详解vue-cli3多环境打包配置
Mar 28 Javascript
对layui中的onevent 和event的使用详解
Sep 06 Javascript
详解Vue2的diff算法
Jan 06 Vue.js
vue ant design 封装弹窗表单的使用
Jun 01 #Vue.js
让JavaScript代码更加精简的方法技巧
Jun 01 #Javascript
cypress测试本地web应用
Jun 01 #Javascript
vue实现登陆页面开发实践
May 30 #Vue.js
Echarts如何重新渲染实例详解
May 30 #Javascript
vue router 动态路由清除方式
May 25 #Vue.js
vue如何清除浏览器历史栈
May 25 #Vue.js
You might like
phpmyadmin配置文件现在需要绝密的短密码(blowfish_secret)的2种解决方法
2014/05/07 PHP
PHP、Python和Javascript的装饰器模式对比
2015/02/03 PHP
PHP调用Linux命令权限不足问题解决方法
2015/02/07 PHP
Symfony2在Nginx下的配置方法图文教程
2016/02/04 PHP
PHP简单获取多个checkbox值的方法
2016/06/13 PHP
PHP实现带进度条的Ajax文件上传功能示例
2019/07/02 PHP
一段效率很高的for循环语句使用方法
2007/08/13 Javascript
javascript模版引擎-tmpl的bug修复与性能优化分析
2011/10/23 Javascript
基于jquery的跟随屏幕滚动代码
2012/07/24 Javascript
jquery图片放大镜功能的实例代码
2013/03/26 Javascript
Javascript连接多个数组不用concat来解决
2014/03/24 Javascript
jQuery实现高亮显示的方法
2015/03/10 Javascript
浅谈JavaScript 的执行顺序
2015/08/07 Javascript
jquery转盘抽奖功能实现
2015/11/13 Javascript
获取input标签的所有属性的方法
2016/06/28 Javascript
详解搭建es6+devServer简单开发环境
2018/09/25 Javascript
js 实现在2d平面上画8的方法
2018/10/10 Javascript
微信小程序常见页面跳转操作简单示例
2019/05/01 Javascript
如何实现小程序tab栏下划线动画效果
2019/05/18 Javascript
vue设置动态请求地址的例子
2019/11/01 Javascript
微信小程序搜索框样式并实现跳转到搜索页面(小程序搜索功能)
2020/03/10 Javascript
[04:10]2018年度CS GO玩家最喜爱的主播-完美盛典
2018/12/16 DOTA
[48:28]完美世界DOTA2联赛循环赛FTD vs Magma第二场 10月30日
2020/10/31 DOTA
Pycharm 操作Django Model的简单运用方法
2018/05/23 Python
详解python里的命名规范
2018/07/16 Python
python实现文件助手中查看微信撤回消息
2019/04/29 Python
用python的turtle模块实现给女票画个小心心
2019/11/23 Python
Matplotlib 折线图plot()所有用法详解
2020/07/28 Python
惠普香港官方商店:HP香港
2019/04/30 全球购物
Abbott Lyon官网:女士手表、珠宝及配件
2020/12/26 全球购物
请写出char *p与"零值"比较的if语句
2014/09/24 面试题
自荐信结尾
2013/10/27 职场文书
2015年毕业生实习评语
2015/03/25 职场文书
卢旺达饭店观后感
2015/06/05 职场文书
React Fragment介绍与使用详解
2021/11/11 Javascript
pycharm无法安装cv2模块问题
2022/05/20 Python