摘要:逻辑上讲,活跃的执行上下文集合组成了一个栈。栈底是全局上下文,栈顶是当前活跃执行上下文。举个例子,我们将执行上下文定义成一个数组。一个抛出但是没有捕获的异常可能导致退出一个或多个执行上下文代码代码更加复杂。
原文地址
作者的话有很多文章已经对ECMAScript的核心概念做了详尽解读。本系列文章翻译自Dmitry Soshnikov的个人网站,相信不少人已经看过原文或者译文。原文简洁易懂并且严谨,条理清晰地阐明了所有JavaScript开发者不得不深入理解的ECMAScript核心概念。重复翻译的原因主要是为了个人收藏、整理之用。初次翻译,技巧拙劣,如有不足,请不吝赐教。
正文介绍
定义
可执行代码的类型
全局代码
函数代码
Eval代码
结论
介绍在这篇文章中我们将提到ECMAScript的执行上下文和与之相关的可执行代码类型。
定义每次当控制转移到ECMAScript可执行的代码,控制就进入了一个执行上下文。
执行上下文(Execution context,缩写:EC)是ECMA-262标准使用的抽象概念,用来分类和区别一段可执行代码
标准没有从技术实现角度定义EC的准确结构和类型,这是ECMAScript引擎如何实现标准的问题。
逻辑上讲,活跃的执行上下文集合组成了一个栈。栈底是全局上下文(global context),栈顶是当前(活跃)执行上下文。在进入和退出不同的EC时,栈被修改(pop/push)。
可执行代码的类型的概念与执行上下文的抽象概念相关。讲到代码类型,在特定时候,它可以指执行上下文。
举个例子,我们将执行上下文定义成一个数组。
ECStack = [];
每次进入一个函数,栈会被压入一个上下文(即使是函数被递归调用,或者作为构造函数被调用),在eval函数中也是如此。
全局代码这个类型的代码在程序(Program)层面被处理:比如加载的外部js文件、内联代码(在标签内)。全局代码不包含任何函数体内的代码。
在初始化(程序开始)时,ECStack看起来像这样:
ECStack = [ globalContext ];函数代码
在进入函数代码(所有类型的函数)时,ECStack会被塞入新的元素。需要注意到:所说的函数代码不包含内部函数的代码。
举个例子,我们看看这个递归调用自身一次的函数:
(function foo(flag) { if (flag) { return; } foo(true); })(false);
然后,ECStack被修改成下面这样:
// first activation of foo ECStack = [functionContext globalContext ]; // recursive activation of foo ECStack = [ functionContext – recursively functionContext globalContext ];
每次函数返回会退出当前执行上下文,ECStack弹出一个元素。当这段代码的工作结束,ECStack再一次地仅包含globalContext-直到程序退出。
一个抛出但是没有捕获的异常可能导致退出一个或多个执行上下文:
(function foo() { (function bar() { throw "Exit from bar and foo contexts"; })(); })();Eval代码
eval代码更加复杂。在这种情况下,有一个概念叫调用上下文(calling context),比如eval函数被调用的地方的上下文:
// influence global context eval("var x = 10"); (function foo() { // and here, variable "y" is // created in the local context // of "foo" function eval("var y = 20"); })(); alert(x); // 10 alert(y); // "y" is not defined
注意,在ES5的严格模式中,eval已经不会影响 调用上下文,而是在本地沙箱中运行代码。
对于上面的例子,我们有如下的ECStack修改:
ECStack = [ globalContext ]; // eval("var x = 10"); ECStack.push({ context: evalContext, callingContext: globalContext }); // eval exited context ECStack.pop(); // foo funciton call ECStack.push(functionContext); // eval("var y = 20"); ECStack.push({ context: evalContext, callingContext: functionContext }); // return from eval ECStack.pop(); // return from foo ECStack.pop();
非常随意而正常的调用栈。
在老版的SpiderMonkey 实现(firefox)中,最多到1.7版本,可以将 调用上下文作为第二个参数传给eval函数。因此,如果上下文任存在,会影响到私有变量:
function foo() { var x = 1; return function () { alert(x); }; }; var bar = foo(); bar(); // 1 eval("x = 2", bar); // pass context, influence internal var "x" bar(); // 2
然而,由于现代引擎的安全问题,它被修复而不在有意义。
ES2015+引入了一种新的代码类型-模块代码
结论这些基础的理论需要被用来更深入地研究与执行上下文相关的细节,比如变量对象和作用域链,这些描述可以在适当的章节被找到。
原始作者:Dmitry Soshnikov
原始发布时间:2009-06-26
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/96823.html
摘要:不同执行上下文中的变量对象对于所有的执行上下文类型,变量对象的一些操作比如变量实例化和行为是共通的。从这点看,将变量对象看作是抽象的基础的东西很便利。全局上下文的变量对象现在,应该先给出全局对象的定义。 原文地址 作者的话 有很多文章已经对ECMAScript的核心概念做了详尽解读。本系列文章翻译自Dmitry Soshnikov的个人网站,相信不少人已经看过原文或者译文。原文简洁易懂...
摘要:堆栈结构的底部是全局执行上下文,顶部是当前执行上下文。不同的执行上下文切换时堆栈会发生改变译论及代码类型时,在某些时候可能也意味着执行上下文。函数体中代码执行完后,只剩全局上下文直到程序结束译代码更有意思。 第一次翻译,希望各位多多包涵,有错误处还望指出,欢迎提出建议。 Chapter 1.Execution Contexts Introduction (介绍) Definitio...
摘要:如果在全局代码中调用函数,程序的顺序流进入被调用的函数,创建新的执行上下文并将其推送到执行堆栈的顶部。每次调用函数时,都会创建一个新的执行上下文。 翻译:疯狂的技术宅链接:http://davidshariff.com/blog/... 本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章 在这篇文章中,我将深入探讨JavaScript的最...
摘要:前言大家学的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行。其实,前面两个例子里的变量,也可以换成,因为和外面的不在一个作用于,所以不会出现问题,这也是匿名函数闭包的威力。 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行。 在详细了解这个之前,我们来谈了解一下自执行这个叫法,本文对这个功能的叫法也不一定完全对...
摘要:然后最后一步就是从父作用域链中将该特殊对象删除,整个过程的伪代码如下注意这里,该属性不能删除,只读。 起因是我在逛sf的时候看到了一个人的提问: 为什么将函数c赋值给变量b,在函数体里面,给c赋值,为什么会失败?也就是这代码执行时为什么c打印出来的不是3 var b = function c () { a=1, b=2, c=3; console.log(a); ...
阅读 1601·2021-11-02 14:42
阅读 497·2021-10-18 13:24
阅读 899·2021-10-12 10:12
阅读 1801·2021-09-02 15:41
阅读 3187·2019-08-30 15:56
阅读 2863·2019-08-29 16:09
阅读 2019·2019-08-29 11:13
阅读 3591·2019-08-28 18:06