词法环境

词法作用域

词法作用域(lexcical scope)。即JavaScript变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码。

词法环境

用于定义特定变量和函数标识符在ECMAScript代码的词法嵌套结构上的关联关系, 一个词法环境由一个环境记录项和可能为空的外部词法环境引用构成

词法环境 = 词法环境记录项 + 外部词法环境
外部词法环境是包含内部词法环境的词法环境, 外部词法环境可能有多个内部词法环境

环境记录项 = 声明式环境记录项 || 对象式环境记录项

执行环境

  • javascript引擎在执行每个函数实例时,都会创建一个执行环境(execution context)

  • 执行环境中包含一个调用对象(call object), 调用对象是一个scriptObject结构(scriptObject是与函数相关的一套静态系统,与函数实例的生命周期保持一致),用来保存内部变量表varDecls、内嵌函数表funDecls、父级引用列表upvalue等语法分析结构。 varDecls和funDecls等信息是在语法分析阶段就已经得到,并保存在语法树中

  • 函数实例执行时,会将这些信息从语法树复制到scriptObject上

Executable Code and Execution contents

“执行上下文”可以看做当前代码的运行环境或者作用域。

Types of Executable Code

  • Global Code:全局级别的代码 – 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。

  • Function Code: 函数级别的代码 – 当执行一个函数时,运行函数体中的代码。

  • Eval Code: 在Eval函数内运行的代码,在特定的一次对 eval 的调用过程中,eval 代码作为该程序的 Global Code 部分。

每当调用执行一个函数时,引擎就会自动新建出一个函数上下文, 函数中函数也可能调用另一个函数,这样又创建一个执行环境, 也被称为上下文堆栈

执行上下文堆栈

  • ECMAScript的程序执行都可以看做是一个执行上下文堆栈[execution context (EC) stack]。堆栈的顶部就是处于激活状态的上下文, 堆栈最底部即为全局执行上下文环境[global execution context];

  • 激活其它上下文的某个上下文被称为 调用者(caller) 。被激活的上下文被称为被调用者(callee) 。被调用者同时也可能是调用者(比如一个在全局上下文中被调用的函数调用某些自身的内部方法)。

  • 当一个caller激活了一个callee,那么这个caller就会暂停它自身的执行,然后将控制权交给这个callee. 于是这个callee被放入堆栈,称为进行中的上下文[running/active execution context]. 当这个callee的上下文结束之后,会把控制权再次交给它的caller,然后caller会在刚才暂停的地方继续执行。在这个caller结束之后,会继续触发其他的上下文。一个callee可以用返回(return)或者抛出异常(exception)来结束自身的上下文。

执行上下文的建立过程

每当调用一个函数时,一个新的执行上下文就会被创建出来。然而,在javascript引擎内部,这个上下文的创建过程具体分为两个阶段:

  1. 建立阶段(发生在当调用一个函数时,但是在执行函数体内的具体代码以前)

    • 建立变量,函数,arguments对象,参数
    • 建立作用域链
    • 确定this的值
  2. 代码执行阶段:

    • 变量赋值,函数引用,执行其它代码

实际上,可以把执行上下文看做一个对象,其下包含了以上3个属性:

executionContextObj = {
variableObject: { /* 函数中的arguments对象, 参数, 内部的变量以及函数声明 */ },
scopeChain: { /* variableObject 以及所有父执行上下文中的variableObject */ },
this: {}
}

变量对象(variable object)

变量对象(缩写为VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:

  1. 变量 (var, 变量声明)
  2. 函数声明 (FunctionDeclaration, 缩写为FD);
  3. 函数的形参 注: 只有全局上下文的变量对象允许通过VO的属性名称来间接访问

不同执行上下文中的变量对象

对于所有类型的执行上下文来说,变量对象的一些操作(如变量初始化)和行为都是共通的。从这个角度来看,把变量对象作为抽象的基本事物来理解更为容易。同样在函数上下文中也定义和变量对象相关的额外内容。

抽象变量对象VO (变量初始化过程的一般行为)

  1. 全局上下文变量对象GlobalContextVO

    (VO === this === global), VO:

    • 所有函数声明(FunctionDeclaration, FD)
    • 所有变量声明(var, VariableDeclaration)
  2. 函数上下文变量对象FunctionContextVO

    (VO === AO, 并且添加了arguments和形参), AO:

    • 普通参数(formal parameters) 与特殊参数(arguments)对象
    • 所有函数声明(FunctionDeclaration, FD)
    • 所有变量声明(var, VariableDeclaration)
  3. eval上下文

    • eval会使用全局变量对象或调用者的变量对象(eval的调用来源)
    • 变量声明在顺序上跟在函数声明和形式参数声明之后,但不会干扰AO中已经存在的同名函数声明或形式参数声明
    • (function x() {}); 类似这样的函数表达式并不会影响AO
    • 不管是使用var关键字(在全局上下文)还是不使用var关键字(在任何地方),都可以声明一个变量”。 请记住,这是错误的概念; 任何时候,变量只能通过使用var关键字才能声明。

变量的特性

  1. 变量有一个特性(attribute):{DontDelete},这个特性的含义就是不能用delete操作符直接删除变量属性
  2. eval上下文,变量没有{DontDelete}特性, 使用一些调试工具(例如:Firebug)的控制台测试该实例时,请注意,Firebug同样是使用eval来执行控制台里你的代码。因此,变量属性同样没有{DontDelete}特性,可以被删除。

作用域

javascript变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,编译器通过静态分析就能确定,因此词法作用域也叫做静态作用域(static scope)。但需要注意,with和eval的语义无法仅通过静态技术实现,所以只能说javascript的作用域机制非常接近词法作用域(lexical scope)

作用域链

在ECMAScript中,会用到内部函数[inner functions],在这些内部函数中,我们可能会引用它的父函数变量,或者全局的变量。我们把这些变量对象成为上下文作用域对象[scope object of the context]. 类似于上面讨论的原型链[prototype chain],我们在这里称为作用域链[scope chain]

作用域链与一个执行上下文相关, 用于在标识符解析中变量查找。 标示符[Identifiers]可以理解为变量名称、函数声明和普通参数

函数上下文的作用域链在函数调用时创建的,包含活动对象和这个函数内部的[[scope]]属性。其scope定义如下:Scope = AO + [[Scope]]

函数在被创建时保存外部作用域,是因为这个 被保存的作用域链(saved scope chain) 将会在未来的函数调用中用于变量查找。这种形式的作用域称为静态作用域[static/lexical scope]

在上下文中示意如下:

activeExecutionContext = {
VO: {...}, // or AO
this: thisValue,
Scope: [ // Scope chain
// 所有变量对象的列表
// for identifiers lookup
]
};
var x = 10;

function foo() {
alert(x);
} (function () {
var x = 20;
foo(); // 10, but not 20
})();

  

在标识符解析过程中,使用函数创建时定义的词法作用域--变量解析为10,而不是20。此外,这个例子也清晰的表明,一个函数(这个例子中为从函数“foo”返回的匿名函数)的[[scope]]持续存在,即使是在函数创建的作用域已经完成之后。

补充说明

  1. 通过构造函数创建的函数的[[scope]]属性总是唯一的全局对象

  2. 在代码执行阶段有两个声明能修改作用域链。这就是with声明和catch语句

  3. 在代码执行过程中,如果使用with或者catch语句就会改变作用域链。而这些对象都是一些简单对象,他们也会有原型链。这样的话,作用域链会从两个维度来搜寻。

  4. 在解释执行阶段, 遇到变量需要解析时,会首先从当前执行环境的活动对象中查找, 如果没有找到而且该执行环境拥有者有prototype属性时, 则会从prototype链中查找, 否则将会按照作用域链查找;

end!

EC+VO+SCOPE for ES3的更多相关文章

  1. Execution Context(EC) in ECMAScript

    参考资料 执行环境,作用域理解 深入理解JavaScript系列(2):揭秘命名函数表达式 深入理解JavaScript系列(12):变量对象(Variable Object) 深入理解JavaScr ...

  2. Scope Chain(作用域链)

    本章,我们讨论一下ECMAScript中的作用域链 , 开门见山. 什么是作用域链 i.ECMAScript是允许创建内部函数的,甚至能从父函数中返回这些函数.作用域链正是内部上下文中所有变量对象(及 ...

  3. 深入理解JavaScript系列(14):作用域链(Scope Chain)

    前言 在第12章关于变量对象的描述中,我们已经知道一个执行上下文 的数据(变量.函数声明和函数的形参)作为属性存储在变量对象中. 同时我们也知道变量对象在每次进入上下文时创建,并填入初始值,值的更新出 ...

  4. JavaScript内部原理实践——真的懂JavaScript吗?(转)

    通过翻译了Dmitry A.Soshnikov的关于ECMAScript-262-3 JavaScript内部原理的文章, 从理论角度对JavaScript中部分特性的内部工作机制有了一定的了解. 但 ...

  5. javascript 执行环境,变量对象,作用域链

    前言 这几天在看<javascript高级程序设计>,看到执行环境和作用域链的时候,就有些模糊了.书中还是讲的不够具体. 通过上网查资料,特来总结,以备回顾和修正. 要讲的依次为: EC( ...

  6. javascript 作用域链

    最近想整理一下js执行代码的一些知识,如果有出错的地方还请指正. 执行环境(Execution Context) 所有的javascript代码都是在一个执行环境中被执行的.它只是一种机制,用来完成运 ...

  7. javascript 之变量对象-09

    变量对象 变量对象:每个执行环境(执行上下文)都有一个对应的变量对象(variable object),环境中(执行上下文中)定义的所有变量.函数都保存在这个对象中. 在上篇中说到,当执行流执行一个函 ...

  8. javascript 之作用域链-10

    前言 在<执行环境>文中说到,当JavaScript代码执行一段可执行代码时,会创建对应的执行上下文(execution context). 变量对象(Variable object,VO ...

  9. 你不知道的JavaScript--Item19 执行上下文(execution context)

    在这篇文章里,我将深入研究JavaScript中最基本的部分--执行上下文(execution context).读完本文后,你应该清楚了解释器做了什么,为什么函数和变量能在声明前使用以及他们的值是如 ...

随机推荐

  1. find 命令的误差估值与单位调整

    一.命令简介 find 命令的 -size 参数 单位b(不是byte而是block).c.w.k.M.G.默认是单位b ,也就是1block = 512byte = 0.5kb (文件系统ext4) ...

  2. DCL的失效:现实与初衷的背离

    最近看了Brian Goetz写的一篇有关DCL的文章:Double-checked locking: Clever, but broken.( 2001年发表于JavaWorld上) 这篇文章讲述了 ...

  3. MyBatis中批量插入数据对插入记录数的限制

    <基于Mybatis框架的批量数据插入的性能问题的探讨>(作者:魏静敏 刘欢杰 来源:<计算机光盘软件与应用> 2013 年第 19 期)中提到批量插入的记录数不能超过1000 ...

  4. Linux下jira自启动设置

    jira 的启动主要依靠的是bin目录下的catalina.sh脚本,提供了如init脚本的start,stop等参数----------------------------------------- ...

  5. python3 python2 import 的区别

    https://stackoverflow.com/questions/12172791/changes-in-import-statement-python3

  6. java inputstream to string

    https://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string 过千赞的答案

  7. mysql 编写存储过程

    先看例子: 1.delimiter $$2.drop procedure if exists`test_procedure` $$3.create procedure test_procedure(I ...

  8. bootstrap 导航栏鼠标悬停显示下拉菜单

    在jsp中加入一下代码: .navbar .nav > li:hover .dropdown-menu { display: block;} 全部代码如下所示: <%@ page lang ...

  9. 第一本的java 的小总结

    1.Java常见的注释有哪些,语法是怎样的? 1)单行注释用//表示,编译器看到//会忽略该行//后的所文本  2)多行注释/* */表示,编译器看到/*时会搜索接下来的*/,忽略掉/* */之间的文 ...

  10. JS文本框每隔4个数字加一个空格,银行卡号文本框

    <input type="text" onkeypress="return (function(key,that){return (key>47&&a ...