宝哥软件园

全面了解Javascript执行上下文

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

在本文中,我们将深入解释执行上下文JavaScript中最基本和最重要的概念。相信看完这篇文章,你会明白javascript引擎在执行代码之前做了什么,为什么有些函数和变量可以在声明之前使用,以及它们的最终值是如何定义的。

什么是执行上下文

有三种运行Javascript代码的环境:

全局级代码这是默认的代码运行环境。一旦代码被加载,引擎首先进入这个环境。

函数级代码当函数被执行时,函数体中的代码被运行。

评估代码在评估函数中运行的代码。

网上可以找到很多资源来解释范围。为了使这篇文章易于大家理解,我们可以将“执行上下文”视为当前代码的运行环境或范围。让我们看一个例子,它包括全局和函数级执行上下文:

在上图中,有四个执行上下文。紫色代表全球语境;绿色代表人的功能范围内的上下文;蓝色和橙色代表人物功能中其他两个功能的上下文。请注意,在任何情况下,只有一个全局上下文可以被任何其他上下文访问。也就是说,我们可以在person的上下文中访问全局上下文中的sayHello变量,当然这个变量也可以在函数firstName或lastName中访问。

函数上下文的数量没有限制。每次调用和执行函数时,引擎都会自动创建一个新的函数上下文。换句话说,它将创建一个新的局部范围,在其中可以声明私有变量。不能在外部上下文中直接访问此本地范围内的元素。在上面的例子中,内部函数可以在外部上下文中访问声明的变量,否则它将不起作用。那么,是什么原因呢?发动机内部是如何处理的?

执行上下文堆栈

在浏览器中,javascript引擎在一个线程中工作。也就是说,在某个时刻,只有一个事件被激活处理,其他事件被放入队列,等待处理。下面的示例图描述了这样的堆栈:

我们已经知道,当浏览器加载javascript代码文件时,默认情况下会首先输入全局执行上下文。当一个函数在全局上下文中被调用和执行时,程序流进入被调用的函数,引擎为该函数创建一个新的执行上下文,并将其推到执行上下文堆栈的顶部。浏览器总是在堆栈顶部执行当前上下文。一旦执行完成,上下文将从堆栈顶部弹出,然后在它下面输入上下文来执行代码。这样,堆栈中的上下文将依次执行,堆栈将弹出,直到它返回到全局上下文。请看下面的例子:

(function foo(I){ if(I===3){ return;} else { foo(I);} }(0));在上面的foo被声明之后,它被()操作符强制直接运行。函数代码调用自己三次,每次局部变量I增加1。每次foo函数被自己调用时,都会创建一个新的执行上下文。每当执行一个上下文时,上层上下文从堆栈中弹出,返回到前一个上下文,直到它再次返回到全局上下文。整个过程抽象如下图:所示

可以看出,执行上下文的抽象概念可以概括如下:

单线程

同步执行

独特的全球背景

函数的执行上下文数量没有限制

每次调用一个函数,都会为它创建一个新的执行上下文,甚至是调用函数本身。

执行上下文的建立过程

我们现在知道,每次调用一个函数,都会创建一个新的执行上下文。然而,在javascript引擎中,该上下文的创建过程具体分为两个阶段:

构建阶段(在调用函数时,但在执行函数体中的特定代码之前发生)

建立变量、函数、参数对象和参数

建立范围链

确定这个的值

代码执行阶段:

变量赋值、函数引用、其他代码的执行

实际上,执行上下文可以看作一个对象,它包含以上三个属性:

(executioncontextobj={ variable object : {/*详细分析参数对象、参数、内部变量和函数声明*/},scope Chang : {/* variable object和variableobject */}在所有父执行上下文中,这是3360 {}}建立阶段和代码执行阶段

准确地说,执行上下文对象(上面提到的executionContextObj)是在调用函数时创建的,但在实际执行函数体之前。当函数被调用时,它是我上面描述的两个阶段中的第一个——建立阶段。此时,引擎将检查函数中的参数、声明的变量和内部函数,然后基于这些信息构建一个executionContextObj对象。在这个阶段,将确定变量对象、范围链以及由此指向的对象。

第一阶段的具体流程如下:

查找在当前上下文中调用函数的代码

在执行被调用函数体中的代码之前,开始创建执行上下文

进入第一阶段-构建阶段:

建立变量对象对象:

建立参数对象,检查当前上下文中的参数,并在该对象下建立属性和属性值

检查当前上下文中的函数声明:

每次找到函数声明时,都会在variableObject下用函数名创建一个属性。属性值是对内存中函数地址的引用

如果上述函数名已经存在于变量对象下,相应的属性值将被新引用覆盖。

初始化范围链

确定它在上下文中指向的对象

代码执行阶段:

执行函数体中的代码,逐行运行代码,并为variableObject中的变量属性赋值。

让我们看一个具体的代码示例:

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

foexecutioncontext={ variable object : { arguments : { 0: 22,length: 1 },i: 22,c:指向函数c()的指针a:未定义,B:未定义},scopechan: {.},这: {.}}由此可以看出,在建立阶段,除了参数、函数声明和参数被赋予特定的属性值外,其他变量属性默认是未定义的。一旦上述建立阶段结束,引擎将进入代码执行阶段。该阶段完成后,上述执行上下文对象如下:

foexecutioncontext={ variableobject : { arguments : { 0: 22,length: 1 },i: 22,c:指向函数c()的指针a: 'hello ',B:指向函数privateb ()}的指针,作用域链: {.},这个: {.}}我们可以看到,只有在代码执行阶段,变量属性才会被赋予特定的值。

扩大局部变量范围的原因

我在网上一直看到这样一个总结:函数中声明的变量和函数的范围被提升到函数的顶部,换句话说,其中声明的变量和函数一进入函数体就可以被访问。这是真的,但是你知道为什么吗?相信你也应该通过上面的解释来理解。但是在这里再分析一下。

请看下面的代码:

(function(){ console . log(foo的类型);//函数指针console.log(类型为bar);//undefined var foo='hello ',bar=function(){ return ' world ';};function foo() {返回“hello”;} }());上面的代码定义了一个匿名函数,该函数被迫通过()运算符来理解和执行。那么我们知道此时将创建一个执行上下文。我们可以看到,foo和bar变量可以在示例中立即访问,foo作为函数引用输出,bar通过typeof未定义。

为什么我们可以在声明foo变量之前访问foo?

因为在上下文建立阶段,首先处理参数和参数,然后是函数的声明,最后是变量的声明。然后,在发现foo函数的声明后,将在variableObject下建立一个foo属性,其值是对该函数的引用。处理变量声明时,发现有var foo的声明,但variableObject已经有foo属性,所以直接跳过。当您进入代码执行阶段时,您可以访问foo属性,因为它已经存在并且是一个函数引用。

为什么bar是未定义的?

因为bar是变量的声明,所以在构建阶段分配给它的默认值是未定义的。因为只有在代码执行阶段才会给它一个特定的值,所以调用typeof(bar)时的输出值是未定义的。

好了,就这样。我相信你应该对执行环境有所了解。这个执行上下文的概念非常重要。一定要理解好!

以上对Javascript执行上下文的全面理解是边肖与大家分享的全部内容。希望能给大家一个参考,支持我们。

更多资讯
游戏推荐
更多+