ECMA-262-5 词法环境:ECMA实现(三)--- 执行上下文
来源:互联网 发布:java中国天气网api 编辑:程序博客网 时间:2024/05/21 09:13
执行上下文的结构
这部分主要讨论在ES5中执行上文(同执行环境)的结构,它与ES3中的有一些不同,先看看它的组成组件:
ExecutionContextES5 = { ThisBinding: <this value>, VariableEnvironment: { ... }, LexicalEnvironment: { ... },}
在执行上下文中同时包含词法环境组件和变量环境组件,其中执行上下文的词法环境和变量环境组件始终为 词法环境 对象,这一点可能很容易造成混淆。下面也将会介绍它们的区别,但在这先简单的说下它们的区别在于函数声明(FD)和函数表达式(FE)中[[scope]]值的不同。
下面来分别看下执行上下文中的组成。
this绑定
this值现在被叫做this绑定,虽然术语换了,但在语法角度上来说没有太大区别(除了在严格模式下,this值可以是undefined)。在全局上下文中,this绑定仍然是全局对象。
(function (global) { global.a = 10;})(this);console.log(a); // 10
在函数内部的执行上下文中,this值仍然是由函数被调用的方式所决定的。我们知道,存在一个引用类型(Reference type),而引用类型的base属性的值就是this的值,这在所有情况下都是用,无论是全局对象或者严格模式下值为undefined的this绑定。
var foo = { bar: function () { console.log(this); };};// --- Reference cases ---// with a referencefoo.bar(); // "this" is "foo" - the basevar bar = foo.bar;// with the referencebar(); // "this" is the global, implicit basethis.bar(); // the same, explicit base, the global// with also but another referencebar.prototype.constructor(); // "this" is "bar.prototype"// --- non-Reference cases ---(foo.bar = foo.bar)(); // "this" is "global" or "undefined"(foo.bar || foo.bar)(); // "this" is "global" or "undefined"(function () { this; })(); // "this" is "global" or "undefined"
注意,下面这种方式在严格模式下已无法获得全局对象:
(function () { "use strict"; var global = (function () { return this; })(); console.log(global); // undefined!})();
关于这部分的具体内容可以在严格模式(注二)一章中找到。
下面来到我们要讨论的变量环境组件和词法环境组件,关于这两部分内容经常会造成混淆,而且在一些文本中的描述也并不正确。
变量环境
变量环境是在进入执行上下文阶段时,用来储存变量声明和函数声明的初始值。这一点很像ES3中的变量对象VO/AO。
当进入函数的执行上下文时,创建一个特殊的激活对象AO来替代VO,通过函数的arguments属性初始化,用来储存函数的形参等。它由三部分组成:
- callee — 指向当前函数的引用;
- length — 真正传递的参数的个数;
- properties-indexes(字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)。 properties-indexes内部元素的个数等于arguments.length.
properties-indexes 的值和实际传递进来的参数之间是共享的(注意这一点)。
现在在严格模式下,激活对象已与过往不同。其实arguments属性不再与函数的实际形参之间共享,同时callee属性也被移除。
看下面代码:
function foo(a) { var b = 20;} foo(10);
下面是它的变量环境结构:
fooContext.VariableEnvironment = { environmentRecord: { arguments: {0: 10, length: 1, callee: foo}, a: 10, b: 20 }, outer: globalEnvironment};
那什么是词法环境呢?有意思的一点是,其词法环境组件和变量环境组件最初是同一个值,词法环境复制于变量环境,变量环境组件永远不会变,而词法环境组件可能会变。
词法环境
无论是变量环境还是词法环境都是属于词法环境的概念(先不用管这里名字上造成的困惑),都是在上下文中静态的(词法的)决定内部函数的外部绑定。
我们上面提到过,在初始化阶段(当上下文被激活),词法环境就是变量环境的一个副本,就以上面这个例子来看:
fooContext.LexicalEnvironment = copy(fooContext.VariableEnvironment);
它们的改变发生在代码执行阶段,与with语句和catch从句的词法环境扩张有关。
在上面讲过,with语句和catch从句会在代码执行阶段替代掉当前的执行环境。下面先看下讨论函数表达式。
我们知道,在函数创建阶段,会创建一个闭包来保存它创建时所在上下文的词法环境。如果一个函数表达式是创建在with语句(或catch从句)中,它同样应该保存当前的词法环境。
按照规则,用with创建的环境替代了当前变量环境后,当with执行完成,又会把当前环境恢复过来。然而这也意味着,此时函数表达式已无法访问到在with执行时创建的闭包内容,但函数表达式的确需要这些内容。
而且,实际上(with创建的环境)是不能替换掉当前的变量环境,因为函数声明同样可以在with语句中被调用,并且不同于函数表达式,函数声明应该继续使用它创建时绑定的初始状态值而不是来自with对象。
对此,我们要知道。函数声明会保存变量环境作为它[[scope]]属性的值。对于函数表达式,在这个例子中,函数表达式保存的是词法环境。这也是最重要,也是唯一点来区分这两个概念的原因。
而要是with语句完全的从ES中消失(下个版本的ES),或正如目前ES5的严格模式下,那么ES概念在这方面的困惑就会少很多。
再次说明,函数表达式(FE)保存的是词法环境,因为它需要动态绑定在with执行时创建的环境,而函数声明(FD)的保存的变量环境,因为它不能在块级作用域创建以及函数声明提升这一规则。通过下面这个例子来具体了解它们的内部过程:
var a = 10;// FDfunction foo() { console.log(a);}with ({a: 20}) { // FE var bar = function () { console.log(a); }; foo(); // 10!, from VariableEnvrionment bar(); // 20, from LexicalEnvrionment}foo(); // 10bar(); // still 20它的过程是这样:// "foo" is createdfoo.[[Scope]] = globalContext.[[VariableEnvironment]];// "with" is executedpreviousEnvironment = globalContext.[[LexicalEnvironment]];globalContext.[[LexicalEnvironment]] = { environmentRecord: {a: 20}, outer: previousEnvironment};// "bar" is createdbar.[[Scope]] = globalContext.[[LexicalEnvironment]];// "with" is completed, restore the environmentglobalContext.[[LexicalEnvironment]] = previousEnvironment;
为了更清楚的了解这个过程,我们来写些不怎么符合规范的代码,把函数声明移到块中,要记住这是个语法错误。但其实在今天所有的引擎实现都不会抛出错误,而是有一套自己的机制来管理这种情况。Firefox 就有一套非标准的扩展实现叫做函数语句(FS)。其他实现,比如google的V8,会有下面的表现。
var a = 10;with ({a: 20}) { // FD function foo() { // do not test in Firefox! console.log(a); } // FE var bar = function () { console.log(a); }; foo(); // 10!, from VariableEnvrionment bar(); // 20, from LexicalEnvrionment}foo(); // 10bar(); // still 20
这里同样是上面提到的情况,函数声明(这里存在声明提升)保存变量环境作为[[scope]]的值,而函数表达式保存的with执行时被替换后的那个词法环境,即with创建的环境。
注意:ES6中规范了块级函数声明,所以在上面这个例子中,foo也会采用词法环境,输出20。
虽然我们从抽象的角度讨论了两种环境,但并没有一个严格意义上的区分。所以我们可能会经常说执行上下文的词法环境。
然而,有一点需要注意,词法环境会参与标识符解析的过程。
原文链接
ECMA-262-5 in detail. Chapter 3.2. Lexical environments: ECMAScript implementation.
- ECMA-262-5 词法环境:ECMA实现(三)--- 执行上下文
- ECMA-262-5 词法环境:ECMA实现(一)--- 简介
- ECMA-262-5 词法环境:ECMA实现(二)--- 环境记录项
- ECMA-262-5 词法环境:ECMA实现(四)--- 标识符解析及其他
- ECMA-262-5 词法环境:通用理论(四)--- 环境
- ECMA-262-5 词法环境:通用理论(三)--- 标识符绑定
- ECMA-262-5 词法环境:通用理论(一)--- 作用域
- ECMA-262-5 词法环境:通用理论(二)--- 动态作用域
- V8引擎实现标准ECMA-262(三)
- V8引擎实现标准ECMA-262(三)
- [JavaScript]ECMA-262-3 深入解析.第一章.执行上下文
- ECMA
- V8引擎实现标准ECMA-262(一)
- V8引擎实现标准ECMA-262(二)
- !!ECMA-262 核心
- ECMA-262个人补充
- ECMA-262规范
- 看书 ecma-262
- Java解析txt文件中json数据到List<entity>,并存入数据库
- 12.4课堂笔记及作业
- Centos连接工具
- 链式二叉树的遍历C语言版
- JVM内存结构及内存分配策略
- ECMA-262-5 词法环境:ECMA实现(三)--- 执行上下文
- Android开发项目终止
- ACdream 1069
- 链式二叉树的遍历Java版
- python-scikit-learn-DBSCAN
- Python基本数据类型
- 虚拟机下安装CentOS 7.4教程
- windows(64位)下使用curl命令
- 各种依赖