浅析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 相关文章推荐
不用写JS也能使用EXTJS视频演示
Dec 29 Javascript
JQuery入门——事件切换之hover()方法应用介绍
Feb 05 Javascript
jQuery下实现等待指定元素加载完毕(可改成纯js版)
Jul 11 Javascript
JavaScript中json使用自己总结
Aug 13 Javascript
jquery 鼠标滑动显示详情应用示例
Jan 24 Javascript
jquery禁止输入数字以外的字符的示例(纯数字验证码)
Apr 10 Javascript
javascript在网页中实现读取剪贴板粘贴截图功能
Jun 07 Javascript
js插件YprogressBar实现漂亮的进度条效果
Apr 20 Javascript
js父页面中使用子页面的方法
Jan 09 Javascript
全面解析JS字符串和正则表达式中的match、replace、exec等函数
Jul 01 Javascript
使用 jQuery 实现表单验证功能
Jul 05 jQuery
全网小程序接口请求封装实例代码
Nov 06 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的FTP学习(二)[转自奥索]
2006/10/09 PHP
javascript整除实现代码
2010/11/23 Javascript
JSON辅助格式化处理方法
2013/03/26 Javascript
javascript实现焦点滚动图效果 具体方法
2013/06/24 Javascript
Ext JS 4实现带week(星期)的日期选择控件(实战一)
2013/08/21 Javascript
Javascript 鼠标移动上去 滑块跟随效果代码分享
2013/11/23 Javascript
node.js操作mongodb学习小结
2015/04/25 Javascript
jquery实现鼠标滑过小图查看大图的方法
2015/07/20 Javascript
Bootstrap入门书籍之(三)栅格系统
2016/02/17 Javascript
jstree创建无限分级树的方法【基于ajax动态创建子节点】
2016/10/25 Javascript
canvas学习之API整理笔记(二)
2016/12/29 Javascript
JS实现数组按升序及降序排列的方法
2017/04/26 Javascript
NodeJs安装npm包一直失败的解决方法
2017/04/28 NodeJs
Javascript实现时间倒计时效果
2017/07/15 Javascript
jQuery基于Ajax实现读取XML数据功能示例
2018/05/31 jQuery
在layui中select更改后生效的方法
2019/09/05 Javascript
解决在layer.open中使用时间控件laydate失败的问题
2019/09/11 Javascript
vue+springboot图片上传和显示的示例代码
2020/02/14 Javascript
Python Socket传输文件示例
2017/01/16 Python
Python 最大概率法进行汉语切分的方法
2018/12/14 Python
python感知机实现代码
2019/01/18 Python
彻底理解Python中的yield关键字
2019/04/01 Python
利用Python绘制有趣的万圣节南瓜怪效果
2019/10/31 Python
ansible动态Inventory主机清单配置遇到的坑
2020/01/19 Python
Pycharm 安装 idea VIM插件的图文教程详解
2020/02/21 Python
selenium携带cookies模拟登陆CSDN的实现
2021/01/19 Python
德国古洛迷亚百货官网:GALERIA Kaufhof
2017/06/20 全球购物
Lookfantastic意大利官网:英国知名美妆购物网站
2019/05/31 全球购物
手工制作的男士奢华英国鞋和服装之家:Goodwin Smith
2019/06/21 全球购物
荷兰时尚精品店:Labels Fashion
2020/03/22 全球购物
DJI全球:DJI Global
2021/03/15 全球购物
Linux的主要特性
2016/09/03 面试题
JDBC连接的六步实例代码(与mysql连接)
2021/05/12 MySQL
Python中的变量与常量
2021/11/11 Python
使用 Apache Dubbo 实现远程通信(微服务架构)
2022/02/12 Servers
MYSQL中文乱码问题的解决方案
2022/06/14 MySQL