JS温故而知新之变量提升和时间死区


Posted in Javascript onJanuary 27, 2019

前言

开始执行脚本时,执行脚本的第一步是编译代码,然后再开始执行代码,如图

JS温故而知新之变量提升和时间死区

另外,在编译优化方面来说,最开始时也并不是全部编译好脚本,而是当函数执行时,才会先编译,再执行脚本,如图

JS温故而知新之变量提升和时间死区

  • 编译阶段:经历了词法分析,语法分析生成AST,以及代码生成。并且在此阶段,它只会扫描并且抽出环境中的声明变量,声明函数以便准备分配内存,所有的函数声明和变量声明都会被添加到名为Lexical Environment的JavaScript内部数据结构内的内存中。因此,它们可以在源代码中实际声明之前使用。但是,Javascript只会存储函数声明和变量声明在内存,并不会存储他们的值
  • 执行阶段:给变量x赋值,首先询问内存你这有变量x吗,如果有,则给变量x赋值,如果没有则创建变量x并且给它赋值。

变量提升

如下图,左边灰色块区域,是演示函数执行前的编译阶段,先抽出所有声明变量和声明函数,并进行内存分配。然后再开始执行代码,在执行第一行代码的时候,若是变量a存在于内存中,则直接给变量a赋值。而执行到第二行时,变量b并没有在内存中,则会创建变量b并给它赋值。

JS温故而知新之变量提升和时间死区

Lexical enviroment是一种包含标识符变量映射的数据结构

LexicalEnviroment = {
 Identifier: <value>,
 Indentifier: <function object>
}

简而言之,Lexical enviroment就是程序执行过程中变量和函数存在的地方。

let,const变量

console.log(a)
let a = 3;

输出

ReferenceError: a is not defined

所以let和const变量并不会被提升吗?

这个答案会比较复杂。所有的声明(function, var, let, const and class)在JavaScript中都会被提升,然而var声明被undefined值初始化,但是let和const声明的值仍然未被初始化。

它们仅仅只在Javascript引擎运行期间它们的词法绑定被执行在才会被初始化。这意味着引擎在源代码中声明它的位置计算其值之前,你无法访问该变量。这就是我们所说的时间死区,即变量创建和初始化之间的时间,我们无法访问该变量。

如果JavaScript引擎仍然无法在声明它们的行中找到let或者const的值,它将为它们分配undefined值或返回错误值(在const的情况下会返回错误值)。

JS温故而知新之变量提升和时间死区

6a9a50532bf60f5fac6b3c.png](evernotecid://F2BCA3B5-CC5A-4EB3-BD61-DD865800F342/appyinxiangcom/10369121/ENResource/p1163)

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

在编译阶段,JavaScript引擎遇到变量a并将它存储在lexical enviroment,但是因为它是一个let变量,所以引擎不会为它初始化任何值。所以,在编译阶段,lexical enviroment看起来像下面这样。

// 编译阶段
lexicalEnvironment = {
 a: <uninitialized>
}

现在如果我们尝试在声明它之前访问该变量,JavaScript引擎将会尝试从词法环境中拿到这个变量的值,因为这个变量未被初始化,它将抛出一个引用错误。

在执行期间,当引擎到达了变量声明的行,它将试图执行它的绑定,因为该变量没有与之关联的值,因此它将为其赋值为unedfined

// 执行阶段
lexicalEnviroment = {
 a: undefined
}

之后,undefined将会被打印到控制台,然后将值5赋值给变量a,lexical enviroment中变量a的值也会从undefined更新为5

functionn foo() {
console.log(a)
}

let a = 20;

foo();
function foo() {
console.log(a): // ReferenceError: a is not defined
}
foo();
let a = 20;

JS温故而知新之变量提升和时间死区

Class Declaration

就像let和const声明一样,class在JavaScript中也会被提升,并且和let,const一样,知道执行之前,它们都会保持uninitialized。因此它们同样会受到Temporal Deal Zone(时间死区)的影响。例如

let peter = new Person('Peter', 25); // ReferenceError: Person is not defined

console.log(peter);

class Person {
 constructor(name, age) {
 this.name = name;
 this.age = age;
 }
}

因此要访问class,必须先声明它

class Person {
 constructor(name, age) {
 this.name = name;
 this.age = age;
 }
}

let peter = new Person('Peter', 25); 
console.log(peter);
// Person { name: 'Peter', age: 25 }

所以在编译阶段,上面代码的lexical environment(词法环境)将如下所示:

lexicalEnvironment = {
 Person: <uninitialized>
}

当引擎执行class声明时,它将使用值初始化类。

lexicalEnvironment = {
 Person: <Person object>
}

提升Class Expressions

let peter = new Person('Peter', 25);
console.log(peter);
let Person = class {
 constructor(name, age) {
 this.name = name;
 this.age = age;
 }
}

JS温故而知新之变量提升和时间死区

let peter = new Person('Peter', 25); 
console.log(peter);
var Person = class {
 constructor(name, age) {
 this.name = name;
 this.age = age;
 }
}

JS温故而知新之变量提升和时间死区

所以现在我们知道在提升过程中我们的代码并没有被JavaScript引擎实际移动。正确理解提升机制将有助于避免因变量提升而产生的任何未来错误和混乱。为了避免像未定义的变量或引用错误一样可能产生的副作用,请始终尝试将变量声明在各自作用域的顶部,并始终尝试在声明变量时初始化变量。

Hoisting in Modern JavaScript — let, const, and var

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
JS如何将UTC格式时间转本地格式
Sep 04 Javascript
JS对HTML标签select的获取、添加、删除操作
Oct 17 Javascript
JS实现鼠标经过好友列表中的好友头像时显示资料卡的效果
Jul 02 Javascript
Jquery插件实现点击获取验证码后60秒内禁止重新获取
Mar 13 Javascript
JavaScript使用shift方法移除素组第一个元素实例分析
Apr 06 Javascript
JS中BOM相关知识点总结(必看篇)
Nov 22 Javascript
javascript 单例模式详解及简单实例
Feb 14 Javascript
canvas仿iwatch时钟效果
Mar 06 Javascript
原生JS实现瀑布流插件
Feb 06 Javascript
vue打包的时候自动将px转成rem的操作方法
Jun 20 Javascript
vue 使用axios 数据请求第三方插件的使用教程详解
Jul 05 Javascript
使用JavaScrip模拟实现仿京东搜索框功能
Oct 16 Javascript
vue组件文档(.md)中如何自动导入示例(.vue)详解
Jan 25 #Javascript
命令行批量截图Node脚本示例代码
Jan 25 #Javascript
Node.js 进程平滑离场剖析小结
Jan 24 #Javascript
Vue.js样式动态绑定实现小结
Jan 24 #Javascript
实例讲解JavaScript预编译流程
Jan 24 #Javascript
实例讲解vue源码架构
Jan 24 #Javascript
详解Node.js amqplib 连接 Rabbit MQ最佳实践
Jan 24 #Javascript
You might like
phpmyadmin中配置文件现在需要绝密的短语密码的解决方法
2007/02/11 PHP
PHP ? EasyUI DataGrid 资料取的方式介绍
2012/11/07 PHP
php 表单提交大量数据发生丢失的解决方法
2014/03/03 PHP
JavaScript实现删除电脑的关机键
2016/07/26 PHP
laravel框架之数据库查出来的对象实现转化为数组
2019/10/23 PHP
Laravel自动生成UUID,从建表到使用详解
2019/10/24 PHP
通过JAVAScript实现页面自适应
2007/01/19 Javascript
javaScript 读取和设置文档元素的样式属性
2009/04/14 Javascript
自己的js工具 Event封装
2009/08/21 Javascript
jquery获取div宽度的实现思路与代码
2013/01/13 Javascript
jQuery-1.9.1源码分析系列(十一)DOM操作续之克隆节点
2015/12/01 Javascript
Bootstrap表单组件教程详解
2016/04/26 Javascript
js中使用使用原型(prototype)定义方法的好处详解
2016/07/04 Javascript
AngularJS创建自定义指令的方法详解
2016/11/03 Javascript
原生js实现网页顶部自动下拉/收缩广告效果
2017/01/20 Javascript
vue分类筛选filter方法简单实例
2017/03/30 Javascript
Echarts之悬浮框中的数据排序问题
2018/11/08 Javascript
如何封装了一个vue移动端下拉加载下一页数据的组件
2019/01/06 Javascript
layui 解决form表单点击无反应的问题
2019/10/25 Javascript
对vue中的事件穿透与禁止穿透实例详解
2019/10/28 Javascript
详解Python中的array数组模块相关使用
2016/07/05 Python
Pycharm学习教程(6) Pycharm作为Vim编辑器使用
2017/05/03 Python
python中numpy包使用教程之数组和相关操作详解
2017/07/30 Python
python使用opencv对图像mask处理的方法
2019/07/05 Python
Django  ORM 练习题及答案
2019/07/19 Python
python selenium爬取斗鱼所有直播房间信息过程详解
2019/08/09 Python
打包PyQt5应用时的注意事项
2020/02/14 Python
Java Spring项目国际化(i18n)详细方法与实例
2020/03/20 Python
英国排名第一的最新设计师品牌手表独立零售商:TIC Watches
2016/09/24 全球购物
Bibloo荷兰:女士、男士和儿童的服装、鞋子和配饰
2019/02/25 全球购物
酒店总经理欢迎词
2014/01/15 职场文书
物流管理毕业生自荐信范文
2014/03/15 职场文书
教师见习报告范文
2014/11/03 职场文书
煤矿百日安全活动总结
2015/05/07 职场文书
2016教师校本培训心得体会
2016/01/08 职场文书
Python图片检索之以图搜图
2021/05/31 Python