JavaScript变量提升运行机制

简介: JavaScript的工作原理是,先解析代码,获取所有声明的变量或者函数,然后运行。这造成的结果就是所有声明的变量或者函数都被提升到代码的头部,这叫做声明提示提升。


JavaScript的工作原理是,先解析代码,获取所有声明的变量或者函数,然后运行。这造成的结果就是所有声明的变量或者函数都被提升到代码的头部,这叫做声明提示提升。


先看一个换汤不换药的经典例子:这个例子大家其实都知道发生了变量提升导致;结果是undefined。问题来了,这个提升的过程在哪里发送?怎么发生?


console.log(a);  // undefined
var a = 1;
b()              // 1
function b() {
    console.log(a)
}


一、JavaScript的执行过程


1. JavaScript在执行之前会经历一个编的过程(其他术语:创建、预解释过程)。


1.1 宏观的角度


  • 创建(包含变量对象,父级作用域上下文的变量对象)。
  • 创建(参数、内部变量、函数声明)。
  • 设置
PS:变量对象的创建过程
  • 根据函数的参数,创建并初始化argument object。
  • 扫描函数内部的代码,查找函数声明。对于所有找到的函数名和函数引用,都会放到变量对象中。如果之前已经存在同名的函数,会进行覆盖。
  • 扫描函数内部的代码,找到变量声明,对于所有找到的变量申明,都放到变量对象中,并初始化为undefined,变量名称跟已经声明的参数或者是函数相同,就会被忽略,不会干扰到之前的声明。


1.2 微观的角度


  • ,这个过程就是将字符串分解为有意义的代码块,这些代码块我们称为词法单元。
  • ,这个过程是将词法单位抽象为抽象语法树AST。
  • 。将AST生成可以执行的代码。


2. 编译完之后会进行一个执行阶段。


2.1 执行:变量的值、函数的引用、执行代码。


1、,例如在代码中var a,编译器会询问作用域中是否有一个该名称的变量存在。如果存在,就继续往下编译,如果不存在就会在当前作用域申明一个新的变量。

2、运行阶段,引擎发现代码a = 1的时候,首先会查询当前作用域是否是否有一个名为a的变量,如果存在就进行 赋值,。大多数情况下,编译发生在代码执行之前的几微秒(甚至更短)。


二、ES6 let、const


ES新增的变量声明语法let与const。我们用let作为例子。


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


这段代码直接报错a is not defined,let和const拥有类似的特征,阻止了变量提升,当执行console.log(a)的时候变量没有定义,所以报错了。 在阮一峰老师的网站也写到let和const都是不存在变量提升的。如下

,对此我也产生的疑问,发现在不同的权威机构有着不一样的解释。


  • MDN中写到:In ECMAScript 2015, let do not support Variable Hoisting, which means the declarations made using "let", do not move to the top of the execution block.

在MDN中认为let不存在变量提升

  • ECMA-262-13.3.1 Let and Const Declarations写到: let and const declarations define variables that are scoped to the running execution context's LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.

这说明即使是 block 最后一行的 let 声明,也会影响 block 的第一行。这就是提升(hoisting)

  • ECMA-262: 8.2.1.2 Runtime Semantics: EvalDeclarationInstantiation( body, varEnv, lexEnv, strict)写到: The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts.

这句话也间接的证明 let hoisting 的存在。


那其实大家会有疑问,为什么上面的代码会报错。其实这并不是由于变量不提示导致的,而是由于TDZ(临时性死区)导致的。


在举个例子:


{
    a = 2;
    let a;
}


这段代码可以解释为:


{
    let a;// 变量提升
    "start TDZ"
    a = 2; // 这里在TDZ中间,所以会导致a = 2 报错
    a;
    "end TDZ"
}


所以破案了:let是存在变量提升。它“变量提升的行为”,是由于TDZ导致的。


so...总结一下

  • let 声明会提升到块顶部
  • 从块顶部到该变量的初始化语句,这块区域叫做 TDZ(临时死区)
  • 如果你在 TDZ 内使用该变量,JS 就会报错,注意TDZ 跟 hoisting不等价。
目录
相关文章
|
6天前
|
JavaScript 安全 前端开发
乾坤js隔离机制
乾坤js隔离机制
|
13天前
|
前端开发 JavaScript Java
JavaScript的运行原理
JavaScript 的运行原理包括代码输入、解析、编译、执行、内存管理和与浏览器交互几个步骤。当打开网页时,浏览器加载 HTML、CSS 和 JavaScript 文件,并通过 JavaScript 引擎将其解析为抽象语法树(AST)。接着,引擎将 AST 编译成字节码或机器码,并在执行阶段利用事件循环机制处理异步操作,确保单线程的 JavaScript 能够高效运行。同时,JavaScript 引擎还负责内存管理和垃圾回收,以减少内存泄漏。通过与 DOM 的交互,JavaScript 实现了动态网页效果,提供了灵活且高效的开发体验。
|
6天前
|
缓存 JavaScript 前端开发
常见的 JavaScript 隔离机制
常见的 JavaScript 隔离机制
|
2月前
|
监控 JavaScript Linux
[译] 在生产环境运行 PM2 & Node.js
[译] 在生产环境运行 PM2 & Node.js
|
2月前
|
JavaScript 前端开发 算法
js 内存回收机制
【8月更文挑战第23天】js 内存回收机制
33 3
|
2月前
|
存储 JavaScript 前端开发
学习JavaScript 内存机制
【8月更文挑战第23天】学习JavaScript 内存机制
27 3
|
2月前
|
JavaScript 中间件 开发者
深入浅出Node.js中间件机制
【8月更文挑战第31天】本文将带你领略Node.js中间件的奥秘,通过直观的案例分析,揭示其背后的设计哲学。你将学会如何运用中间件构建强大而灵活的后端应用,以及在面对复杂业务逻辑时如何保持代码的清晰与高效。
|
2月前
|
设计模式 JavaScript 中间件
深入浅出Node.js中间件机制
【8月更文挑战第31天】在Node.js的世界里,中间件如同魔法般存在,它让复杂的请求处理变得井然有序。本文将带你领略中间件的奥秘,从原理到实战,一步步揭开它的神秘面纱。你将学会如何运用中间件来构建强大而灵活的后端应用,就像拼乐高一样有趣。
|
2月前
|
JavaScript Windows
【Azure 应用服务】用App Service部署运行 Vue.js 编写的项目,应该怎么部署运行呢?
【Azure 应用服务】用App Service部署运行 Vue.js 编写的项目,应该怎么部署运行呢?
|
2月前
|
JavaScript 前端开发 C++
【Azure Function】调试 VS Code Javascript Function本地不能运行,报错 Value cannot be null. (Parameter 'provider')问题
【Azure Function】调试 VS Code Javascript Function本地不能运行,报错 Value cannot be null. (Parameter 'provider')问题