手机版

向我学习javascript的执行上下文

时间:2021-09-22 来源:互联网 编辑:宝哥软件园 浏览:

在本文中,我将深入研究JavaScript最基础的部分,——执行上下文。读完这篇文章,你应该知道解释器做了什么,为什么函数和变量可以在声明之前使用,以及它们的值是如何确定的。

1.执行环境或执行上下文。

每当控制器到达ECMAScript可执行代码时,控制器就进入一个执行上下文(多么高的概念)。

在javascript中,EC分为三种类型:

全局级代码这是默认的代码运行环境。一旦代码被加载,引擎首先进入这个环境。函数级代码当函数被执行时,函数体中的代码被运行。评估代码在评估函数中运行的代码。EC分两个阶段建立:进入执行上下文(创建阶段)和执行阶段(激活/执行代码)。

1)进入上下文阶段:在调用函数时发生,但在执行特定代码之前(例如,在函数参数具体化之前),创建一个Scope Chain来创建变量、函数和参数。求“本”的价值。2)代码执行阶段:变量赋值函数是指解释/执行其他代码。我们可以把EC看作一个对象。

EC={ VO:{/*所有父执行上下文中的参数对象、参数、内部变量和函数声明*/}、this: {}、scope: {/* vo和VO */}}现在让我们看一个包含全局和函数上下文的代码示例:

举个简单的例子,我们有一个被紫色边框包围的全局上下文和三个被绿色、蓝色和橙色包围的不同函数上下文。任何其他上下文都只能访问全局上下文(的变量)。

您可以有任意数量的函数上下文。每次调用一个函数来创建一个新的上下文时,都会创建一个私有范围。在函数内部声明的任何变量都不能在当前函数范围之外直接访问。在上面的示例中,函数可以访问当前上下文之外的变量声明,但不能访问外部上下文中的内部变量/函数声明。为什么会这样?代码是如何解释的?

2.ECS——执行上下文堆栈。

一系列活动的执行上下文在逻辑上形成一个堆栈。栈的底部总是全局上下文,栈的顶部是当前(活动的)执行上下文。当在不同的执行上下文之间切换时(退出和进入新的执行上下文),堆栈将被修改(通过按下或退出堆栈)。

推送:全局EC-本地EC1-本地EC2-当前EC推送:全局EC-本地EC1-本地EC2-当前EC。

我们可以用数组的形式表示环境堆栈:

ECS=[本地EC,全局EC];每次控制器进入一个函数(即使该函数被递归调用或作为构造函数调用),都会发生推栈的操作。过程类似于javascript数组的推送和弹出操作。

浏览器中的JavaScript解释器是作为单线程实现的。这意味着一次只能发生一件事,其他文章或事件将在所谓的执行堆栈中排队。下图是单线程堆栈的抽象视图:

我们已经知道,当浏览器第一次加载您的脚本时,它将默认进入全局执行上下文。如果在全局代码中调用一个函数,程序的定时将进入被调用的函数,并插入一个新的执行上下文,新创建的上下文将被推到执行栈的顶部。

如果在当前函数内部调用其他函数,这里也会发生同样的事情。代码的执行流进入内部函数,创建一个新的执行上下文,并将其推到执行堆栈的顶部。浏览器将始终在堆栈顶部执行执行上下文。一旦当前上下文函数的执行完成,它将从堆栈顶部弹出,并将上下文控制交给当前堆栈。以下示例显示了递归函数的执行堆栈调用过程:

(function foo(I){ if(I===3){ return;} else { foo(I);}}(0));

这段代码调用自己三次,每次给I的值加一。每次调用foo函数时,都会创建一个新的执行上下文。一旦上下文被执行,它将从堆栈顶部弹出,并将控制返回给后面的上下文,直到只剩下全局上下文。

关于执行堆栈(调用堆栈),需要记住五个要点:

单线程。同步执行。全球背景。无限函数上下文。每次调用函数时,它都会创建一个新的执行上下文,包括调用自身。3.VO-可变对象。

每个EC对应一个变量对象VO,该EC中定义的所有变量和函数都存储在其对应的VO中。

VO分为全局上下文VO (Global object,我们通常称之为全局对象)和函数上下文的AO。

VO: {//上下文中的数据(函数参数、函数声明(FD)、变量声明(var))}1)。进入执行上下文时,VO的初始化过程如下:

函数参数(进入函数执行上下文时)——变量对象的属性,其属性名为参数名,其值为实际参数值;对于未传递的参数,该值是未定义的;

FunctionDeclaration,FD) ——是变量对象的一个属性,它的属性名和值是由函数对象创建的。如果变量对象已经包含同名属性,则替换其值;

变量声明(var) ——是变量对象的属性,其属性名为变量名,其值未定义;如果变量名与声明的函数名或函数的参数名相同,则现有属性不会受到影响。注意:这个过程是有顺序的。

2)在代码执行阶段,将确定VO中某些属性的未定义值。

4.AO活动对象。

在函数的执行上下文中,不能直接访问VO。它主要扮演被称为激活对象(简称AO)的角色。这句话怎么理解?当EC环境是一个函数时,我们访问AO而不是VO。

VO(function context)===AO;AO是在进入函数的执行上下文时创建的,它为对象初始化一个参数属性,该属性的值是参数对象。

Ao={参数: {callee:length3360,properties-indexes 3360//函数传递的参数值} };FD的形式只能如下:

函数f(){}是在调用函数时创建的,但在实际函数执行之前。这是我们上面提到的第一个阶段,创作阶段。在这个阶段,解释器扫描传递给函数的参数或自变量、局部函数声明和局部变量声明,并创建executionContextObj对象。扫描的结果将完成可变对象的创建。

的内部执行顺序如下:

1.找到调用函数的代码。

2.在执行函数代码之前,创建执行上下文。3.进入创建阶段:

初始化范围链:创建变量对象:创建参数对象,检查上下文,初始化参数名称和值,并创建引用的副本。扫描上下文的函数声明:对于每个找到的函数,在变量对象上创建一个属性(正好是函数的名称),这个属性在内存中有函数的引用。如果函数名已经存在,引用指针将被重写。扫描上下文的变量声明:对于找到的每个变量声明,在变量对象上创建一个属性——,它是变量的名称,并将变量的值初始化为undefined。如果变量对象中已经存在该变量的名称,将不执行任何操作,扫描将继续。在上下文中找到“这个”的价值。4.激活/代码执行阶段:在当前上下文中运行/解释函数代码,并在代码逐行执行时分配变量值。

例子

1.具体的例子。

函数foo(I){ var a=' hello ';var b=函数privateB(){ };函数c(){ } } foo(22);当调用foo(22)时,创建状态如下:

foexecutioncontext={ scope chain : }.},variable object : { arguments : { 0: 22,length: 1 },i: 22,C:指向函数c()的指针a: undefined,b: undefined},this3360 {.}}如您所见,创建状态负责处理已定义属性的名称,而不是为它们分配特定的值,并处理形式参数/实际参数。一旦创建阶段完成,执行流程就进入函数和激活/代码执行阶段,看看执行完成后函数会是什么样子:

foexecutioncontext={ scope chain : }.},variable object : { arguments : { 0: 22,length: 1 },i: 22,C :指向函数C()的指针a :' hello ',b :指向函数privateb ()}的指针,这: { 0.}} 2.VO示例:

警报(x);//functionvar x=10;警报(x);//10x=20;函数x(){ };警报(x);//20进入执行上下文时,

ECObject={ VO: {对FunctionDeclaration 'x' }的引用};执行代码时:

ECObject={ VO:{ x:20 //与函数x同名,先被10替换,后变成20 } };对于上面的过程,我们会详细说明。

进入上下文时,由填充函数声明VO;在同一阶段,有一个变量声明“x”,但如前所述,变量声明在函数声明和函数参数之后,变量声明不会与现有的同名函数声明和函数参数冲突。因此,在进入上下文的阶段,VO按照以下形式填写:

VO={ };VO['x']=引用的函数声明' x'/找到的var x=10//如果函数“x”还没有定义,//那么‘x’是未定义的,但是//在我们的例子中,变量声明不会影响同名的函数值。VO['x']=值不受影响,仍然是函数的代码执行阶段。VO修改如下:

VO[' x ']=10;VO[' x ']=20;下面的例子再次表明,在上下文阶段,变量存储在VO中(因此,尽管else的代码块永远不会被执行,“B”仍然在VO中)。

if(true){ var a=1;} else { var b=2;} alert(a);//1 alert(b);//未定义,但不是“b未定义”3。AO示例:

功能测试(a,b){ var c=10;function d(){ } var e=function _ e(){ };(函数x(){ });}测试(10);//进入测试(10)的执行上下文时调用,其AO为:

testEC={ AO : { arguments : { callee : test length :1,0:10 },a:10,c:undefined,AO:对FunctionDeclaration 'd ',e:undefined }的引用} };可以看到,在建立阶段,VO被赋予了除参数、函数声明和参数之外的特定属性值,其他变量属性默认不定义。函数表达式不影响VO,所以VO中不存在(函数x() {})。

当执行测试(10)时,其AO为:

testEC={ ao : { arguments : { calleAO: test,length:1,0:10 },a:10,c:10,AO:对FunctionDeclaration 'd '的引用,e :对FunctionDeclaration 'e '的引用} };可以看出,只有在这个阶段,变量属性才会被赋予特定的值。

5.提升解密。

在之前的JavaScript Item中,它被简化为变量,函数声明被提升到函数作用域的顶部。然而,没有人解释为什么会发生这种情况的细节。在学习了上面关于解释器如何创建主动活动对象的新知识后,就很容易理解为什么了。请看下面的例子:

(function(){ console . log(foo的类型);//函数指针console.log(类型为bar);//undefined var foo='hello ',bar=function(){ return ' world ';};function foo() {返回“hello”;}}());我们可以回答以下问题:

1.为什么我们可以在声明foo之前访问它?如果我们遵循创建阶段,我们知道变量是在激活/代码执行阶段创建的。因此,在函数开始执行之前,foo已经在活动对象中定义。

2.foo已经声明了两次,为什么foo显示为函数而不是undefined或string?虽然foo已经声明了两次,但是我们知道从创建阶段就已经在活动对象中创建了函数,这发生在变量创建之前,如果活动对象上已经存在属性名,我们只更新引用。因此,对foo()函数的引用首先是在活动对象中创建的,当我们解释var foo时,我们看到foo属性名已经存在,所以代码什么也不做,继续执行。

3.为什么bar的值没有定义?Bar实际上是一个变量,但是变量的值是一个函数,我们知道变量是在创建阶段创建的,但是它们被初始化为未定义。

这就是本文的全部内容,包括详细的问题和示例代码,以帮助您更好地理解javascript的执行上下文。希望你喜欢这篇文章。

版权声明:向我学习javascript的执行上下文是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。