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 相关文章推荐
javascript的键盘控制事件说明
Apr 15 Javascript
Array.prototype.concat不是通用方法反驳[译]
Sep 20 Javascript
javascript读写XML实现广告轮换(兼容IE、FF)
Aug 09 Javascript
js与jQuery 获取父窗、子窗的iframe
Dec 20 Javascript
js判断当页面无法回退时关闭网页否则就history.go(-1)
Aug 07 Javascript
jQuery中的each()详细介绍(推荐)
May 25 Javascript
Javascript实现前端简单的路由实例
Sep 11 Javascript
微信小程序媒体组件详解(视频,音乐,图片)
Sep 19 Javascript
shiro授权的实现原理
Sep 21 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
Nov 12 Javascript
Vue 框架之键盘事件、健值修饰符、双向数据绑定
Nov 14 Javascript
keep-alive保持组件状态的方法
Dec 02 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
深入理解:XML与对象的序列化与反序列化
2013/06/08 PHP
那些年我们错过的魔术方法(Magic Methods)
2014/01/14 PHP
destoon公司主页模板风格的添加方法
2014/06/20 PHP
PHP封装的HttpClient类用法实例
2015/06/17 PHP
PHP策略模式定义与用法示例
2017/07/27 PHP
cnblogs csdn 代码运行框实现代码
2009/11/02 Javascript
JavaScript.Encode手动解码技巧
2010/07/14 Javascript
js函数setTimeout延迟执行的简单介绍
2013/07/17 Javascript
js鼠标点击按钮切换图片-图片自动切换-点击左右按钮切换特效代码
2015/09/02 Javascript
jQuery文字横向滚动效果的实现代码
2016/05/31 Javascript
jQuery实现鼠标滑过预览图片大图效果的方法
2017/04/26 jQuery
使用 Node.js 开发资讯爬虫流程
2018/01/07 Javascript
JS从非数组对象转数组的方法小结
2018/03/26 Javascript
微信小程序手机号码验证功能的实例代码
2018/08/28 Javascript
详解关于vue2.0工程发布上线操作步骤
2018/09/27 Javascript
express+vue+mongodb+session 实现注册登录功能
2018/12/06 Javascript
Vue的H5页面唤起支付宝支付功能
2019/04/18 Javascript
小程序的上传文件接口的注意要点解析
2019/09/17 Javascript
[53:52]EG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
Python爬取网易云音乐热门评论
2017/03/31 Python
用Python设计一个经典小游戏
2017/05/15 Python
Python3.4编程实现简单抓取爬虫功能示例
2017/09/14 Python
遗传算法之Python实现代码
2017/10/10 Python
python pcm音频添加头转成Wav格式文件的方法
2019/01/09 Python
Python设计模式之迭代器模式原理与用法实例分析
2019/01/10 Python
Python爬虫UA伪装爬取的实例讲解
2021/02/19 Python
佳能德国网上商店:Canon德国
2017/03/18 全球购物
世界领先的艺术图书出版社:TASCHEN
2018/07/23 全球购物
写给女朋友的检讨书
2014/01/28 职场文书
妇女干部培训方案
2014/05/12 职场文书
机关职员工作检讨书
2014/10/23 职场文书
2015年汽车销售工作总结
2015/04/07 职场文书
让生命充满爱观后感
2015/06/08 职场文书
简历自我评价:教师师德表现自我评价
2019/04/24 职场文书
pytorch实现ResNet结构的实例代码
2021/05/17 Python
Ruby使用Mysql2连接操作MySQL
2022/04/19 Ruby