其实规范这东西不是给人看的,它更多的是给语言实现者提供参考。可是当碰到问题找不到答案时,规范每每能提供想要的答案 。偶尔读一下可以带来很大的启发和思考,若是只读一章 Javascript 规范,大神们以为非第10章莫属。javascript
咱们来试试看,此次选用的是 ECMA2.2
的 5.1 版,整个规范才200页, 而第10章共10页,能够感觉到 Javascript
的精简,目前的版本加了太多 ES6 的东西,让人望而生畏。css
资料地址:http://www.ecma-international...html
阅读 ECMA262
5.1 第10章 Executable Code and Execution Contexts
(可执行代码与执行上下文)
你能针对这章内容提出问题吗? 即知道答案找出问题。
你能使用图来更形象地表达文章内容吗?html5
原汁原味 ECMAScript 5.1 英文版
平易近人 ECMAScript 5.1 中文版java
像
v8
等JavaScript
引擎都是按照ecma-262
的规范来实现的,JavaScript
引擎在解释JavaScript
代码时,将可执行代码分为了三种。分别是:git
全局代码github
代码加载时首先进入的环境。 例如加载外部的 JavaScript 文件或者本地 <script></script> 标签内的代码。 但不包括任何 function 体内的代码。
函数代码web
是指做为 function 被解析的源代码。 不包括做为其嵌套函数的 function 被解析的源代码。 由于 JavaScript 函数中还能够嵌套函数,所以这也是三种可执行代码中最复杂的一种。
eval
代码segmentfault
指的是传递给 eval 内置函数的代码。
注:不了解 eval(string)
的小伙伴,请参考 eval() - JavaScript | MDN浏览器
当
JavaScript
引擎开始执行(进入)一段可执行代码以后,会生成一个执行环境(Execution Context),或执行上下文。引擎用执行环境来维护执行当前代码所须要的变量声明、this指向等。
词法环境
是执行环境的三个组成的状态之一。
官方解释:
词法环境是用来定义特定变量和函数标识符的。一个词法环境由一个环境记录项和可能为空的外部词法环境引用构成。
一般词法环境会与
ECMAScript
代码诸如函数声明(FunctionDeclaration)
、WithStatement
或者TryStatement
的Catch
块这样的特定句法结构相联系,且相似代码每次执行都会有一个新的词法环境被建立出来。外部词法环境引用 用于表示词法环境的逻辑嵌套关系模型。(内部)词法环境的外部引用是逻辑上包含内部词法环境的词法环境。外部词法环境天然也可能有多个内部词法环境。
例如,若是一个 FunctionDeclaration 包含两个嵌套的 FunctionDeclaration,那么每一个内嵌函数的词法环境都是外部函数本次执行所产生的词法环境。
环境记录项 又能够分为两种声明式环境记录项和对象式环境记录项。
声明式环境记录项 用于标识标识符和函数声明
、变量声明
、catch 语句
等语法元素的绑定。对象式环境记录项 主要用于定义那些将标识符与具体对象的属性绑定的语法元素。
咬文嚼字,很差理解?
通俗点讲:
词法环境就是 JavaScript 引擎在执行代码过程当中用来标识函数声明、变量声明这一类的。咱们每次声明一个函数,或者使用 with
、catch
语句的时候,就会有新的词法环境被建立出来。全局词法环境的外部词法环境就是空的,由于他已是最外层的词法环境了。
咱们用个例子来讲明词法环境:
var x = 10; function foo(y){ var z = 30; function bar(q){ return x+y+z+q; } return bar; } var bar = foo(20); bar(40);
给出一个标识符字符串,首先在当前的词法环境内寻找,若是存在,返回引用的标识符字符串,若是不存在,再在当前词法环境的外部词法环境寻找。
咦,怎么感受和做用域链的概念很类似?他们有什么关系吗?
函数只要被建立,就会有本身的“地盘”,有本身的做用域。可是只有函数被执行的时候,才会有本身的执行环境。函数执行完毕的时候,执行环境就会退出。并且一个做用域下可能存在多个执行环境,好比闭包。
一、词法环境分为了两部分:环境记录项和外部词法环境。
二、环境记录项根据绑定的 ECMA 脚本元素的不一样也分为了两部分。
三、函数声明或者使用 with
、catch
语句时,就会有新的词法环境被建立出来。
若是咱们的 JavaScript
程序有各类函数,函数之间还有嵌套的状况,那 JavaScript
引擎怎么解释各类声明和执行上下文哪?
当控制器转入 ECMA
脚本的可执行代码时,上文已经说了有三种可执行代码,无论进入哪种控制器都会进入一个执行环境。多个执行环境在逻辑上造成一个栈结构。栈结构最顶层的执行环境称为当前运行的执行环境,最底层是全局执行环境。
用一张图解释
由于 JS 引擎被实现为单线程,也就是同一时间只能发生一件事情,其余的行为就会依次排队。
你能够有任意多个函数执行环境,每次调用函数建立一个新的执行环境,会建立一个私有做用域,函数内部声明的任何变量都不能在当前函数做用域外部直接访问。函数能访问当前执行环境外面的变量声明,但在外部执行环境不能访问内部的变量/函数声明。
关于执行栈(调用栈)
单线程。 同步执行。 一个全局上下文。 无限制函数上下文。 每次函数被调用建立新的执行上下文,包括调用本身。 return 或者抛出异常退出一个执行环境。
咱们用一个具体的函数理解:
function foo(i) { if (i < 0) return; console.log('begin:' + i); foo(i - 1); console.log('end:' + i); } foo(2); // 输出: // begin:2 // begin:1 // begin:0 // end:0 // end:1 // end:2
代码的执行流程进入内部函数,建立一个新的执行上下文并把它压入执行栈的顶部。浏览器总会执行位于栈顶的执行上下文,一旦当前上下文函数执行结束,它将被从栈顶弹出,并将上下文控制权交给当前的栈。 这样,堆栈中的上下文就会被依次执行而且弹出堆栈,直到回到全局的上下文。
执行环境包含全部用于追踪与其相关的代码的执行进度的状态。精确地说,每一个执行环境包含以下表列出的组件。
执行环境的三个状态
组件 | 做用目的 |
---|---|
词法环境 | 指定一个词法环境对象,用于解析该执行环境内的代码建立的标识符引用。 |
变量环境 | 指定一个词法环境对象,其环境数据用于保存由该执行环境内的代码经过 变量表达式 和 函数表达式 建立的绑定。 |
this 绑定 | 指定该执行环境内的 ECMA 脚本代码中 this 关键字所关联的值。 |
当建立一个执行环境时,其词法环境组件和变量环境组件最初是同一个值。在该执行环境相关联的代码的执行过程当中,变量环境组件永远不变,而词法环境组件有可能改变。变量环境的不变和词法环境的可能改变都是指引用的改变。
解释执行全局代码或使用
eval
函数输入的代码会建立并进入一个新的执行环境。每次调用ECMA
脚本代码定义的函数也会创建并进入一个新的执行环境,即使函数是自身递归调用的。每一次
return
都会退出一个执行环境。抛出异常也可退出一个或多个执行环境。当控制流进入一个执行环境时,会设置该执行环境的
this
绑定组件,定义变量环境和初始词法环境,并执行声明式绑定初始化过程。以上这些步骤的严格执行方式由进入的代码的类型决定。
执行如下步骤:
一、将变量环境设置为 全局环境 。 二、将词法环境设置为 全局环境 。 三、将 this 绑定设置为 全局对象 。 四、使用全局代码执行声明式绑定初始化化步骤。
当控制流根据一个函数对象 F、调用者提供的
thisArg
以及调用者提供的argumentList
,进入函数代码的执行环境时,执行如下步骤
若是函数代码是严格模式下的代码,设 this 绑定 为 thisArg。 不然若是 thisArg 是 null 或 undefined,则设 this 绑定 为全局对象。 不然若是 Type(thisArg) 的结果不为 Object,则设 this 绑定 为 ToObject(thisArg)。 不然设 this 绑定 为 thisArg。 以 F 的 [[Scope]] 内部属性为参数调用 NewDeclarativeEnvironment(新建声明式词法环境),并令 localEnv 为调用的结果。 设 词法环境组件 为 localEnv。 设 变量环境组件 为 localEnv。 令 code 为 F 的 [[Code]] 内部属性的值。 使用函数代码 code 和 argumentList 执行声明式绑定初始化化步骤。
咱们用伪代码表示一下:
if(是 严格模式) { this = thisArg } else if(thisArg === null || thisArg === undefined) { this = window } else if(typeof thisArg != 'object') { this = Object(thisArg) } else { this = thisArg }
哎,这里的 thisArg
指的是什么?上文说了 thisArg
来自于函数的调用者。
这里表明函数的 apply
,call
,bind
等设置 this
绑定的参数:
经过 call
或者 apply
调用函数时,thisArg
的值比较明显,为传入的第一个参数。
Function.prototype.apply (thisArg, argArray) Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )
固然 thisArg
还有其余的可能,具体的能够参考这篇文章 Javascript this 解析
对上面的伪代码作一下解释:
严格模式: 也就是说,在严格模式下,this
只能为 thisArg
,而当 thisArg
为 undefined
时,this
就是 undefined
,而不是 window
。
非严格模式: 若是 thisArg
是 null
(如 fun.call(null)
) 或 undefined
(直接调用函数),则 this
为全局对象,浏览器里就是 window
。
不然,若是 传入了 thisArg
, 但不是个对象,则把它转为对象,并赋给 this
,好比,当 fun.call('hhh')
时,打印 fun 内的 this
为
String {0: "h", 1: "h", 2: "h", length: 3, [[PrimitiveValue]]: "hhh"}
不然 ,也就是仅剩的一种状况,显式的传入了一个对象做为 thisArg
参数的状况下,设 this
绑定为 thisArg
。
每一个执行环境都有一个关联的 变量环境
。当在一个执行环境下评估一段 ECMA
脚本时,变量和函数定义会以绑定的形式添加到这个 变量环境
的环境记录中。对于函数代码,参数也一样会以绑定的形式添加到这个 变量环境
的环境记录中。
ECMAScript
代码的执行由运行环境来完成。不一样的运行环境可能采起不一样的执行方式,但基本的流程是相同的。如浏览器在解析 HTML
页面中遇到 <script>
元素时,会下载对应的代码来运行,或直接执行内嵌的代码。代码的基本执行方式是从上到下,顺序执行。在调用函数以后,代码的执行会进入一个执行上下文之中。因为在一个函数的执行过程当中会调用其余的函数,执行过程当中的活动执行上下文会造成一个堆栈结构。在栈顶的是当前正在执行的代码。当函数返回时,会退出当前的执行上下文,而回到以前的执行上下文中。若是代码执行中出现异常,则可能从多个执行上下文中退出。
在代码执行过程当中很重要的一步是标识符的解析。好比当执行过程当中遇到语句 alert(val)
时,首先要作的是解析标识符 val
的值。ECMAScript
不一样于 Java
和 C/C++
等语言,在进行标识符解析时须要利用词法环境并与函数调用方式相关。具体来讲,标识符解析由当前代码所对应的执行上下文来完成。为了描述标识符的解析过程,ECMAScript
规范中使用了词法环境的概念来进行描述。一个词法环境描述了标识符与变量或函数之间的对应关系。一个词法环境由两个部分组成:一部分是记录标识符与变量之间的绑定关系的环境记录,另外一部分是包围当前词法环境的外部词法环境。环境记录能够当作是一个标识符与变量或函数之间的映射表。不一样词法环境之间能够互相嵌套,而内部词法环境会持有一个包围它的外部词法环境的引用。在进行标识符解析时,若是当前词法环境中找不到标识符所对应的变量或函数,则使用外部词法环境来尝试解析。递归查找下去,直到解析成功或外部词法环境为 null
。
具体来讲,根据标识符关联方式的不一样,环境记录能够进一步分红两类。两种类型分别对应不一样的 ECMAScript
中不一样的语法结构。当使用这些语法结构时,会对环境记录中的内容产生影响,进而影响标识符的解析过程。第一类环境记录是声明式环境记录。顾名思义,声明式环境记录用来绑定 ECMAScript
代码中的变量声明。当使用 var
声明变量或使用相似 function func(){}
的形式声明函数时,对应的变量或函数会被绑定到相应的环境记录中。另外一类环境记录是对象环境记录。对象环境记录并不绑定具体的变量或函数,而是绑定另一个对象中的属性。对象环境变量主要用来描述 ECMAScript
中 with
操做符的行为。
每一个执行上下文会对应两个不一样的词法环境。一个是用来进行标识符解析的词法环境,可能随着代码的执行而发生变化;另一个是包含执行上下文对应的做用域中的变量或函数声明的词法环境。
读完这篇文章,问问本身,可以回答下面的问题吗?
一、ECMAScript 中可执行代码有几种?
二、什么状况下会建立一个执行环境?
三、什么状况下会退出一个执行环境?
四、做用域链和执行环境的关系?
五、执行环境的存在是为了解决什么?
六、词法环境和变量环境的异同?
七、this 的绑定的几种状况?
ES5/可执行代码与执行环境
深刻理解JavaScript系列(11):执行上下文(Execution Contexts)
了解JavaScript的执行上下文
深刻理解JavaScript执行上下文、函数堆栈、提高的概念
关于js做用域那些事
从 ECMAScript 规范来看 JS 的 this 绑定规则
深刻探讨 ECMAScript 规范