JavaScript高级系列(三) - JavaScript的运行过程

一. 全局代码的执行过程

1.1. ECMA的版本说明

在ECMA早期的版本(ECMAScript3),代码的执行流程的术语和ECMAScript5以及之后的术语会有所区别:
目前网上大多数流行的说法都是基于ECMAScript3版本的解析,
并且在面试问到的大多数都是ECMAScript3的版本内容;
ECMAScript3终将过去, ECMAScript5必然会成为主流,所以最好也理解ECMAScript5甚至包括ECMAScript6以及更好版本的内容;

事实上在TC39( ECMAScript5 )的最新描述中,和ECMAScript5之后的版本又出现了一定的差异;那么按照如下顺序学习:
a. 通过ECMAScript3中的概念学习JavaScript执行原理、作用域、作用域链、闭包等概念;
b. 通过ECMAScript5中的概念学习块级作用域、let、const等概念;

它们只是在对某些概念上的描述不太一样,在整体思路上都是一致的。

1.2. 全局代码初始化

假如我们有下面一段代码,它在JavaScript中是如何被执行的呢?

var name = "wulin"
function foo() {
  var name = "foo"
  console.log(name)
}

var num1 = 20
var num2 = 30
var result = num1 + num2

foo()

1.2.1. 初始化全局对象

js引擎会在执行代码之前,会在堆内存中创建一个全局对象:

Global Object(GO) 该对象 所有的作用域(scope)都可以访问;
里面会包含Date、Array、String、Number、setTimeout、setInterval等等;
其中还有一个window属性指向自己;
在这里插入图片描述
在这里插入图片描述

1.2.2. 执行上下文(EC)

在 JavaScript 引擎内部,有一个执行上下文栈(Execution Context Stack,简称 ECS),它是用于执行代码的调用栈,存储着当前正在执行的执行上下文。
当 JavaScript 引擎开始执行代码时,它会先创建一个全局执行上下文(Global Execution Context,简称 GEC),并将其推入 ECS 中,表示当前正在执行全局的代码块。

GEC 包含了两部分内容:
第一部分是在代码执行前,JavaScript 引擎会扫描全局作用域下的变量和函数,将它们添加到全局对象(Global Object)中,并分配内存空间,但是并不会赋值。这个过程称为变量的作用域提升(hoisting)。
第二部分是在代码执行中,JavaScript 引擎会对变量进行赋值,并执行其他的函数。

1.3. 全局代码的执行

1.3.1. VO和GO的理解

每一个执行上下文会关联一个VO(Variable Object,变量对象),变量和函数声明会被添加到这个VO对象中。
变量对象是一个抽象的概念,在 JavaScript 引擎内部,它被实现为一个与当前执行上下文相关联的对象。

      在全局执行上下文中,变量对象是全局对象(Global Object);
      在函数执行上下文中,变量对象是一个称为活动对象(Activation Object)的对象。

当全局代码被执行的时候,VO就是GO对象了

当全局代码被执行时,变量对象(Variable Object,简称 VO)就是全局对象(Global Object,简称 GO)了。
全局对象是在 JavaScript 引擎内部创建的一个唯一对象,它在全局作用域下被访问,并且可以存储全局变量、函数、内置对象和浏览器提供的 API 等等。

1.3.2. 全局代码执行过程(执行前)

GO 对象在执行全局代码之前就已经被创建,并且包含了一些内置对象和方法,例如 Object、Array、String、Number、Math、JSON、setTimeout、setInterval 等等。

当全局执行上下文被创建时,变量对象 VO 会被设置为 GO 对象,并且全局作用域下的变量和函数会被添加到变量对象中。

1.3.3. 全局代码执行过程(执行后)

代码执行后就会给变量进行复制操作:
在这里插入图片描述

二. 函数代码的执行过程

2.1. 函数代码的执行过程

2.1.1. 函数执行的概念描述

在 JavaScript 中,当函数被调用时,会创建一个函数执行上下文(Functional Execution Context,简称 FEC),并将其压入执行上下文栈(Execution Context Stack,简称 ECS)中,表示当前正在执行该函数的代码块。

函数执行上下文关联的变量对象(Variable Object,简称 VO)是一个称为活动对象(Activation Object,简称 AO)的对象。

在进入函数执行上下文时,JavaScript 引擎会创建一个 AO 对象,并将其关联到当前的执行上下文中。AO 对象是一个包含函数的参数、函数声明和变量声明的列表。

它的初始值使用函数的参数列表作为参数并进行初始化。这个 AO 对象会作为执行上下文的变量对象(VO)来存储变量的初始化。

当函数执行完毕后,其执行上下文会从 ECS 中弹出,AO 对象会被垃圾回收,所以不能在函数执行完毕后再访问该函数的 AO 对象

2.1.2. 函数代码执行过程(执行前)

在这里插入图片描述

2.1.3. 函数代码执行过程(执行后)

2.2. 作用域、作用域链的理解

在 JavaScript 中,当进入一个执行上下文时,执行上下文也会关联一个作用域链(Scope Chain)。

作用域链是一个对象列表,用于变量标识符的查找和求值。当进入一个执行上下文时,这个作用域链被创建,并且根据代码类型,添加一系列的对象;

作用域链是由当前执行上下文的变量对象(VO)和所有外部环境的作用域链组成的。
在这里插入图片描述

2.2.2. 作用域、作用域链面试题

接下来我们可以利用变量的提升过程,做一些面试题:
下面展示一些 内联代码片

// 1.面试题一:
var n = 100
function foo() { 
 n = 200
 }
 foo()
 console.log(n)
 // 2.面试题二:
 var n = 100
 function foo() {  
 console.log(n)  
 var n = 200 
  console.log(n)
  }
  foo()
  // 3.面试题三:
  var n = 100
  function foo1() {  
  console.log(n)
  }
  function foo2() {  
  var n = 200  
  console.log(n)  foo1()
  }
  foo2()
  // 4.面试题四:
  var n = 100
  function foo() {  
  console.log(n)  
  return  
  var n = 200  }
  foo()
// 5.在开发中可能会出现这样错误的写法
function foo() {  
message = "Hello World"
}
foo()
console.log(message)
// 6.面试题五:
function foo() {  
var a = b = 100  
}
foo()
console.log(b)

2.3. 闭包和闭包的内存泄漏

2.3.1. 闭包的概念定义

这里先来看一下闭包的定义,分成两个:在计算机科学中和在JavaScript中。

在计算机科学中对闭包的定义(维基百科):

闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures);

是在支持 头等函数 的编程语言中,实现词法绑定的一种技术;

闭包在实现上是一个结构体,它存储了一个函数和一个关联的环境(相当于一个符号查找表);

闭包跟函数最大的区别在于,当捕捉闭包的时候,它的 自由变量 会在捕捉时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行;

闭包的概念出现于60年代,最早实现闭包的程序是 Scheme,那么我们就可以理解为什么JavaScript中有闭包:

因为JavaScript中有大量的设计是来源于Scheme的;
MDN对JavaScript闭包的解释:

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure);
也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域;
在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来;
那么我的理解和总结:

一个普通的函数function,如果它可以访问外层作用于的自由变量,那么这个函数就是一个闭包;
从广义的角度来说:JavaScript中的函数都是闭包;
从狭义的角度来说:JavaScript中一个函数,如果访问了外层作用于的变量,那么它是一个闭包.

2.3.2. 闭包的形成过程

如果一个函数需要使用到外面作用域的变量,那么必须将所有的变量全部传入进去。

var name = "wulin"
var age = 21
var height = 1.7
var address = "西安"
var intro = "我想看世界!"
function foo(name, age, height, address,
intro, num1, num2) { 
var message = "Hello World"         console.log(message, name, age, height, address, intro)  

function bar() {   
 console.log(name) 
 }  
 bar()
 }
 foo(name, age, height, address, intro, 20, 30)

有了闭包之后

function createAdder(count) {
  function adder(num) {
    return count + num
  }

  return adder
}

var adder5 = createAdder(5)
adder5(100)
adder5(55)
adder5(12)

var adder8 = createAdder(8)
adder8(22)
adder8(35)
adder8(7)

console.log(adder5(24))
console.log(adder8(30))

2.3.4. 闭包的内存泄漏

使用闭包可能会导致内存泄漏:因为闭包会引用外部函数的变量对象,如果这个闭包被长期保存,那么外部函数的变量对象就会一直存在内存中,无法被垃圾回收。
因此,在使用闭包时,需要注意内存的管理。那么我们为什么经常会说闭包是有内存泄露的呢?

在上面的案例中,如果后续我们不再使用add10函数了,那么该函数对象应该要被销毁掉,并且其引用着的父作用域AO也应该被销毁掉;

但是目前因为在全局作用域下add10变量对0xb00的函数对象有引用,而0xb00的作用域中AO(0x200)有引用,所以最终会造成这些内存都是无法被释放的;

所以我们经常说的闭包会造成内存泄露,其实就是刚才的引用链中的所有对象都是无法释放的;

那么,怎么解决这个问题呢?
因为当将add8设置为null时,就不再对函数对象0xb00有引用,那么对应的AO对象0x200也就不可达了;

在GC的下一次检测中,它们就会被销毁掉;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实现el-table合并列

效果图如下 <el-table :data"atlasDataList" style"width: 100%" :span-method"spanMethod"><el-table-column prop"stationName" label"" width"180" /><el-table-column prop"atlasNumbe…

网络编程—DAY5

select实现的TCP并发服务器 #include <myhead.h> #define SER_PORT 8888 #define SER_IP "192.168.117.96"int main(int argc, const char *argv[]) {int sfd -1;sfd socket(AF_INET,SOCK_STREAM,0);if(sfd -1){perror("socket");return -1;}prin…

LVGL:拓展部件——键盘 lv_keyboard

一、概述 此控件特点&#xff1a; 特殊Button矩阵&#xff1a;lv_keyboard 本质上是一个经过定制的按钮矩阵控件。每个按钮都可以独立触发事件或响应。预定义的键映射&#xff1a;lv_keyboard 自带了一套预设的按键布局和对应的字符映射表&#xff0c;开发者可以根据需要选择…

【Vue3】Vue3中的编程式路由导航 重点!!!

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

大数据架构技术选型

OLAP数据库选型对比&#xff1a; AnalyticDB(阿里&#xff09;、Hologres&#xff08;阿里&#xff09;、Doris、StarRocks、ClickHouse、Hbase AnalyticDB技术架构 db是融合数据库、大数据技术于一体的云原生企业级数据仓库服务、支持高吞吐的数据实时增删改查低延时的实时分…

电脑数据安全新利器:自动备份文件的重要性与实用方案

一、数据安全的守护神&#xff1a;自动备份文件的重要性 在数字化时代&#xff0c;电脑中的文件承载着我们的工作成果、个人回忆以及众多重要信息。然而&#xff0c;数据丢失的风险无处不在&#xff0c;无论是硬件故障、软件崩溃&#xff0c;还是恶意软件的攻击&#xff0c;都…

Java基础-IO流

文章目录 1.文件1.基本介绍2.常用的文件操作1.创建文件的相关构造器和方法代码实例结果 2.获取文件相关信息代码实例结果 3.目录的删除和文件删除代码实例 2.IO流原理及分类IO流原理IO流分类 3.FileInputStream1.类图2.代码实例3.结果 4.FileOutputStream1.类图2.案例代码实例 …

(C语言)memcpy函数详解与模拟实现

目录 1. memcpy函数详解 情况一&#xff1a; 情况二&#xff1a; 情况三&#xff1a; 情况四&#xff1a; 情况五&#xff1a; 2. memcpy模拟实现 2.1 重叠情况的讨论&#xff1a; 2.2 memcpy函数超出我们的预期 1. memcpy函数详解 头文件<string.h> 这个函数和…

腾讯云服务器多少钱一个月?5元1个月,这价格没谁了

2024腾讯云服务器多少钱一个月&#xff1f;5元1个月起&#xff0c;腾讯云轻量服务器4核16G12M带宽32元1个月、96元3个月&#xff0c;8核32G22M配置115元一个月、345元3个月&#xff0c;腾讯云轻量应用服务器61元一年折合5元一个月、4核8G12M配置646元15个月、2核4G5M服务器165元…

C++ Qt开发:QUdpSocket网络通信组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QUdpSocket组件实现基于UDP的网络通信…

网站引用图片但它域名被墙了或者它有防盗链,我们想引用但又不能显示,本文附详细的解决方案非常简单!

最好的办法就是直接读取图片文件&#xff0c;用到php中一个常用的函数file_get_contents(图片地址)&#xff0c;意思是读取远程的一张图片&#xff0c;在输出就完事。非常简单&#xff5e;话不多说&#xff0c;直接上代码 <?php header("Content-type: image/jpeg&quo…

“小白也能玩转!ES集群搭建零报错全攻略,轻松上手指南!”

&#x1f310; 全新的“Open开袁”官方网站现已上线&#xff01;https://open-yuan.com&#xff0c;汇聚编程资源、深度技术文章及开源项目地址&#xff0c;助您探索技术新境界。 &#x1f4f1; 关注微信公众号“全栈小袁”&#xff0c;随时掌握最新项目动态和技术分享&#xf…

2024地方门户源码运营版带圈子动态+加即时通讯(PC电脑端+WAP移动端+H5微信端+微信小程序+APP客户端)

2024地方门户源码运营版带圈子动态加即时通讯&#xff08;PC电脑端WAP移动端H5微信端微信小程序APP客户端&#xff09; 源码介绍&#xff1a; 包含5个端 PC电脑端WAP移动端H5微信端微信小程序APP客户端 功能介绍&#xff1a; 包含功能&#xff1a;信息资讯、二手信息、房产…

docker入门(四)—— docker常用命令详解

docker 常用命令 基本命令 # 查看 docker 版本 docker version # 查看一些 docker 的详细信息 docker info 帮助命令&#xff08;–help&#xff09;&#xff0c;linux必须要会看帮助文档 docker --help[rootiZbp15293q8kgzhur7n6kvZ /]# docker --helpUsage: docker [OPTI…

Grok ai——很牛叉的ai工具Grok-1大模型

Grok Grok 是一款仿照《银河系漫游指南》&#xff08;Hitchhikers Guide to the Galaxy&#xff09;设计的人工智能。它可以回答几乎任何问题&#xff0c;更难的是&#xff0c;它甚至可以建议你问什么问题&#xff01; Grok 是一个仿照《银河系漫游指南》设计的人工智能&#x…

数据结构:详解【顺序表】的实现

1. 顺序表的定义 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。动态顺序表与数组的本质区别是——根据需要动态的开辟空间大小。 2. 顺序表的功能 动态顺序表的功能一般有如下几个&#xff1a; 初始化顺序表打印顺序…

计算机组成原理-3-系统总线

3. 系统总线 文章目录 3. 系统总线3.1 总线的基本概念3.2 总线的分类3.3 总线特性及性能指标3.4 总线结构3.5 总线控制3.5.1 总线判优控制3.5.2 总线通信控制 本笔记参考哈工大刘宏伟老师的MOOC《计算机组成原理&#xff08;上&#xff09;_哈尔滨工业大学》、《计算机组成原理…

spring suite搭建springboot操作

一、前言 有时候久了没开新项目了&#xff0c;重新开发一个新项目&#xff0c;搭建springboot的过程都有点淡忘了&#xff0c;所有温故知新。 二、搭建步骤 从0开始搭建springboot 1&#xff0e;创建work空间。步骤FileNewJava Working Set。 2.选择Java Working Set。 3.自…

微信小程序接口请求出错:request:fail url not in domain list:xxxxx

一、微信小程序后台和开发者工具配的不一样导致了这个错误 先说结论&#xff1a; 开发者工具配置了https://www.xxx.cn/prod-api/ 微信后台配置了 https://www.xxx.cn 一、最开始 开发者工具配置了https://www.xxx.cn:7500 微信后台配置了 https://www.xxx.cn 报错:reques…

OPTEE v3.20.0 FVP环境搭建

目录 一、前提条件 二、下载fvp代码 三、下载工具链 四、下载Foundation_Platform FVP平台 五、编译及运行 一、前提条件 1、安装如下的依赖工具 sudo apt-get install android-tools-adb android-tools-fastboot autoconf \ automake bc bison build-essential ccache c…