切换语言为:繁体

从 JavaScript 引擎编译到var/let/const的深入理解

  • 爱糖宝
  • 2024-07-23
  • 2066
  • 0
  • 0

JS引擎的编译时机

JavaScript也是需要编译才能执行的!!!它与某些编程语言(Java、C/C++、Golang等)不同,这些语言在执行时一定是编译好的文件;JavaScript是解释型语言,执行之前仍然是用户编写的源代码,并不是一个编译后的文件;它会一边执行一边编译,编译时机在代码执行环境进入一个新的作用域到开始执行第一行代码中间的时间片(微秒级、也能解释为什么代码错误只能在执行时才发现)

变量提升讨论

var/function

通过var/function声明的变量,会出现变量提升的现象,也就是说在代码块执行之前(编译)会识别var和funciton声明的变量,并将其声明在代码块顶部,初始化为undefined

如:
{  
    console.log(1);  
    var a = 1;  
    var b = 2;  
}  
编译为:  
{  
    var a; // 变量提升初始化为undefined  
    var b; // 变量提升初始化为undefined  
    console.log(1);  
    a = 1;  
    b = 2;  
}

let/const

目前网上很多解释都说let/const声明的变量不存在变量提升,但我不赞同!我认为let/const同var/funciton一样,也是具有变量提升属性的,区别在于let/const有一些“特殊处理”

  • 暂时性死区:通过let/const声明的变量存在暂时性死区,通俗来说就是声明的这个变量暂时不可使用;仔细理解“暂时不可用”,可以得到结论:变量已经声明了,只是不可用。也就是说变量在没有定义就已经被声明,足以证明变量提升的存在。

如果你仍然不支持let/const也存在变量提升,请继续往后看:

JS引擎与let/const

前面简单提到JS代码是一边编译一边执行的,现在我们需要了解,编译过程会同步生成一个“符号表”,用于记录当前作用域所包含的所有变量(包括let/const声明的变量),这个表中记录 标识符名称(变量/函数等名称)、声明类型(var/let/const)、初始化状态(是否被初始化)、初始化值(默认undefined)(这里只列出便于本文分析的数据,符号表相当复杂且不同引擎的实现存在差异)

let/const特性在JS引擎的实现

function func() {};
var a = 1;
let b = 2;
const c = 3;

  • 代码未执行的符号表数据

标识符名称 声明类型 初始化状态 初始值
func function true func: Function
a var true undefined
b let false undefined
c const false 3
  • 代码执行完毕的符号表数据

标识符名称 声明类型 初始化状态 初始值
func function true func: Function
a var true 1
b let true 2
c const true 3
  • let/const暂时性死区特性
    通过上表可以分析出,暂时性死区其实就是对应“初始化状态”列,在“符号表”被创建时:var/function方式声明的变量对应初始化状态为true。let/const类型声明的变量初始化状态为false。

  • JS引擎判断暂时性死区伪代码

/**
 * 执行在读取变量值方法
 */
// declared - 是否已声明  |  initialized - 是否初始化
if (!declared(a) || !initialized(a)) {  
    throw new ReferenceError("a is not defined.");
}

const初始化数据和不可修改特性
  • 声明但未给初始化值:这个异常在编译阶段就会出现,在生成“符号表”过程中,判断变量声明类型是否为const,如果是且未给定初始值,则抛出错误

/**
 * 执行在编译阶段
 */
// declareTypeIsConst - 是否通过const声明  |  enableValue - 是否给定初始值
if (declareTypeIsConst(a) && !enableValue(a)) {
    throw new SyntaxError("Missing initializer in const declaration.");
}

  • 后续修改:在变量修改方法中,判断当前变量声明类型是否是const,如果是,则抛出错误

/**
 * 执行在修改变量值方法
 */
// declareTypeIsConst - 是否通过const声明
if (declareTypeIsConst(a)) {
  throw new TypeError("Assignment to constant variable.");
}

一些问题

  1. Q: ES6编译为ES5后,还能保持let/const的特性吗?
    A: 不能完全保持let/const的特性

  • 解释1(特性消失):let和const在声明之前使用会报错

    • ES6: 报错(期望状态)

    • ES5:未报错

从 JavaScript 引擎编译到var/let/const的深入理解

从 JavaScript 引擎编译到var/let/const的深入理解

  • 解释2(特性存在):for语句中let是块级作用域,可以做到依次输出0~5

    • ES6:正常输出

    • ES5:正常输出

从 JavaScript 引擎编译到var/let/const的深入理解

从 JavaScript 引擎编译到var/let/const的深入理解


0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.