JS: 变量提升与时间死区

栏目: JavaScript · 发布时间: 5年前

内容简介:开始执行脚本时,执行脚本的第一步是编译代码,然后再开始执行代码,如图另外,在编译优化方面来说,最开始时也并不是全部编译好脚本,而是当函数执行时,才会先编译,再执行脚本,如图

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

JS: 变量提升与时间死区

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

JS: 变量提升与时间死区

Lexical Environment

变量提升

如下图,左边灰色块区域,是演示函数执行前的编译阶段,先抽出所有声明变量和声明函数,并进行内存分配。然后再开始执行代码,在执行第一行代码的时候,若是变量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 值初始化,但是 letconst 声明的值仍然未被初始化。

它们仅仅只在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

就像 letconst 声明一样, 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


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Black Box Society

The Black Box Society

Frank Pasquale / Harvard University Press / 2015-1-5 / USD 35.00

Every day, corporations are connecting the dots about our personal behavior—silently scrutinizing clues left behind by our work habits and Internet use. The data compiled and portraits created are inc......一起来看看 《The Black Box Society》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具