`
三月沙
  • 浏览: 618004 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ECMA-262-3 in detail. Chapter 1. Execution Contexts

 
阅读更多

 by Dmitry Soshnikov

 

 

In this note we will mention execution contexts of ECMAScript and types of executable code related with them.

Every time when control is transferred to ECMAScript executable code, control is entered an execution context.

Execution context (abbreviated form — EC) is the abstract concept used by ECMA-262 specification for typification and differentiation of an executable code.

The standard does not define accurate structure and kind of EC from the technical implementation viewpoint; it is a question of the ECMAScript-engines implementing the standard.

Logically, set of active execution contexts forms a stack. The bottom of this stack is always a global context, the top — a current (active) execution context. The stack is modified (pushed/popped) during the entering and exiting various kinds of EC.

With abstract concept of an execution context, the concept of type of an executable code is related. Speaking about code type, it is possible in the certain moments to mean an execution context.

For examples, we define the stack of execution contexts as an array:

ECStack = [];

The stack is pushed every time on entering a function (even if the function is called recursively or as theconstructor), and also at built-in eval function work.

This type of code is processed at level Program: i.e. the loaded external .js-file or the local inline-code (inside the <script></script> tags). The global code does not include any parts of a code which are in bodies of functions.

At initialization (program start), ECStack looks like:

ECStack = [
  globalContext
];

On entering the function code (all kinds of functions), ECStack is pushed with new elements. It is necessary to notice that the code of concrete function does not include codes of the inner functions.

For example, let’s take the function which calls itself recursively once:

(function foo(flag) {
  if (flag) {
    return;
  }
  foo(true);
})(false);

Then, ECStack is modified as follows:

// first activation of foo
ECStack = [
  <foo> functionContext
  globalContext
];
 
// recursive activation of foo
ECStack = [
  <foo> functionContext – recursively
  <foo> functionContext
  globalContext
];

Every return from a function exits the current execution context and ECStack popped accordingly — consecutively and upside-down — quite natural implementation of a stack. After the work of this code is finished, ECStack again contains only globalContext — until the program end.

A thrown but not caught exception may also exit one or more execution contexts:

(function foo() {
  (function bar() {
    throw 'Exit from bar and foo contexts';
  })();
})();

Things are more interesting with eval code. In this case, there is a concept of a calling context, i.e. a context from which eval function is called.

The actions made by eval, such as variable or function definition, influence exactly the calling context:

// 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

Note, in the strict-mode of ES5eval already does not influence the calling context, but instead evaluates the code in the local sandbox.

For the example above we have the following ECStack modifications:

ECStack = [
  globalContext
];
 
// eval('var x = 10');
ECStack.push({
  context: evalContext,
  callingContext: globalContext
});
 
// eval exited context
ECStack.pop();
 
// foo funciton call
ECStack.push(<foo> functionContext);
 
// eval('var y = 20');
ECStack.push({
  context: evalContext,
  callingContext: <foo> functionContext
});
 
// return from eval
ECStack.pop();
 
// return from foo
ECStack.pop();

I.e. quite casual and logical call-stack.

In old SpiderMonkey implementations (Firefox), up to version 1.7, it was possible to pass a calling context as a second argument for eval function. Thus, if the context still exists, it is possible to influence private variables:

 

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

However, due to security reasons in modern engines it was fixed and is not significant anymore.

This theoretical minimum is required for the further analysis of details related with execution contexts, such as variable object or scope chain, which descriptions can be found in the appropriate chapters.

Corresponding section of ECMA-262-3 specification — 10. Execution Contexts.

 

中文版 转载自 http://www.cnblogs.com/justinw/archive/2010/04/16/1713086.html

 

 

[JavaScript]ECMA-262-3 深入解析.第一章.执行上下文

JavaScript]ECMA-262-3 深入解析.第一章.

这篇文章我们主要探讨ECMAScript执行上下文和相关的ECMAScript可执行代码。

每次当控制器转到ECMAScript可执行代码的时候,即会进入到一个执行上下文

执行上下文(简称-EC)是一个抽象概念,ECMA-262标准用这个概念同可执行代码(executable code)概念进行区分。

标准规范没有从技术实现的角度准确定义EC的类型和结构;这应该是具体实现ECMAScript引擎时要考虑的问题。

活动的执行上下文在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(global context),堆栈顶部是当前(活动的)执行上下文。堆栈在EC类型的变量(various kingds of EC)被推入或弹出的同时被修改。

可执行代码的概念与抽象的执行上下文的概念是相对的。在某些时刻,可执行代码与执行上下文是等价的。

例如,我们可以定义一个数组来模拟执行上下文堆栈:

1
ECStack = [];

每次进入函数 (即使函数被递归调用或作为构造函数) 的时候或者内置的eval函数工作的时候,这个堆栈都会被推入。

这种类型的代码是在"程序"级处理的:例如加载外部的js文件或者本地的在<script></script>标签内的代码。全局代码不包括任何函数体内的代码。

在初始化(程序启动)阶段,ECStack是这样的:

 

1
2
3
ECStack = [
  globalContext
];

 

当进入函数代码(所有类型的函数),ECStack被推入新元素。要注意的是,具体的函数代码不包括内部函数(inner functions)代码。如下所示,我们使函数自己调自己的方式递归一次:

1
2
3
4
5
6
(function  foo(bar) {
  if (bar) {
    return;
  }
  foo(true);
})();

那么,ECStack以如下方式被改变:

1
2
3
4
5
6
7
8
9
10
11
12
// first activation of foo
ECStack = [
  <foo> functionContext
  globalContext
];
  
// recursive activation of foo
ECStack = [
  <foo> functionContext – recursively
  <foo> functionContext
  globalContext
];

每次返回存在的当前执行上下文和ECStack弹出相应的执行上下文的时候,栈指针会自动移动位置,这是一个典型的堆栈实现方式。一个被抛出但是没有被截获的异常,同样存在一个或多个执行上下文。当相关段代码执行完以后,直到整个应用程序结束,ECStack都只包括全局上下文(global context)。

 eval 代码有点儿意思。它有一个概念: 调用上下文(calling context), 这是一个当eval函数被调用的时候产生的上下文。eval(变量或函数声明)活动会影响调用上下文(calling context)

1
2
3
4
5
6
7
8
eval('var x = 10');
  
(function foo() {
  eval('var y = 20');
})();
  
alert(x); // 10
alert(y); // "y" is not defined

ECStack的变化过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ECStack = [
  globalContext
];
  
// eval('var x = 10');
ECStack.push(
  evalContext,
  callingContext: globalContext
);
  
// eval exited context
ECStack.pop();
  
// foo funciton call
ECStack.push(<foo> functionContext);
  
// eval('var y = 20');
ECStack.push(
  evalContext,
  callingContext: <foo> functionContext
);
  
// return from eval
ECStack.pop();
  
// return from foo
ECStack.pop();

看到了吧,这是一个非常普通的逻辑调用堆栈

在版本号1.7以上的SpiderMonkey(内置于Firefox,Thunderbird)的实现中,可以把调用上下文作为第二个参数传递给eval。那么,如果这个上下文存在,就有可能影响“私有”(类似以private关键字命名的变量一样)变量。

1
2
3
4
5
6
7
8
9
10
11
12
function foo() {
  var x = 1;
  return function () { alert(x); };
};
  
var bar = foo();
  
bar(); // 1
  
eval('x = 2', bar); // pass context, influence on internal var "x"
  
bar(); // 2

这篇文章的内容是未来分析其他跟执行上下文相关的主题(例如变量对象,作用域链,等等)的最起码的理论基础,这些主题将在后续章节中讲到。

这篇文章的内容在ECMA-262-3 标准规范中对应的章节— 10. Execution Contexts.

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics