最近更新
阅读排行
读过本文章的之前读了
关注本站

JavaScript 执行上下文 (execution contexts)

阅读:5521 次   编辑日期:2013-10-16

目录:

概述:

本片文章将跟大家聊聊ECMAScript的执行上下文和相关的可执行代码类型。

定义:

每当控制器运行到ECMAScript可执行代码的时候,就会进入到一个执行上下文。执行上下文(简称:EC)是一个抽象的概念,它用于在ECMA-262标准中区分不同类型的可执行代码。
标准规范并没有从技术实现的角度定义EC的准确类型和结构,这应该是具体实现ECMAScript引擎时要考虑的问题。
执行上下文从逻辑上组成了一个栈(栈的特点是先进后出),所以这个栈的最底部永远是全局上下文(global context),栈的最顶部是当前活动的上下文,当改变当前活动的时候,这个栈就会被修改(通过压栈或者退栈的方式)。

可执行代码的类型:

可执行代码与执行上下文有关系,谈到代码类型,有的时候可能意味着一个执行上下文。举个例子吧,我们把执行上下文当成一个数组:
    ECStask = [ ];
每次进入function (即使function被递归调用或作为构造函数) 的时候或者内置的eval函数工作的时候,这个堆栈都会被压栈。

全局代码:

这类代码是在“程序”级别上被处理的:比如,加载一个外部的js文件或者内联的js代码(被包含在<script></script>标签内)。全局代码不包含任何function体内的代码。
在初始化(程序启动)阶段,ECStack是这样的:
   ECStack = [
	  globalContext
    ];

函数代码:

当进入funtion函数代码(所有类型的funtions)的时候,新元素被压栈到ECStack。需要注意的是,实体函数代码不包括内部函数(inner functions)代码。如下所示,我们使函数递归调用自己一次。
    (function foo(bar){
		if (bar){
		return;
		}
		foo(true);
	 })();
之后,ECStack就被修改成下面:
   //第一次激活foo函数调用(函数后面的())
	ECStack = [
		 functionContext --函数调用
		 globalContext --全局
	];
	//递归激活foo函数(foo(true))
	ECStack = [
		 functionContext -- recursively --递归
		 functionContext -- 函数调用
		 globalContext -- 全局
	];
每次函数返回,退出当前活动的执行上下文时,ECStack就会被执行对应的退栈操作(先进后出)和传统的栈实现一致,栈指针会自动移动位置。一个抛出的异常如果没被截获的话也有可能从一个或多个执行上下文退出。相关代码执行完以后,ECStack只会包含一个全局上下文(global context),一直到整个应用程序结束。

Eval代码:

eval代码有点意思,有一个概念叫:调用上下文(calling context),例如,eval函数调用的时候产生的上下文。eval(变量或函数声明)活动会影响调用上下文(calling context)。
    eval('var x = 10');
	(function foo() {
	  eval('var y = 20');
	})();
	alert(x); // 10
	alert(y); // "y" 提示没有声明(not defined)

	//ECStack的变化过程:
	ECStack = [
	  globalContext
	];

	// eval('var x = 10');
	ECStack.push({
	  context: evalContext,
	  callingContext: globalContext
	});

	// 退出eval上下文
	ECStack.pop();

	// 调用foo函数
	ECStack.push( functionContext);

    // eval('var y = 20');
    ECStack.push({
    context: evalContext,
    callingContext:  functionContext
        });

    // 从eval返回
    ECStack.pop();

    // 从foo返回
    ECStack.pop();
这是很平常很合理的一个堆栈。
在版本号1.7以上的SpiderMonkey(内置于Firefox,Thunderbird的JS引擎)实现中,可以把调用上下文作为第二个参数传递给eval。那么,如果这个上下文存在,就有可能影响“私有”变量(在该上下文中声明的变量)。
    function foo() {
	  var x = 1;
	  return function () { alert(x); };
	};

	var bar = foo();

	bar(); // 1
	eval('x = 2', bar); // 传入上下文,影响了内部的var x 变量
	bar(); // 2

结论:

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

关于本文:

本文翻译自 Dmitry Soshnikov 的文章 Execution Contexts.
将本篇文章分享到:
top