JavaScript执行时

JavaScript在执行阶段,引擎解释执行字节码或机器码,逐行执行。

这个过程中,引擎会根据需要,动态地进行类型检查、优化等操作,以提高代码的执行效率。

因此JavaScript的编译和执行是同时进行的,而不是先编译完毕后再执行,编译过程将源代码转换为可执行的字节码或机器码,然后立即开始执行,这种动态编译和解释的方式,使得JavaScript能够实现即时编译(JIT)和动态类型检查等特性。

JavaScript是通过调用栈来执行代码的,调用栈用于存储函数调用时的信息,包括函数指针、参数和局部变量等。

在JavaScript编译时文章中提到过,编译时的代码生成阶段,会生成一个全局执行环境,到运行时阶段,执行最外层代码。

最外层的代码被视为一个匿名函数,放入调用栈中作为初始的执行上下文。

这个匿名函数代表了整个程序的执行环境,随着代码的执行,其他函数被调用时,它们的执行上下文也会被添加到调用栈中,生成一个函数执行环境,然后将该执行环境推入调用栈中,并在栈中分配空间以存储函数的局部变量和参数。

当函数执行完毕后,对应的执行上下文会被从调用栈中弹出,直到最外层的匿名函数执行完毕,整个程序的执行结束。

整个执行过程中,最外层的代码被视为最大的函数,其执行过程视为调用栈的开始和结束。

并且在执行过程中,调用栈会形成一个可观察的、动态的作用域链。

由于JavaScript是单线程的,为了避免代码阻塞采用了异步执行,该机制即事件循环(Event Loop)。

实际上,尽管JavaScript是单线程的,但参与JavaScript执行过程的线程至少有五个:JS引擎线程、事件触发线程、定时器触发线程、HTTP请求线程和web worker。

但永远只有JS引擎线程在执行JavaScript程序,其他线程只是协助。

JavaScript线程

JavaScript引擎线程就是引擎,也叫JavaScript内核,比如V8引擎,不仅参与JavaScript代码执行,还参与JavaScript代码的编译。

事件触发线程属于浏览器内核的进程,主要用于控制事件,当事件触发时,事件触发线程就会把该事件的处理函数推入任务队列,等待js引擎线程执行。

定时器触发线程,主要控制计时器和延时器,用于定时器的计时,计时完毕触发后将计时器函数推入任务队列,等待js引擎线程执行。

HTTP异步请求线程,通过XMLHttpRequest连接后,通过浏览器新开的一个线程,监控readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推入任务队列中。

任务执行

宏任务

宏任务分为同步任务和异步任务:

1、同步任务指的是JavaScript引擎主线程上按顺序执行的任务,只有前一个任务执行完毕后,才能执行后一个任务,形成一个执行栈(函数调用栈)。

2、异步任务指的是不直接进入JS引擎主线程,而是满足触发条件时,相关的线程将该异步任务推入任务队列,等待JavaScript引擎主线程上的任务执行完毕时读取执行的任务。

理解宏任务中同步任务和异步任务的执行顺序,就相当于理解了JS异步执行机制------事件循环。

事件循环

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么为什么JavaScript不能有多个线程呢?

这个原因主要和它的用途有关,作为直接和用户互动的语言来说,单线程是其必要特征,否则会带来很多复杂的同步问题。

而为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个子线程,但子线程完全受主线程控制,且不得操作DOM。

即便如此,这个新标准并没有改变JavaScript单线程的本质。

对于事件循环,可以理解其为三部分组成,分别是:

  1. 调用栈
  2. 异步任务
  3. 任务队列

任务队列就是以队列对事件任务进行管理,在js引擎主线程执行过程中:

1、首先执行宏任务中的同步任务,可以理解为此时生成了一个调用栈

2、调用栈的函数执行一些异步的API,开启对应的线程对其进行监控。

3、异步任务的事件会放在宿主环境中,当满足触发条件时,对应的线程则会把该事件的处理函数推进任务队列,等待主线程读取执行。

4、js引擎主线程上的任务执行完毕,读取任务队列中的事件,并将事件推进主线程,按任务队列顺序执行。

5、当js引擎主线程上的任务执行完毕后,再次读取任务队列中的事件任务,如此循环,就是事件循环的过程。

微任务

微任务的API主要有Promise、gennerator等,加入微任务后,宏任务和微任务的执行顺序:宏任务(同步任务)------微任务------宏任务(异步任务),因而当js引擎主线程执行完后,会检查任务队列中是否有微任务,如果有就优先执行微任务。

全局对象

全局对象是一个特殊对象,包含了在全局作用域中定义的所有变量、函数和其他对象。

根据运行环境,JavaScript中可以分为两种全局对象:运行在浏览器中的全局对象window和运行在Node.js环境中的全局对象node。

这里所说的全局对象,即浏览器中的全局对象。

当JavaScript代码开始执行时,会创建一个全局对象,并给全局对象定义一组初始属性:比如undefined、NaN、isNaN()、Date()、Math等。

全局对象的初始属性并不是保留字,但应该被当作保留字来对待。

在浏览器JavaScript中,在其表示的浏览器窗口中的所有JavaScript代码中,window对象充当了全局对象,这个全局对象有个属性引用其本身:var window = this

全局对象的内置对象、属性和方法

值属性

全局对象的值属性本身是简单值,这些值没有自己的属性和方法,常用的值属性如下:

NaN

当数字运算是无意义时结果为NaN,比如0除以0,但数字处于0时JavaScript会根据情况返回infinite或-infinite。

NaN的特殊之处在于它和任何值都不相等,包括其自身,因此无法通过a==NaN来判断变量a是否为NaN,应该使用a!=a来判断,因为只有当a为NaN,该表达式结果才为真。

Infinity

当数字运算的结果超出JavaScript的数字允许的范围时,会根据情况得出infinite或-infinite。

undefined

对于undefined需要注意的问题是,它是基础数据类型,且是全局对象的一个属性,并不是实例化的对象,因而它谈不上有没有原型的问题。

函数属性

全局函数可以直接调用,不需要在调用时指定所属对象,执行结束后会将结果直接返回给调用者,常用的函数属性如下:

parseInt和parseFloat

parseFloat(string),将参数string其转化为小数,若无法转化成数字则返回NaN。

parseInt(string,radix)接受两个参数,将参数string转化为十进制整数,radix是string的进制,默认为10。

encodeURI和decodeURI

encodeURI用于对整个URI(统一资源标识符)进行编码,它会将特殊字符(如空格、中文、标点符号等)转换为其对应的百分号编码(%XY,其中 XY 是字符的 ASCII 码的十六进制表示)。

但它不会对某些具有特殊含义的URL组件(如冒号、正斜杠等)进行编码,如下:

JavaScript 复制代码
    const uri = "https://example.com/测试 页面";

    const encodedURI = encodeURI(uri);

    console.log(encodedURI); // 输出:"https://example.com/%E6%B5%8B%E8%AF%95%20%E9%A1%B5%E9%9D%A2"

decodeURI就是解码使用encodeURI编码的统一资源标识符(URI)。

为什么要对URI进行编码呢,因为URL编码格式采用的是ASCLL码,即不能在url中包含任何非ASCLL字符。

但是encodeURI有个问题,由于它不会对某些特殊含义的URL组件进行编码,当用户输入含有"&、="等字符作为查询选项时,不对其编码会导致前后端对url的解析不正确。

JavaScript 复制代码
// 直接将用户输入拼接在url中,并对整个url进行编码

let url = 'https://www.google.com/search?q=中&文'

let encodeUrl = encodeURI(url)

console.log(encodeUrl)

let query = new URLSearchParams(encodeUrl.split("?")[1])

query.get("q") // "中"  解析不正确

console.log(query.get("q") )

decodeURIComponent和encodeURIComponent

encodeURIComponent函数用于对URI的单个组件(如查询字符串参数)进行编码,它会将所有特殊字符(包括具有特殊含义的 URL 组件,如冒号、正斜杠等)转换为百分号编码,比如:

JavaScript 复制代码
    const uriComponent = "测试=测试 页面";

    const encodedURIComponent = encodeURIComponent(uriComponent);

    console.log(encodedURIComponent); // 输出:"测试%3D%E6%B5%8B%E8%AF%95%20%E9%A1%B5%E9%9D%A2"

decodeURIComponent(): 这个函数用于对使用 encodeURIComponent() 编码的 URI 组件进行解码。它将百分号编码的字符转换回原始字符。

encodeURIComponent和encodeURI的区别在于,它用于对URL的组成部分进行个别编码,而不用对整个URL进行编码。

JavaScript 复制代码
// 对用户值输入采用decodeURIComponent进行编码

let url = `https://www.google.com/search?q=${encodeURIComponent('中&文')}`

let query = new URLSearchParams(url.split("?")[1])

query.get("q") // '中&文' 正确解析

console.log(query.get('q'))

内置对象

这里的对象指的是全局对象下的内置对象,包括:

基本对象

基本对象是定义或其他对象的基础,包括Object、Function、Boolean和Symbol

错误对象

错误对象有诸如URIEroor、TypeError、ReferenceError等,这些对象都是Error对象的派生对象。

数字和日期对象

用来表示数字、日期和执行数学计算的对象,需要知道的Number、Math、BigInt、Math和Date

字符串对象

这些对象用来表示字符串并支持操作字符串,需要知道的有String和RegExp

可索引的集合对象

即Array、类型数组以及类数组结构的对象,这里知道Array即可。

使用键的集合对象

这些集合在存储数据时会用到键,知道Map和Set即可,这两都可迭代,都支持按顺序迭代访问元素。

结构化数据对象

这些对象用来表示和操作结构化的缓冲区数据,或使用JSON编码的数据。

包括ArrayBuffer和JSON

控件抽象对象

控件抽象对象可以帮助构造代码,尤其是异步代码(如果不想使用深度嵌套的回调)

这部分的内容就该比较多了,之前提到的包装对象就是这部分的知识内容,应该做个提炼,比如: Object、Function、Boolean、Symbol、String、Number、BigInt、RegExp、Array、Map、Set、Math、JSON、Iterator、Promise等等。

web规范

JavaScript的web环境下接口对象众多,从mdn上看起来格外分散,因而这部分目录从规范上来学习。

web规范定义了接口对象的特性、行为和用法,并提供了具体的实现细节,通过学习规范可以深入了解接口对象。

这些规范基本是由相关组织(比如W3C或浏览器厂商)制定,并在JavaScript引擎中实现,规范化有助于确保在图通的浏览器或环境中,相同的操作和功能能够一致地工作。

这里接口是一种抽象的概念,从狭义的角度来说其实就是对象,从广义上来说它不仅仅只起到对象的作用,它更大的作用在于定义了对象之间的交互规则和方式,有助于提高代码的可复用性和扩展性。

这部分重要的是在应用上,也就是每个规范或接口在什么场景下使用。

比如迭代器和生成器。

迭代器是一个对象,该对象允许对它的值集合进行遍历,并保持任何必要的状态以便能跟踪到当前遍历的位置。

迭代器必须包含next方法,每次调用next都返回集合的下一个值。

JavaScript对for/in循环的功能进行扩展,可以用他来遍历可迭代对象,如果关键词in右侧的值是可迭代的,那么for/in循环会自动调用它的__iterator__方法来获得一个迭代器对象,for/in循环自己会处理StopIteration异常,而且处理过程对开发者实不可见的。

这大概就是JavaScript整体的架构体系,虽然很多东西没有深入,仅仅只是概略,但这就是架构的本质。

相关推荐
yuki_uix4 分钟前
AI辅助网页设计:从图片到代码的实践探索
前端
我想说一句4 分钟前
事件机制与委托:从冒泡捕获到高效编程的奇妙之旅
前端·javascript
陈随易5 分钟前
MoonBit助力前端开发,加密&性能两不误,斐波那契测试提高3-4倍
前端·后端·程序员
汤姆Tom10 分钟前
JavaScript reduce()函数详解
javascript
小飞悟12 分钟前
你以为 React 的事件很简单?错了,它暗藏玄机!
前端·javascript·面试
中微子18 分钟前
JavaScript 事件机制:捕获、冒泡与事件委托详解
前端·javascript
Whoisshutiao35 分钟前
网安-XSS-pikachu
前端·安全·网络安全
惊鸿28740 分钟前
Taro3+小程序Canvas动态生成海报和二维码分享到朋友圈
前端
蓝翔认证10级掘手1 小时前
🤯 家人们谁懂啊!我的摸鱼脚本它...它成精了!🚀
javascript
做梦都在学习前端1 小时前
发布一个monaco-editor 汉化包
前端·npm·vite