当前位置: 首页 > 科技观察

JavaScript的抽象概念,简单而不简单,简单而不简单!

时间:2023-03-13 04:24:03 科技观察

本文转载自微信公众号《小鹿动画学习编程》,作者小鹿。转载本文请联系小鹿动漫学习编程公众号。想必大家看到本期的标题时也是很疑惑的。为什么简单而不简单,简单而不简单的执行上下文?先说明一下,对于javaScript的一些抽象概念,我们可以把它弄得很复杂,也可以把它弄得很简单,可以把它弄得很复杂,也可以把它弄得很简单。嗯~最近重温了这些抽象的概念,发现之前有些概念没有很好的融合,所以就把这些比较抽象难懂的概念写成几篇文章。我个人认为,为了更好地理解抽象的概念,我们不得不借助视觉图像来减少作者和读者之间的理解和理解错误。所以,不用着急,本文小鹿会通过更多的图片,对这些抽象的概念进行深入浅出的讲解。一、执行上下文1.1JavaScript引擎说到执行上下文,就不得不先说说JavaScript引擎。什么是JavaScript引擎?考虑到本文没有专门写JavaScript引擎,大家可以自行google。说白了,JavaScript引擎就是用来“解释”、“编译”和“执行”JavaScript代码的。毕竟开发者写的JS代码,只能被开发者识别,交给电脑。由于计算机只识别二进制,因此需要进行一系列的解释和转换才能理解和执行这些JavaScript代码。1.2执行栈(Executionstack)小鹿注:JavaScript代码的执行“顺序”是有保证的。JavaScript引擎既然可以执行JS代码,那么它是按照什么顺序执行的,如何保证这些顺序不被打乱。先看一段简单的代码:varfoo2=function(){console.log('foo2');}varfoo1=function(){console.log('foo1');foo2()console.log('foo3')}foo1();//Output:"foo1foo2foo3"通过以上代码片段的执行,输出序列为'foo1foo2foo3'。代码执行时,先执行foo1()函数,先输出'foo1'。当遇到foo2()函数的执行命令时,将执行权交给foo2,执行foo2函数体,输出'foo2'。foo2执行完成后,将执行权交还给foo1函数,最后输出'foo3'。我们可以找出上面代码执行的规律。先执行的函数最后退出,后执行的函数先执行。这个执行顺序不就是“栈”的“先进后出”``“后进先出”结构吗。JavaScript引擎将其执行结构称为“执行栈”,用于保证JavaScript代码的顺序。1.3执行上下文(ExceptionContext)小鹿注:要执行的代码是“模块化”的——执行上下文的分类。什么是执行上下文?虽然我们很难直接理解“执行上下文”的含义,但是很容易理解它具体代表什么。接下来,我将把“执行上下文”这个抽象概念具体化。上面我们已经说明了JavaScript引擎是通过执行栈来保证代码的执行顺序的,但是执行过程中需要涉及到范围定义(scope)、变量闭包等一些复杂的情况。我们需要JavaScript引擎引入一种机制来解决这些看似复杂的问题,于是“执行上下文”的概念应运而生。但是,执行上下文是什么?这不得不让我想起组件的模块化开发。之前的web应用代码从上到下写了上千行代码,难以阅读和维护,所以才有了后来的模块。发展。每个模块都有自己的功能,有自己的局部变量和样式。我们可以理解为,为了更好地解释和执行代码,JavaScript引擎引入了一个类似于组件模块“执行上下文”的概念来管理运行时代码的复杂性。2.执行上下文的分类在上面,我们将抽象的“执行上下文”做成类似于“模块”的具体概念,以便于理解。当然,执行上下文有不同的分类,即所谓的“模块”。这里我们只展开两种,“全局执行上下文”和“局部执行上下文”。2.1全局执行上下文(GlobalExceptionContext)全局上下文这个“模块”由“全局对象”和“this”两部分组成。下图是全局执行上下文的最基本形式。包含一个window对象,和一个this变量,this变量指向window对象,如最右边的打印结果所示。从这里我们可以看出,执行上下文可以理解为内存中“对象和变量”集合的一个模块(或片段),这也是为什么我们可以把它看成一个“模块”(除此之外还有其他功能)小鹿注:为了便于理解,定义是自己总结的。如有不足,请指出~2.2局部执行上下文局部执行上下文与全局执行上下文类似,但不完全相同。在函数本地执行上下文中,有两点需要注意:函数传入的参数将作为本地执行上下文的变量存储。本地上下文有一个arguments参数对象(引用)。localexecutioncontext的内容将在以下两个阶段详细介绍。3.执行上下文的两个阶段无论是全局执行上下文还是局部执行上下文,都会经历两个阶段,即“创建”和“执行”。我们有一段代码如下:varname="LittleDeer";varage=23;functiongetInfo(){return{name:name,age:age};}3.1创建阶段(Creation)创建阶段要做的事情阶段如下:在堆内存中创建浏览器环境下的全局对象(globalobject)——浏览器环境是windows,Node环境是Global让这个变量指向这个全局对象设置“变量和”的内存空间functions”在当前执行上下文中将声明的变量添加到内存中(同时挂在全局对象上,为变量赋值undefined,函数以字符串形式存储小鹿)创建舞台全局对象的状态。JavaScript引擎在执行代码之前,首先会在堆内存中创建一个全局执行上下文,生成一个全局对象(globalobject),然后让this变量指向this变量。JavaScript发现了代码中声明的name和age这两个变量,然后在全局执行上下文中申请内存空间,将变量存储在内存空间中,然后将undefined赋值给变量,函数存储在内存中字符串的形式。小鹿注:在创建阶段为变量声明指定默认值(undefined)的过程称为“变量提升”。3.2执行阶段(Execution)全局执行上下文创建后,开始从创建状态(Creation)转变为执行状态(Execution)。JavaScript引擎开始逐行运行和执行代码,并为在创建阶段放入内存的变量赋值。小鹿注:左(1)是执行的代码,左(2)是执行阶段完成后执行上下文在内存中的状态,右(1)是全局对象在执行阶段的状态。本地执行上下文和全局执行上下文的创建和执行过程是完全一样的。但是全局执行上下文是创建一次的,而函数局部执行上下文是随着函数的每次调用创建一个局部执行上下文。还是上面的例子,执行结果如下:varname="LittleDeer";varage=23;functiongetInfo(name){console.log(name);return{name:name,age:age};}getInfo(name);functionlocalcontext执行状态如下:小鹿注:由于函数中没有定义新的变量,所以这里没有变量提升。我们了解什么是函数局部上下文。当函数局部上下文执行时,会执行弹出操作,执行权会交给父执行上下文(可能是局部执行上下文,也可能是全局执行上下文)。上面的getInfo函数执行状态如下图所示。此时函数执行完毕,弹出并销毁本地执行上下文,将执行权交给全局执行上下文继续执行其他代码。由于JavaScript是单线程的,一次只能执行一个任务。为了方便大家理解,左图(3)是执行栈的调用状态。当然,我们也可以发现,左(2)图以嵌套的方式模拟了执行栈的运行,每一个嵌套选项都是栈中一个新的执行上下文。4.总结执行上下文是JavaScript中一个非常重要的概念。在接下来的几篇进阶文章中,scope、scope、closure、this等概念都会和本文的内容联系起来。