原文

ECMA-262-5 in detail. Chapter 3.2. Lexical environments: ECMAScript implementation.

简介

在之前的3.1章。我们讨论了词法环境的整体理论。我们还特别讨论了与之相关的静态作用域(static scope)和闭包(closures)。我们还提到ECMAScript所采用的链式环境帧模型(the model of chained environment frames)。在这一章,我们将用ECMAScript去实现词法环境(lexical environments)。我们要关注实现过程中的结构和术语是如何体现这个普遍的理论。我们先从定义开始。尽管之前我们已经给出了词法环境在普遍理论中的定义,在这里我们给出在ECMA-262-5中标准的定义。

定义

在我们之前提到的理论中,环境是用来管理嵌套的代码块中的数据,例如变量,函数等等。在ECMAScript中也是如此。
在ECMAScript的代码中,词法环境依据词法上嵌套的结构来确定标识符与变量值和函数的关联。我们也提到过这种名称与值的关联叫做绑定。在ECMAScript中,词法环境由两部分构成:一条环境记录(environment)和一个指向外部环境的引用。环境的定义与我们之前讨论的模型中的(frame)相对应。因此,一条环境记录记录这个词法环境中创建的标识符的绑定。换句话说,一条环境记录保存了出现在上下文中的变量。
考虑下面这个例子

var x = 10;
function foo() {var y = 20;
}

于是我们有了两个抽象的环境,分别对应中全局上下文和foo函数的上下文:

// environment of the global contextglobalEnvironment = {environmentRecord: {// built-ins:Object: function,Array: function,// etc ...// our bindings:x: 10},outer: null // no parent environment
}// environment of the "foo" functionfooEnvironment of the "foo" functionfooEnvironment = {environmentRecord: {y: 20},outer: globalEnvironment
}

outer引用是用来链接当前环境和父环境的。父环境当然也有自己的outer链接。全局环境的外部链接被设为null。全局环境是作用域链(chain of scopes)的终点。这让人想起原型继承是如何在ECMAScript中工作的。如果在对象本身上没有发现属性,就会去查找该对象的原型,若没有就是原型的原型,直到原型连的终点。环境和这个一样,上下文中出现的变量或标识符代表属性,外部链接代表指向原型的引用。一个词法环境可能包裹多个内部的词法环境。例如,一个函数内部有两个函数,那么内部的函数的词法环境的外部环境就是包裹它们的函数。

function foo() {var x = 10;function bar() {var y = 20;console.log(x + y); // 30}function baz() {var z = 30;console.log(x + z); // 40}
}// ----- Environments -----// "foo" environmentfooEnvironment = {environmentRecord: {x: 10},outer: globalEnvironment
};// both "bar" and "baz" have the same outer
// environment -- the environment of "foo"barEnvironment = {environmentRecord: {y: 20},outer: fooEnvironment
};bazEnvironment = {environmentRecord: {z: 30},outer: fooEnvironment
}

环境记录类型

ECMAScript定义了两种环境记录类型:声明式环境记录(declarative environment records)和对象环境记录(object environment records)

声明式环境记录

声明式环境记录是用来处理函数作用域中出现的变量,函数,形参等。例如

// all: "a", "b" and "c"
// bindings are bindings of
// a declarative recordfunction foo(a) {var b = 10;function c() {}
}

在大多数场合中,声明记录保存绑定被认为是在底层实现的。这是和ES3中活动对象概念的主要的不同。换句话说,不要求声明记录被当作一个普通对象的方式来实现,那样很低效。这意味着声明式环境记录并被直接暴露给用户,我们无权访问这些绑定,即记录的属性。实际上,我们以前也不可以,即使在ES3中,我们也无法直接访问活动对象。潜在的,声明式记录允许采用词法地址技术(lexical addressing technique),这能够直接去访问需要的变量,而不用去作用域链上查找,无论作用域嵌套的有多深。ES5的标准文档里并没有直接提到这个事实。我们要用声明式环境记录替换旧的活动对象的概念,它们的实现效率就不一样。Brendan Eich也提到

the activation object implementation in ES3 was just “a bug”: “I will note that there are some real improvements in ES5, in particular to Chapter 10 which now uses declarative binding environments. ES1-3’s abuse of objects for scopes (again I’m to blame for doing so in JS in 1995, economizing on objects needed to implement the language in a big hurry) was a bug, not a feature”.

一条声明式环境记录可以这样表现

environment = {// storageenvironmentRecord: {type: "declarative",// storage},// reference to the parent environmentouter: <...>
};

对象环境记录

相比之下,对象环境记录是用来确定全局环境和with声明中出现的变量和函数的。它们被当作普通对象来实现,效率低。在这样的上下文中,用来存储绑定的对象叫绑定对象(binding object)。在全局环境下,变量被绑定来全局对象上。

var a = 10;
console.log(a); // 10// "this" in the global context
// is the global object itself
console.log(this.a); // 10

一条对象环境记录可以这样表现

environment = {// storageenvironmentRecord: {type: "object",bindingObject: {// storage}},// reference to the parent environmentouter: <...>
};

执行环境的结构

在这里,我们简单介绍下ES5中执行上下文的结构。与ES3有些不同,它有以下属性:

ExecutionContextES5 = {ThisBinding: <this value>,VariableEnvironment: { ... },LexicalEnvironment: { ... },
}

this绑定

在全局环境中,this仍然是全局对象本身

(function (global) {global.a = 10;
})(this);console.log(a); // 10

在环境对象中,this仍然取决于函数是怎样被调用的。如果被引用调用(called with a reference), 那么这个引用的所有者(the base value of the reference)就是这个this

var foo = {bar: function () {console.log(this);}
};// --- Reference cases ---// with a reference
foo.bar(); // "this" is "foo" - the basevar bar = foo.bar;// with the reference
bar(); // "this" is the global, implicit base
this.bar(); // the same, explicit base, the global// with also but another reference
bar.prototype.constructor(); // "this" is "bar.prototype"

变量环境

变量环境就是存储上下文中的变量和函数的。当我们进入一个函数环境中,arguments对象就被创建,保存来形参的值。

function foo(a) {var b = 20;
}foo(10);

它的变量环境

fooContext.VariableEnvironment = {environmentRecord: {arguments: {0: 10, length: 1, callee: foo},a: 10,b: 20},outer: globalEnvironment
};

词法环境

[译者注:额,变量环境的拷贝,与with有关,不译了]
闭包保存来创造它的上下文的词法环境。

标识符解析

标识符解析是依据词法环境决定上下文中标识符的绑定。换句话说,它就是作用域的查找。与上文中提到的原型链类似。

var a = 10;(function foo() {var b = 20;(function bar() {var c = 30;console.log(a + b + c); // 60})();})();

解析a的过程如下

function resolveIdentifier(lexicalEnvironment, identifier) {// if it's the final link, and we didn't find// anything, we have a case of a reference errorif (lexicalEnvironment == null) {throw ReferenceError(identifier + " is not defined");}// return the binding (reference) if it exists;// later we'll be able to get the value from the referenceif (lexicalEnvironment.hasBinding(identifier)) {return new Reference(lexicalEnvironment, identifier);}// else try to find in the parent scope,// recursively analyzing the outer environmentreturn resolveIdentifier(lexicalEnvironment.outer, identifier);}resolveIdentifier(bar.[[LexicalEnvironment]], "a") ->-- bar.[[LexicalEnvironment]] - not found,
-- bar.[[LexicalEnvironment]].outer (i.e. foo.[[LexicalEnvironment]]) -> not found
-- bar.[[LexicalEnvironment]].outer.outer -> found reference, value 10

Lexical environments: ECMAScript implementation相关推荐

  1. JS深入--词法作用域、执行上下文与闭包

    文章目录 词法作用域 执行上下文与词法环境 闭包 闭包练习 作用域链 REF   个人博客文章同步地址 词法作用域   JS 使用的是词法作用域(或称为静态作用域),函数的作用域在定义的时候就决定了, ...

  2. 深入理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)

    http://www.cnblogs.com/TomXu/archive/2012/01/12/2308594.html 本篇是ECMA-262-3 in detail系列的一个概述(本人后续会翻译整 ...

  3. 【译】JavaScript 核心(第二版)

    原文:JavaScript. The Core: 2nd Edition 作者:Dmitry Soshnikov 文章其他语言版本:俄语 这篇文章是 JavaScript. The Core 演讲的第 ...

  4. js变量后面加问号是什么_js没那么简单(1)-- 执行上下文

    前言 我为什么写这个文章?也许换个耳熟能详的话题会有更多人看吧.之前发了个tls感觉阅读量不行. 要讲ecma语法吗?我觉得还是不了吧,毕竟这些繁琐,枯燥,而且门槛低. 那讲什么好?讲一点我自己觉得大 ...

  5. WEB前端学习二 JS作用域和作用域链

    先上三段说明作用域的代码 ? 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 28 29 30 31 3 ...

  6. ES6: 参数默认值及中间域

    下午看了一章 ECMA-262 by Dmitry Soshnikov, 现在稍稍来小结下ES6中的参数默认值以及由此产生的参数中间作用域. 原文地址: http://dmitrysoshnikov. ...

  7. Js核心技术——待补充

    首页新闻博问闪存招聘园子 [登录·注册] 知识库专题.NET技术Web前端软件设计手机开发软件工程程序人生项目管理数据库最新文章 您的位置:知识库 » Web前端 [JavaScript] JavaS ...

  8. 犀牛书第七版学习笔记:执行上下文与作用域

    目录 1.执行上下文 2.作用域链 3.作用域链增强Scope Chain Augmentation 4.变量声明 Variable Declaration 4.1使用 var 的函数作用域声明 Fu ...

  9. 【前端圭臬】十:从规范看 JavaScript 执行上下文(上)

    前言 接下来我们来啃一个硬骨头,JavaScript 的 执行上下文(Execution Context). 与执行上下文相关的知识有很多弯弯绕绕,不过没关系,我们只需要以两个主要问题为线索展开.第一 ...

  10. 读懂 ECMA 规格

    一般我们都不关心 ECMA 规范,只需要学习怎么使用就好了.但有时候遇到一些难以解释的问题/现象,就不得不看一下规范是如何要求实现的了.规范内容庞杂,理解其中的术语有利于我们快速看懂规范. Envir ...

最新文章

  1. 用python画猫咪怎么画-Python海龟画图工具绘制叮当猫程序
  2. 数据驱动:这是一种文化
  3. MyListUtil.java list工具类
  4. facenet训练自己的数据_基于SSD与Facenet的人脸识别
  5. 宝塔php漏洞,[安全预警]关于最近宝塔闹得很厉害的PMA漏洞BUG
  6. Div+CSS布局入门教程(五) 页面制作-用好border和clear 附加:1.DIV+CSS设计原则 2.DIV+CSS中标签ul ol li dl dt dd用法
  7. python中int转换为时间戳_python日期和时间戳互相转化操作详解
  8. 多线程的那点儿事(之嵌套锁)
  9. INSERT INTO SELECT语句概述和示例
  10. python登录交换机执行命令_利用Python脚本登录交换机实现自动配置备份的方法
  11. 数论基础——素数判断约数枚举整数分解(模板)
  12. N、NP、NPC问题分析总结
  13. num =10在c语言中是什么意思,num是什么词性
  14. python电影推荐系统_电影推荐系统---协同过滤算法(SVD,NMF)
  15. 三国志战略版:官渡之战_新阵容解读_曹操
  16. Android Gradle Plugins系列-02-Maven Publish 插件踩坑指南
  17. cocos实现PC端鼠标指针更换功能
  18. oauth2单点登录总结
  19. java 导出本地xml文件_java导出xml文件
  20. EAP-TLS/EAP-TTLS/EAP-PEAP

热门文章

  1. 用《内网穿山甲》共享内网中的远程桌面服务
  2. 关于软件开发的个人体会
  3. 公司居然使用监听设备,大家来讨论下IT公司应该怎样管理
  4. 数据结构(树状结构-树)
  5. 搭建本地LNMP开发环境(6)-配置nginx和PHP
  6. 为什么所有浏览器的userAgent都带Mozilla
  7. 学习笔记:利用GeoServer结合uDig发布WMS详细过程
  8. 数学到底有多重要?网友:道理都懂,实力不允许啊…
  9. 精选|2018年8月R新包推荐
  10. 统计挖掘那些事(八)—— 分层抽样与交叉验证