学习JavaScript底层逻辑之预编译

认识预编译前首先需要知道声明提升的概念

1. 变量声明,声明提升

在JavaScript中,变量声明(使用var、let、const关键字)会被提升到当前作用域的顶部。但其初始化(赋值)部分仍然保留在原地。 例如:

    console.log(a)var a = 10;

这串代码能够正常运行,它将等价于

    var a;console.log(a)a = 10

2. 函数声明,整体提升

函数声明(使用function关键字)同样会被提升,但不同于变量声明,整个函数体也会被提升,而不仅仅是函数声明。 例如:

    console.log(greet()); // 输出 "Hello, world!" function greet() {return "Hello, world!";}

尽管greet函数在调用之前声明,但由于函数声明的整体提升,这段代码能够正常运行。

注意:

  • 函数表达式不会被提升,只有函数声明会被提升。例如,var greet = function() {…};不会被提升。
  • 块级作用域:let和const引入了块级作用域,这改变了变量声明的作用域规则,使得它们在声明它们的块之外不可见。

函数中的预编译

  1. 创建函数的执行上下文对象 (AO / Lexical Environment)
    每当一个函数被调用时,JavaScript引擎首先会为其创建一个新的执行上下文(Execution Context)。对于函数而言,这个上下文特别被称为活动对象(Activation Object, AO) 或在ES6标准中更准确地称为词法环境(Lexical Environment)。这个环境保存了函数执行时的所有局部变量、参数、以及对外部作用域的引用等信息。

  2. 找形参和变量声明,将形参和变量名作为AO的属性,值为undefined
    接下来,函数的形参(形式参数)和在其内部声明的变量会被收集并加入到AO中。在实际的值被赋给它们之前,这些变量和形参都被初始化为undefined。这意味着在函数体内的任何代码执行之前,所有声明的变量都已经存在,但还没有具体的值。

  3. 将实参和形参统一
    当函数被调用时,传入的实参(实际参数)会与之前声明的形参相匹配并赋值。这个过程发生在预编译阶段之后,实参的值会覆盖AO中形参的默认undefined值。通过这种方式,函数内部就可以使用这些传入的值进行计算和操作。

  4. 在函数体内找函数声明,将函数名作为AO的属性名,值赋予函数体
    最后,函数体内部的函数声明也会被处理。任何在函数内部声明的函数会被提升到AO中,并将其函数体赋值给相应的属性名。这意味着内部函数可以在函数体的任何地方被访问和调用,即使声明在调用点之后。这一点与变量声明类似,但要注意的是,函数声明会覆盖掉同名的变量声明(如果有的话)。

利用上面的步骤分析如下代码

    function fn(a) {console.log(a); // [Function: a]var a = 123console.log(a); // 123  function a() {}console.log(a); // 123var b = function () {}console.log(b); // [Function: b]    function d() {}var d = aconsole.log(d); // 123}fn(1)

详解如下

  1. 创建函数的执行上下文对象 (AO):
    初始化AO,为函数fn创建一个新的词法环境。

  2. 找形参和变量声明:
    将形参a加入AO,值为undefined。发现变量声明var a,加入AO,值为undefined(注意这里先于形参声明,但不影响最终结果,因为形参的赋值会覆盖这个undefined)。发现变量声明var b,加入AO,值为undefined。发现函数声明function d() {},加入AO,值为undefind。
    此时 AO 大致如下:

    AO : {a: undefined, b: undefined,d: undefined}
  1. 将实参和形参统一: 实参1与形参a绑定,此时AO中的a值为1。 此时 AO 大致如下:
    AO : {a: 1, b: undefined,d: undefined}
  1. 处理函数体内的函数声明:
    处理函数声明function a() {},由于函数声明优先级高于变量声明,此处将a视为函数定义,值为函数体。处理var b = function () {},但由于之前已经声明了b,这里相当于赋值操作,b变为一个新函数。
    函数声明function d() {},值变为 function d(){}
    此时 AO 大致如下:
    AO : {a: function a(){}, b: undefined,d: function d(){}}
  1. 最后,执行函数fn里的代码:
console.log(a); // [Function: a]
//打印出a,由于函数声明优先,此时a是函数。var a = 123
//变量a被赋予新值123,覆盖了函数声明。console.log(a); // 123
//打印出a,现在值为123。console.log(a); // 123
//再次打印a,依然是123,因为之前的赋值操作已经改变了a的值。var b = function () {}b被赋予一个新的函数表达式,覆盖了之前的函数声明。console.log(b); // [Function: b]
//打印出b,现在是一个函数表达式的值。function d() {}
//此处的d函数声明被忽略,因为前面已有同名的变量声明。var d = a
//d被赋予a的值,即123。console.log(d); // 123
//打印出d,值为123,因为d被赋值为a的值。

全局预编译

  1. 创建全局执行上下文对象 (Global Object, GO)
    当JavaScript环境初始化时,无论是浏览器环境下的window对象(浏览器全局对象)还是Node.js环境下的global对象(Node.js全局对象),都会创建一个全局执行上下文。

  2. 找变量声明,变量名作为GO的属性名,值为undefined
    在全局范围内,任何使用var、let、const或function声明的变量(函数也是一种特殊类型的变量)都会被识别并处理。在这个阶段,变量名会作为全局执行上下文对象的属性添加,但其值初始化为undefined。这意味着在变量真正被赋值之前,尝试访问它们会得到undefined。

  3. 在全局找函数声明,函数名为GO的属性名,值为函数体
    对于使用function关键字声明的函数,其名称和函数体也会在预编译阶段被加入到全局执行上下文中。与变量类似,函数名成为上下文的一个属性,但其值是函数的定义(函数体),而不是undefined。这允许在函数声明之前调用函数。

sayHello(); // 输出 "Hello, World!"
function sayHello() {console.log("Hello, World!");
}

分析例子:

    var num = 10function foo(){console.log(num)}foo()
  1. 创建GO
GO:{}
  1. 找变量声明 变量num通过var声明并在全局上下文中被提升,其初始值为undefined。
GO:{num : undefined
}
  1. 找函数声明,foo(){}
GO:{num:undefinedfoo: function foo(){}
}
  1. 执行全局
GO:{num:100foo:function foo(){}
}

执行foo()创建AO 最后输出num=100

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/16785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity学习日志

目录 获取相机可视范围的世界坐标(2D) 视口转世界坐标和屏幕转世界坐标的区别: 屏幕转世界坐标 视口转屏幕坐标 视口转屏幕结合3D数学实现可视范围的怪物生成 transform.up游戏对象的方向问题 其实还有一种不用Translate的写法: 修改 transform.up 的行为和影响 C#抽象…

全国各城市间驾车耗时和距离矩阵数据集(更新至2022年)

数据简介:城市之间距离越远,耗时越长。经济发达地区的交通状况较好。各城市之间的驾车耗时和距离存在差异。有些城市之间的交通非常便捷,而有些城市之间的交通则较为不便。这表明中国的交通网络发展尚不平衡,需进一步优化。特别是…

Excel如何统计非数值内容行数

需要用到sum函数,具体公式如下: SUM(IF(ISNONTEXT(G4:G199),0,1))

Java集合框架详解:深入探讨Java中的集合框架

前言 Java集合框架是Java语言中用于存储和操作大量数据的基石。集合框架提供了一套灵活的接口和实现,使得数据的存储、访问和管理变得异常简单。在本专栏中,我们将深入探讨Java集合框架,包括List、Set、Map等集合的使用和内部实现。 集合框…

Go 语言简介 -- 高效、简洁与现代化编程的完美结合

在现代软件开发领域,选择合适的编程语言对于项目的成功至关重要。Go 语言(又称 Golang )自 2009 年由Google发布以来,以其简洁的语法、高效的并发模型以及强大的性能,迅速成为开发者们的新宠。Go语言不仅融合了传统编译…

icloud照片怎么恢复到相册?2个方法,轻松解决烦恼

在现代生活中,照片承载着我们的回忆和珍贵的时刻,而iCloud提供了便捷的云存储服务,让用户可以方便地备份和同步手机上的照片、视频等文件。 然而,有时候我们可能会不小心删除了在iCloud上的照片,或者想要将iCloud照片…

青岛大学物理科学学院郭向欣教授

男,1973年1月生,山东济宁人。中国科学院百人计划杰出海外人才和上海市浦江人才,山东省泰山学者特聘教授,青岛市创业创新领军人才。固态离子学理事。博士,教授,博士生导师。 研究工作一直聚焦金属锂二次电池…

Golang:Sirupsen/logrus是一个日志库

Sirupsen/logrus是一个日志库 文档 https://github.com/Sirupsen/logrus 安装 go get github.com/sirupsen/logrus代码示例 package mainimport ("github.com/sirupsen/logrus" )func main() {var log logrus.New()log.Trace("Something very low level.&…

使用B2M 算法批量将可执行文件转为灰度图像

参考论文 基于二进制文件的 C 语言编译器特征提取及识别 本实验使用 B2M 算法将可执行文件转为灰度图像,可执行文件转为灰度图的流程如图 4-3 所示。将 可执行文件每 8 位读取为一个无符号的的整型常量,一个可执行文件得到一个一维向量, …

基于51单片机多功能太阳能充电器设计

1 绪论1.1 本课题研究背景及现状 当代社会随着一些不可再生资源如煤炭,石油等日益减少,使得各国社会经济越来越受能源问题的约制,因此许多国家开始逐渐的实行“阳光计划”,开发洁净的能源如太阳能,用以成为本国经济发…

ANOVA方差分析是什么?优思学院教你如何正确使用

ANOVA(方差分析)是一种统计方法,用来研究三个或三个以上样本平均数的差异是否显著。它可以帮助研究者判断不同组间的均值是否存在统计学上的显著差异。简单来说,如果我们想比较多个不同处理或条件对某个变量的影响,ANO…

【网络层】网络攻击 ARP 欺骗

文章目录 ARP 欺骗原理实战体验 ARP 欺骗原理 ARP(地址解析协议)欺骗是一种网络攻击技术,它利用了ARP协议的工作机制来欺骗网络中的主机。ARP协议用于将IP地址转换为物理MAC地址,以便在局域网内部进行数据包的传输。ARP欺骗的基本…

微信小程序毕业设计-跑腿系统项目开发实战(附源码+演示视频+LW)

大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:微信小程序毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计…

可监测的到手价范围

在品牌进行电商价格监测时,对优惠信息范围的关注至关重要。优惠促销的形式多样,会直接影响到消费者最终的到手价。因此,为了更准确地计算到手价,品牌需要确保监测的促销信息尽可能全面。 不同的电商平台,其优惠形式可…

C# 实现腾讯云点播之媒体管理常用接口

目录 关于腾讯云点播媒体管理 开发前准备 范例运行环境 常用媒体管理API 删除媒体 禁播媒体 获取媒体详细信息 查询媒体 小结 关于腾讯云点播媒体管理 腾讯云点播(Video On Demand)服务基于多年技术积累与基础设施建设,为有音视频应…

TypeScript-interface接口类型

interface接口类型 在TS中使用interface接口来描述对象数据的类型,常用于给对象的属性和方法添加类型约束 ⚠️ 一旦注解接口类型之后对象的属性和方法类型都需要满足要求,属性不能多也不能少 interface Person {name: stringage: number }const p: P…

揭秘APP广告变现的高效秘诀:如何让你的APP更赚钱?

在数字化时代,APP已成为人们获取信息、娱乐休闲的重要平台。对于许多内容创作者来说,如何通过APP实现盈利,是一个亟待解决的问题。而APP广告变现项目,正是其中一种备受关注的盈利模式。那么,如何有效地利用APP广告变现…

分数裂项方法及技巧

裂差 知识点 1 1 1 b − a a b 1 a − 1 b \frac{b-a}{ab} \frac{1}{a} - \frac{1}{b} abb−a​a1​−b1​ 证明: b − a a b b a b − a a b 1 a − 1 b \begin{align*} \\ &\frac{b-a}{ab} \\ &\frac{b}{ab} - \frac{a}{ab} \\ &\frac{1}{a}…

Flutter 中的 RawGestureDetector 小部件:全面指南

Flutter 中的 RawGestureDetector 小部件:全面指南 在Flutter中,处理用户手势是构建交互式应用的关键部分。RawGestureDetector是一个强大的小部件,它允许开发者识别和响应各种手势,包括但不限于点击、滑动、缩放等。本文将为您提…

web安全渗透测试十大常规项(二):web渗透测试之XSS跨站脚本攻击

渗透测试之XSS跨站脚本攻击 XSS跨站脚本攻击 XSS跨站脚本攻击