资讯专栏INFORMATION COLUMN

JavaScript 之执行上下文

FrozenMap / 1996人阅读

摘要:本文就梳理有关执行上下文也叫执行环境的知识。全局代码的执行上下文栈可以表示为函数代码当执行函数代码时,函数代码上下文被压入到执行上下文栈中。

本文共 1090 字,读完只需 4 分钟
概述

JavaScript 是函数式编程语言,作用域也是以函数为单位,那么,这些函数代码块是怎么样的顺序进行的呢, JS 的可执行代码又分为 3 种,不同类型的代码有不一样的执行环境。本文就梳理有关 JS 执行上下文(execution context),也叫执行环境的知识。

活动的执行代码的上下文在语言底层逻辑上构成一个执行上下文栈(excution context stack),我们知道,栈有两个行为,压入栈和弹出栈,并且有后进先出的特点, 最先进入的项会在栈底最后弹出。

不同执行环境的有其相应的变量对象(Variable Object),某个执行环境的所有可执行代码都执行完毕后,该环境中的变量对象和函数定义会被清除。

函数代码会在执行完后清除变量占用的内存,全局代码则会在关闭环境,比如关闭浏览器后清除。

一、代码类型

JS 中可执行的代码可分为三种类型:

全局代码

函数代码

Eval 代码

全局代码

在 web 浏览器中,全局执行环境是 window 对象,所有的全局变量和函数都是作为 window 的属性和方法存在。

全局代码的执行上下文栈可以表示为:

ECStack = [
    globalContext
]

函数代码

当执行函数代码时,函数代码上下文被压入到执行上下文栈中。函数代码的执行环境中,有自己的内部的定义的变量和声明。

function foo1() {
    var name1 = "a";
    console.log(name1)
}

function foo2(){
    var name2 = "b";
    console.log(name2)
}

foo1();
foo2();

上面的执行上下文可以表示为:

ECStack = [
    globalContext
];

ECStack.push( functionContext);
ECStack.pop();
ECStack.push( functionContext);
ECStack.pop();

一个函数,可能有多个执行上下文,每个函数的调用都会产生新的上下文。

function foo() {
    ...
}

foo("a");
foo("b");
foo("c");

eval 代码

eval 关键字接受一个字符串作为参数,并将其作为书写文字上下文的代码。

function foo(str, a) {
    eval( str );  // 声明一个新变量
    console.log(a, b);
}
var b = 123;

foo("var b = 456 ", 123) // 123, 456

以上代码引用《你不知道的 JavaScript》的例子,eval 函数中的字符串,被当做可执行代码,最后在 foo 函数中声明了 b 变量,并覆盖了 foo 函数外部的 b 变量。

eval 函数和 JS 的词法作用域的行为,会造成变量环境和执行上下文的混乱,尽量别使用它。

二、执行上下文栈

前面其实已经提到很多关于执行上下文栈的内容,简而言之,JS 在遇到全局代码,函数代码,eval 代码时,会创建相应的执行上下文,不同的上下文,有其变量对象(variable object: VO)和作用域。

看这一段代码:

function func3() {
    console.log("fun3")
}

function func2() {
    func3();
}

function func1() {
    func2();
}

func1();

以上代码引用自冴羽的github,代码顺序用执行上下文栈来表示就是:

ECStack.push(globalContext);

// 调用 func1(), 进入 func1 执行环境
ECStack.push( functionContext);

// func1中调用了 func2,进入 func2 的执行上下文
ECStack.push( functionContext);

// func2 调用 func3, 进入 func3 的执行上下文
ECStack.push( functionContext);

// func3 执行完毕,退出 func3 执行环境
ECStack.pop();

// func2 执行完毕,退出 func2 执行环境
ECStack.pop();

// func1 执行完毕,退出 func1 执行环境
ECStack.pop();

函数代码执行完毕后,执行上下文栈底只剩下全局代码上下文 globalContext, 直到关闭浏览器才会彻底清空执行上下文栈。

总结

代码在执行时会创建由不同作用域构成的作用域链作用域链保证 JS 中的变量和函数都能够有序地访问和执行。

如果是函数代码,则将其 活动对象 作为变量对象,活动对象最开始时只包含一个对象,即 arguments 对象。

后面的文章会介绍变量对象(VO)和作用域链的具体内容,敬请期待吧。

博客内容源自个人公众号,专注分享原创文章,欢迎关注。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98384.html

相关文章

  • JavaScript深入执行下文

    摘要:深入系列第七篇,结合之前所讲的四篇文章,以权威指南的为例,具体讲解当函数执行的时候,执行上下文栈变量对象作用域链是如何变化的。前言在深入之执行上下文栈中讲到,当代码执行一段可执行代码时,会创建对应的执行上下文。 JavaScript深入系列第七篇,结合之前所讲的四篇文章,以权威指南的demo为例,具体讲解当函数执行的时候,执行上下文栈、变量对象、作用域链是如何变化的。 前言 在《Jav...

    gougoujiang 评论0 收藏0
  • JavaScript深入作用域链

    摘要:下面,让我们以一个函数的创建和激活两个时期来讲解作用域链是如何创建和变化的。这时候执行上下文的作用域链,我们命名为至此,作用域链创建完毕。 JavaScript深入系列第五篇,讲述作用链的创建过程,最后结合着变量对象,执行上下文栈,让我们一起捋一捋函数创建和执行的过程中到底发生了什么? 前言 在《JavaScript深入之执行上下文栈》中讲到,当JavaScript代码执行一段可执行代...

    waltr 评论0 收藏0
  • JavaScript深入闭包

    摘要:深入系列第八篇,介绍理论上的闭包和实践上的闭包,以及从作用域链的角度解析经典的闭包题。定义对闭包的定义为闭包是指那些能够访问自由变量的函数。 JavaScript深入系列第八篇,介绍理论上的闭包和实践上的闭包,以及从作用域链的角度解析经典的闭包题。 定义 MDN 对闭包的定义为: 闭包是指那些能够访问自由变量的函数。 那什么是自由变量呢? 自由变量是指在函数中使用的,但既不是函数参数也...

    caige 评论0 收藏0
  • JavaScript深入执行下文

    摘要:深入系列第三篇,讲解执行上下文栈的是如何执行的,也回答了第二篇中的略难的思考题。 JavaScript深入系列第三篇,讲解执行上下文栈的是如何执行的,也回答了第二篇中的略难的思考题。 顺序执行? 如果要问到 JavaScript 代码执行顺序的话,想必写过 JavaScript 的开发者都会有个直观的印象,那就是顺序执行,毕竟: var foo = function () { ...

    codecraft 评论0 收藏0
  • JavaScript专题递归

    摘要:专题系列第十八篇,讲解递归和尾递归定义程序调用自身的编程技巧称为递归。然而非尾调用函数,就会创建多个执行上下文压入执行上下文栈。所以我们只用把阶乘函数改造成一个尾递归形式,就可以避免创建那么多的执行上下文。 JavaScript 专题系列第十八篇,讲解递归和尾递归 定义 程序调用自身的编程技巧称为递归(recursion)。 阶乘 以阶乘为例: function factorial(n...

    asoren 评论0 收藏0
  • 【进阶1-2期】JavaScript深入执行下文栈和变量对象

    摘要:本计划一共期,每期重点攻克一个面试重难点,如果你还不了解本进阶计划,点击查看前端进阶的破冰之旅本期推荐文章深入之执行上下文栈和深入之变量对象,由于微信不能访问外链,点击阅读原文就可以啦。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,今天是第二天。 本计划一共28期,每期...

    Richard_Gao 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<