- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
编写一段JavaScript代码,它是如何执行的呢?简单来说,JS引擎在执行JavaScript代码的过程中需要先解析再执行。那么在解析阶段JS引擎又会进行哪些操作,接下来就一起来了解一下JavaScript在执行过程中的详细过程,包括执行上下文、GO、AO、VO和VE等概念的理解。
首先,JS引擎会在执行代码之前,也就是解析代码时,会在我们的堆内存创建一个全局对象:Global Object(简称GO),观察以下代码,在全局中定义了几个变量:
示例代码:
var name = 'curry'
var message = 'I am a coder'
var num = 30
JS引擎内部在解析以上代码时,会创建一个全局对象(伪代码如下):
var GlobalObject = {
Math: '类',
Date: '类',
String: '类',
setTimeout: '函数',
setInterval: '函数',
window: GlobalObject,
...
name: undefined,
message: undefined,
num: undefined
}
了解了什么是全局对象后,下面就来聊聊代码具体执行的地方。JS引擎为了执行代码,引擎内部会有一个执行上下文栈(Execution Context Stack,简称ECS),它是用来执行代码的调用栈。
(1)ECS如何执行?先执行谁呢?
(2)那么全局执行上下文(GEC)包含那些内容呢?
**第一部分:**执行代码前。
在转成抽象语法树之前,会将全局定义的变量、函数等加入到Global Object中,也就是上面初始化全局对象的过程;
但是并不会真正赋值(表现为undefined),所以这个过程也称之为变量的作用域提升(hoisting);
**第二部分:**代码执行。
对变量进行赋值,或者执行其它函数等;
下面就通过一幅图,来看看GEC被放入ECS后的表现形式:
接下来,将全局代码复杂化一点,再来看看调用栈调用全局执行上下文(GEC)的过程。
实例代码:
var name = 'curry'
console.log(message)
var message = 'I am a coder'
function foo() {
var name = 'foo'
console.log(name)
}
var num1 = 30
var num2 = 20
var result = num1 + num2
foo()
调用栈调用过程:
1.初始化全局对象。
这里需要注意的是函数存放的是地址,会指向函数对象,与普通变量有所不同;
从上往下解析JS代码,当解析到foo函数时,因为foo不是普通变量,并不会赋为undefined,JS引擎会在堆内存中开辟一块空间存放foo函数,在全局对象中引用其地址;
这个开辟的函数存储空间最主要存放了该函数的父级作用域和函数的执行体代码块;
4.从上往下开始执行全局代码,依次对GO对象中的全局变量进行赋值。
当执行var name = 'curry'
时,就从VO(对应的就是GO)中找到name属性赋值为curry;
接下来执行console.log(message)
,就从VO中找到message,注意此时的message还为undefined,因为message真正赋值在下一行代码,所以就直接打印undefined(也就是我们经常说的变量作用域提升);
后面就依次进行赋值,执行到var result = num1 + num2
,也是从VO中找到num1和num2两个属性的值进行相加,然后赋值给result,result最终就为50;
最后执行到foo()
,也就是需要去执行foo函数了,这里的操作是比较特殊的,涉及到函数执行上下文,下面来详细了解;
在执行全局代码遇到函数如何执行呢?
在执行的过程中遇到函数,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC),并且加入到执行上下文栈(ECS)中。
函数执行上下文(FEC)包含三部分内容:
AO:在解析函数时,会创建一个Activation Objec(AO);
作用域链:由函数VO和父级VO组成,查找是一层层往外层查找;
this指向:this绑定的值,在函数执行时确定;
其实全局执行上下文(GEC)也有自己的作用域链和this指向,只是它对应的作用域链就是自己本身,而this指向为window。
继续来看上面的代码执行,当执行到foo()
时:
console.log(name)
时,就会去foo的VO(对应的就是foo函数的AO)中找到name属性值并打印;上文中提到了很多次VO,那么VO到底是什么呢?下面从ECMA新旧版本规范中来谈谈VO。
在早期ECMA的版本规范中:每一个执行上下文会被关联到一个变量环境(Variable Object,简称VO),在源代码中的变量和函数声明会被作为属性添加到VO中。对应函数来说,参数也会被添加到VO中。
在最新ECMA的版本规范中:每一个执行上下文会关联到一个变量环境(Variable Environment,简称VE),在执行代码中变量和函数的声明会作为**环境记录(Environment Record)**添加到变量环境中。对于函数来说,参数也会被作为环境记录添加到变量环境中。
了解了上面相关的概念和调用流程之后,就来看一下存在函数嵌套调用的代码是如何执行的,以及执行过程中的一些细节,以下面代码为例:
var message = 'global'
function foo(m) {
var message = 'foo'
console.log(m)
function bar() {
console.log(message)
}
bar()
}
foo(30)
初始化全局对象(GO),执行全局代码前创建GEC,并将GO关联到VO,然后将GEC加入ECS中:
foo函数存储空间中指定的父级作用域为全局对象;
开始执行全局代码,从上往下依次给全局属性赋值:
给message属性赋值为global;
执行到foo函数调用,准备执行foo函数前,创建foo函数的AO:
bar函数存储空间中指定父级作用域为foo函数的AO;
创建foo函数的FEC,并加入到ECS中,然后开始执行foo函数体内的代码:
根据foo函数调用的传参,给形参m赋值为30,接着给message属性赋值为foo;
所以,m打印结果为30;
执行到bar函数调用,准备执行bar函数前,创建bar函数的AO:
bar函数中没有定义属性和声明函数,以空对象表示;
创建bar函数的FEC,并加入到ECS中,然后开始执行bar函数体内的代码:
执行console.log(message)
,会先去bar函数自己的VO中找message,没有找到就往上层作用域的VO中找;
这里bar函数的父级作用域为foo函数,所以找到foo函数VO中的message为foo,打印结果为foo;
名词 | 解释 |
---|---|
ECS | 执行上下文栈(Execution Context Stack),也可称为调用栈,以栈的形式调用创建的执行上下文 |
GEC | 全局执行上下文(Global Execution Context),在执行全局代码前创建 |
FEC | 函数执行上下文(Functional Execution Context),在执行函数前创建 |
VO | Variable Object,早期ECMA规范中的变量环境,对应Object |
VE | Variable Environment,最新ECMA规范中的变量环境,对应环境记录 |
GO | 全局对象(Global Object),解析全局代码时创建,GEC中关联的VO就是GO |
AO | 函数对象(Activation Object),解析函数体代码时创建,FEC中关联的VO就是AO |
我有一个 html 格式的表单: 我需要得到 JavaScript在value input 字段执行,但只能通过表单的 submit .原因是页面是一个模板所以我不控制它(不能有
我管理的论坛是托管软件,因此我无法访问源代码,我只能向页面添加 JavaScript 来实现我需要完成的任务。 我正在尝试用超链接替换所有页面上某些文本关键字的第一个实例。我还根据国家/地区代码对这些
我正在使用 JS 打开新页面并将 HTML 代码写入其中,但是当我尝试使用 document.write() 在新页面中编写 JS 时功能不起作用。显然,一旦看到 ,主 JS 就会关闭。用于即将打开的
提问不是为了解决问题,提问是为了更好地理解系统 专家!我知道每当你将 javascript 代码输入 javascript 引擎时,它会立即由 javascript 引擎执行。由于没有看过Engi
我在一个文件夹中有两个 javascript 文件。我想将一个变量的 javascript 文件传递到另一个。我应该使用什么程序? 最佳答案 window.postMessage用于跨文档消息。使
我有一个练习,我需要输入两个输入并检查它们是否都等于一个。 如果是 console.log 正则 console.log false 我试过这样的事情: function isPositive(fir
我正在做一个Web应用程序,计划允许其他网站(客户端)在其页面上嵌入以下javascript: 我的网络应用程序位于 http://example.org 。 我不能假设客户端网站的页面有 JQue
目前我正在使用三个外部 JS 文件。 我喜欢将所有三个 JS 文件合而为一。 尽一切可能。我创建 aio.js 并在 aio.js 中 src="https://code.jquery.com/
我有例如像这样的数组: var myArray = []; var item1 = { start: '08:00', end: '09:30' } var item2 = {
所以我正在制作一个 Chrome 扩展,它使用我制作的一些 TamperMonkey 脚本。我想要一个“主”javascript 文件,您可以在其中包含并执行其他脚本。我很擅长使用以下行将其他 jav
我有 A、B html 和 A、B javascript 文件。 并且,如何将 A JavaScript 中使用的全局变量直接移动到 B JavaScript 中? 示例 JavaScript) va
我需要将以下整个代码放入名为 activate.js 的 JavaScript 中。你能告诉我怎么做吗? var int = new int({ seconds: 30, mark
我已经为我的 .net Web 应用程序创建了母版页 EXAMPLE1.Master。他们的 I 将值存储在 JavaScript 变量中。我想在另一个 JS 文件中检索该变量。 示例1.大师:-
是否有任何库可以用来转换这样的代码: function () { var a = 1; } 像这样的代码: function () { var a = 1; } 在我的浏览器中。因为我在 Gi
我收到语法缺失 ) 错误 $(document).ready(function changeText() { var p = document.getElementById('bidp
我正在制作进度条。它有一个标签。我想调整某个脚本完成的标签。在找到可能的解决方案的一些答案后,我想出了以下脚本。第一个启动并按预期工作。然而,第二个却没有。它出什么问题了?代码如下: HTML:
这里有一个很简单的问题,我简单的头脑无法回答:为什么我在外部库中加载时,下面的匿名和onload函数没有运行?我错过了一些非常非常基本的东西。 Library.js 只有一行:console.log(
我知道 javascript 是一种客户端语言,但如果实际代码中嵌入的 javascript 代码以某种方式与在控制台上运行的代码不同,我会尝试找到答案。让我用一个例子来解释它: 我想创建一个像 Mi
我如何将这个内联 javascript 更改为 Unobtrusive JavaScript? 谢谢! 感谢您的回答,但它不起作用。我的代码是: PHP js文件 document.getElem
我正在寻找将简单的 JavaScript 对象“转储”到动态生成的 JavaScript 源代码中的最优雅的方法。 目的:假设我们有 node.js 服务器生成 HTML。我们在服务器端有一个对象x。
我是一名优秀的程序员,十分优秀!