前端基石:预处理机制,变量提升

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 本文主要讲预处理机制,变量提升

网络异常,图片无法展示
|


一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情


变量提升


在“当前上下文”中,代码执行之前,浏览器首先会将所有带 var/function 的关键字进行提前声明和定义,var 进行提前声明,function 不仅会提前声明还是提前定义(赋值)。

例如:


console.log(a);
var a = 1;
console.log(fn);
function fn() {}

网络异常,图片无法展示
|


a 的声明和定义在 console.log(a) 之后,但是提前使用 a 不会报错而是输出了 undefined,说明 a 被提前声明了


fn 的声明和定义在 console.log(fn) 之后,但是提前使用 fn 不会报错,并且正确的输出了 fn,说明 fn 不仅被提前声明了,还被提前定义(赋值)了


变量提升,无论条件是否成立都会进行变量提升。但是在判断体中,function 新老版本浏览器中表现不太一样(var 没有影响),在老版本浏览器中,function 变量提升之后是声明+定义,但是在新版本浏览器中 function 变量提升只会声明不会定义。


新版本浏览器:

网络异常,图片无法展示
|

老版本浏览器:

网络异常,图片无法展示
|


let、const 的类变量提升


为什么说 let、const 是类变量提升了,因为let、const 本身是没有变量提升的,但是他们存在某种机制来对 let、const 进行处理。

以 let 为例:


console.log(a);
let a = 1;】

由于 let 是没有变量提升机制的,所以在还没有声明之前就使用了肯定会报错,但是会报什么错误了?「Uncaught ReferenceError: a is not defined」

网络异常,图片无法展示
|

其实不然,报错 「Uncaught ReferenceError: Cannot access 'a' before initialization」

网络异常,图片无法展示
|

注意:如果你要在浏览器控制台中直接运行,请加上一个块级作用域。

{
    console.log(a);
    let a = 1;
}


这是为什么了?其实最开始浏览器从服务器获取的js都是文本(字符串),声明的文件格式「content-type: application/javascript」。浏览器首先按照这个格式去解析代码,也就是进行「词法解析」生成 AST 语法树。


基于 let、const 等声明的变量,在词法解析阶段就已经明确在此上下文中未来一定会存在这些 let、const 声明的变量,在代码执行时,如果在具体声明的变量之前使用了这些变量,浏览器就会报错 「Uncaught ReferenceError: Cannot access 'a' before initialization」。所以说 let 、const 类似会有一种变量提升,但是本质上并不是变量提升。


看如下代码,用 debugger 看看。


debugger;
console.log(a);
var a = 1;
console.log(b);
let b = 2;

网络异常,图片无法展示
|


发现 b 是定义在脚本中,这里的脚本就是 VG(G),而 a 是在 全局 GO 中,说明二者并不是一样的处理,let 词法解析阶段就已经明确在此上下文中未来一定会存在这些 let、const 声明的变量。


接下来我们看一个面试练习。


面试练习


console.log(fn);
function fn() {
  console.log(1);
}
console.log(fn);
var fn = 12;
console.log(fn);
function fn(){
  console.log(2);
}
console.log(fn);


第一步变量提升


  1. function fn() {console.log(1);} fn 函数声明加定义,一个函数的创建会在 Heap 堆内存中开辟一块空间(0x001)来存储函数(详情看上一篇文章:函数的底层执行机制),所以 fn -> 0x001。

网络异常,图片无法展示
|

  1. var fn = 12; var 只会声明不会定义,但是发现上下文中已经声明过 fn 了,所以不会重复声明。
  2. function fn(){console.log(2);},fn 现在是一个新的函数,但是发现在此上下文中已经存在 fn ,所以不会重复声明,但是会重新定义。所以开辟一块新的内存空间(0x002)来存储新的 fn 函数。对比第一步的 fn 内存地址是不一样的,开辟新的内存就是一个定义的操作,所以 fn -> 0x002。

网络异常,图片无法展示
|


第二步代码执行


  1. console.log(fn); 在此上下文中查找 fn,发现存在,fn -> 0x002。所以输出 ƒ fn(){console.log(2);}
  2. function fn() {console.log(1);} 跳过,因为这一步在变量提升阶段已经处理过了。
  3. console.log(fn); 在此上下文中查找 fn,发现存在,fn -> 0x002。所以输出 ƒ fn(){console.log(2);}
  4. var fn = 12; var 声明已经在变量提升阶段处理过,现在就是进行重新赋值。fn -> 12

网络异常,图片无法展示
|

  1. console.log(fn); 在此上下文中查找 fn,发现存在,fn -> 12。所以输出 12。
  2. function fn(){console.log(2);} 跳过,因为这一步在变量提升阶段已经处理过了。
  3. console.log(fn); 在此上下文中查找 fn,发现存在,fn -> 12。所以输出 12。

网络异常,图片无法展示
|


总结


这类变量提升的题目,在面试中会经常出现,但是很多同学会认为比较简单,往往出问题,所以遇到这类题目,需要熟练的在大脑中构想一副脑图,明白内存中变量的存储指向。这样才能清楚无误的输出结果。

目录
相关文章
|
8月前
|
存储 前端开发 JavaScript
第六章(原理篇) 微前端间的通信机制
第六章(原理篇) 微前端间的通信机制
187 0
|
8月前
|
移动开发 前端开发 API
深入理解前端路由:构建现代 Web 应用的基石(上)
深入理解前端路由:构建现代 Web 应用的基石(上)
深入理解前端路由:构建现代 Web 应用的基石(上)
|
8月前
|
存储 前端开发 JavaScript
深入理解前端JavaScript执行机制
深入理解前端JavaScript执行机制
82 0
|
8月前
|
前端开发 安全 JavaScript
前端开发中的跨域资源共享(CORS)机制
【2月更文挑战第3天】 在前端开发中,跨域资源共享(CORS)机制是一个重要的安全性问题。本文将介绍CORS的概念、原理和实现方式,并探讨在前端开发中如何处理跨域资源请求,以及如何提高网站的安全性。
|
3月前
|
存储 监控 前端开发
掌握微前端架构:构建未来前端应用的基石
【10月更文挑战第12天】随着前端技术的发展,传统的单体应用架构已无法满足现代应用的需求。微前端架构通过将大型应用拆分为独立的小模块,提供了更高的灵活性、可维护性和快速迭代能力。本文介绍了微前端架构的概念、核心优势及实施步骤,并探讨了其在复杂应用中的应用及实战技巧。
|
3月前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
|
4月前
|
存储 前端开发 JavaScript
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
本文介绍了JavaScript中作用域的概念,包括全局变量和局部变量的区别,预解析机制(变量提升),以及函数返回值的使用和类型。通过具体示例讲解了变量的作用域、函数的返回值、以及如何通过return关键字从函数中返回数据。
31 1
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
|
3月前
|
JSON 前端开发 JavaScript
构建现代前端应用的基石
【10月更文挑战第13天】构建现代前端应用的基石
|
8月前
|
移动开发 前端开发 数据可视化
前端HTML:构建网页的基石
前端HTML:构建网页的基石
56 0
|
8月前
|
JavaScript 前端开发 开发者
【Web 前端】什么是JS变量提升?
【5月更文挑战第1天】【Web 前端】什么是JS变量提升?
【Web 前端】什么是JS变量提升?