浅析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 相关文章推荐
jQuery 创建Dom元素
May 07 Javascript
Jquery判断IE6等浏览器的代码
Apr 05 Javascript
真正的JQuery.ajax传递中文参数的解决方法
May 28 Javascript
用JavaScript计算在UTF-8下存储字符串占用字节数
Aug 08 Javascript
利用jquery包将字符串生成二维码图片
Sep 12 Javascript
JS获取图片高度宽度的方法分享
Apr 17 Javascript
微信小程序 地图定位简单实例
Oct 14 Javascript
微信小程序 自定义对话框实例详解
Jan 20 Javascript
基于JavaScript实现复选框的全选和取消全选
Feb 09 Javascript
Angular在一个页面中使用两个ng-app的方法
Feb 20 Javascript
javascript九宫格图片随机打乱位置的实现方法
Mar 15 Javascript
js中时间格式化的几种方法
Jul 22 Javascript
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
php设计模式 Mediator (中介者模式)
2011/06/26 PHP
如何获知PHP程序占用多少内存(memory_get_usage)
2012/09/23 PHP
PHP获取MSN好友列表类的实现代码
2013/06/23 PHP
JavaScript.Encode手动解码技巧
2010/07/14 Javascript
jQuery 源码分析笔记(4) Ready函数
2011/06/02 Javascript
用jquery的方法制作一个简单的导航栏
2014/06/23 Javascript
jQuery实现ichat在线客服插件
2014/12/29 Javascript
基于js实现投票的实例代码
2015/08/04 Javascript
JavaScript编程中布尔对象的基本使用
2015/10/25 Javascript
详解JavaScript中的Unescape()和String() 函数
2015/11/09 Javascript
node.js+express制作网页计算器
2016/01/17 Javascript
javascript表单控件实例讲解
2016/09/13 Javascript
利用百度地图API获取当前位置信息的实例
2017/11/06 Javascript
js实现下拉框二级联动
2018/12/04 Javascript
jquery无缝图片轮播组件封装
2020/11/25 jQuery
原生JS实现烟花效果
2020/03/10 Javascript
Element Cascader 级联选择器的使用示例
2020/07/27 Javascript
探索浏览器页面关闭window.close()的使用详解
2020/08/21 Javascript
Javascript Symbol原理及使用方法解析
2020/10/22 Javascript
[01:11:32]VG vs FNATIC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
python使用wmi模块获取windows下的系统信息 监控系统
2015/10/27 Python
Python用list或dict字段模式读取文件的方法
2017/01/10 Python
Python编程判断这天是这一年第几天的方法示例
2017/04/18 Python
python将邻接矩阵输出成图的实现
2019/11/21 Python
通过实例解析python描述符原理作用
2020/01/22 Python
PyCharm Anaconda配置PyQt5开发环境及创建项目的教程详解
2020/03/24 Python
从0到1使用python开发一个半自动答题小程序的实现
2020/05/12 Python
使用CSS3的appearance属性改变元素的外观的方法
2015/12/12 HTML / CSS
HTML5给汉字加拼音收起展开组件的实现代码
2020/04/08 HTML / CSS
当当网官方旗舰店:中国图书销售夺金品牌
2018/04/02 全球购物
美国经典刺绣和字母儿童服装特卖:Smocked Auctions
2018/07/16 全球购物
班班通项目实施方案
2014/02/25 职场文书
小学开学标语
2014/07/01 职场文书
毕业证委托书范文
2014/09/26 职场文书
python 如何执行控制台命令与操作剪切板
2021/05/20 Python
Nginx安装配置详解
2022/06/25 Servers