Open RicoLiu opened 6 years ago
你好,感谢翻译了这篇文章,我这里有个疑惑可能需要你帮忙解答一下。 我在medium看到了原文,之后在这里和掘金上看到了相关译文,但是这两篇译文都和作者原文的表诉有点不同。 原文讲解执行上下文的创建阶段时,只提到词法环境和变量环境,并没有this绑定,this绑定根据作者的说法,实际上是包括在词法环境中。(词法环境包括:环境记录、外部环境引用、this绑定)。 但是在译文里,我发现执行上下文的创建阶段是:词法环境、变量环境、this绑定。所以想问下你是否有对原文进行了修改?如果没有的话,那就是作者后来重新对这个地方进行了修改,但是我疑惑的是这个改动的依据是什么?
你好,我刚看了一下原文,的确和我翻译的不一样。可能是作者对原文进行了修改? 我看到评论里有人也提出了这个问题:
@Chorer
我疑惑的是这个改动的依据是什么?
https://es5.github.io/#x10.2 http://www.ecma-international.org/ecma-262/6.0/index.html#table-23
好的,感谢解答。
好的,感谢解答。
看了下标准,所以这么说,本文翻译的是正确的,反而原文是错误的?
好的,感谢解答。
看了下标准,所以这么说,本文翻译的是正确的,反而原文是错误的?
oh,好吧,两个都看了,原来是 ES5 和 ES6 的区别
理解 JavaScript 中的执行上下文和执行栈
英文原文: https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0
如果你是或者打算成为一名 JavaScript 开发者,那么你必须知道 JavaScript 代码内部是如何执行的。理解执行上下文和执行栈对于理解其他的 JavaScript 概念(如:变量提升、作用域、闭包)是非常重要的。
正确的理解执行上下文和执行栈会让你成为更好的 JavaScript 开发者。
那么,让我们开始吧 :)
什么是执行上下文
简单的说,执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。每当运行任何 JavaScript 代码的时候,它都是运行在执行上下文中。
执行上下文的类别
在 JavaScript 中有三种执行上下文。
全局的执行上下文---这是一个默认的或者说是一个基本的执行上下文。不在任何函数内部的代码,那么就在全局的执行上下文中。它执行两件事情:1、它会创建一个全局的对象(在浏览器中是 window 对象);2、设置
this
的值为全局的对象。在一个程序中,有且只有一个全局的执行上下文。函数的执行上下文---每当调用一个函数,都会为该函数创建一个新的执行上下文。每一个函数拥有自己的执行上下文,但是在调用或者调用函数的时候会创建它。函数的执行上下文可以有任意数量。每当一个新的执行上下文被创建,它将会按照定义的顺利执行一系列的步骤,这一点会在稍后的文章中讨论。
Eval 函数执行上下文---在
Eval
函数内部的代码被执行时也会拥有它自己的执行上下文,但是对于 JavaScript 开发者来说,Eval
函数并不常用,所以我不会在这讨论它。执行栈
执行栈,在其他的编程语言中也称为「调用栈」,具有先进后出的数据结构,其作用是用来存储所有在代码执行时创建的执行上下文。
每当 JavaScript 引擎第一次遇到你写的脚本,它就会创建一个全局的执行上下文,并且将它压栈到当前的执行栈中。每当引擎发现函数调用,它就会为该函数创建一个新的执行上下文并将其压栈。
该引擎会按照栈中的顺序,依次从栈顶开始执行函数。当该函数执行完成,它的执行栈将从堆栈中弹出,并且执行顺序将会到达当前堆栈中它下面函数的上下文。
让我们看下面的代码:
每当上面的代码在浏览器中加载,JavaScript 引擎会创建一个全局的执行上下文,并将它压栈到当前的执行堆栈中。当调用
first()
函数是,JavaScript 引擎为该函数创建了一个新的执行上下文并将它压栈到当前的执行堆栈中。当
second()
函数在first()
函数内部被调用时,JavaScript 引擎为该函数创建了一个心得执行上下文,并将它压栈到了当前的执行堆栈中。当second()
函数运行结束时,它的执行栈将从当前堆栈中弹出,并且执行顺序会到达它下一个的执行上下文,也就是first()
函数的执行上下文。当
first()
函数运行结束时,它的执行堆栈将会从栈中移除,并且执行顺序会到达全局的执行上下文。一旦所有的代码都被执行了,JavaScript 引擎会从当前的堆栈中移除全局的执行上下文。执行上下文是如何创建的?
到目前为止,我们已经了解了 JavaScript 引擎是如何管理执行上下文的。现在,让我们来看看 JavaScript 引擎是如何创建执行上下文的。
执行上下文创建分两个步骤:1、创建阶段 和 2、执行阶段。
创建阶段
在任何 JavaScript 代码执行之前,执行环境经历了创建阶段,创建阶段包含以下三个事:
所以,执行上下文可以从概念上表示为如下:
This Binging:
在全局的执行上下文中,
this
的值指向的是全局的对象。(在浏览器中,this
指向的是 window 对象)在函数的执行上下文中,
this
的值取决于函数是如何被调用的。如果它是通过对象引用调用的,那么this
值指向那个对象,否则this
值指向全局的对象或者是undefined
(在严格模式下)。例如:Lexical Environment
官方的 ES6 文档定义如下:
简单来说,词法环境是一种包含标识符变量映射的结构。(这里的标识符指的是变量名或函数名,变量指的是实际的对象【包含函数类型对象】或原始值。)
在词法环境中,有两种组件:(1) environment record、(2) reference to the outer environment.
两种类型的词法环境:
两种类型的environment record:
简短点来说:
注意 --- 对于function environment,declarative environment record包含着一个
arguments
对象,该对象存储传递给函数的索引和参数之间的映射以及传递给函数的参数的长度。抽象地认为词法环境伪代码如下:
Variable Environment:
它也是一个词法环境,其 EnvironmentRecord 包含由此执行上下文中的 VariableStatements 创建的绑定。
如上文所提到的,变量环境也是一个词法环境,所以它有上文中提到的所有词法环境的属性。
在 ES6 中,LexicalEnvironment 和 VariableEnvironment 有一点不同,前者是用来存储函数声明和变量绑定(let & const),后者是用来只存储变量绑定(var).
让我们看如下的例子:
执行上下文如下:
注意 --- 仅在遇到函数
multiply
调用时才会创建函数执行上下文。你可能注意到了:
let
和const
定义的变量没有任何关联到他们的值,但是var
定义的变量被设置为undefined
.这是因为在创建阶段,进行代码扫描以获取变量和函数声明,函数声明存储在它的整个环境中,但是变量被初始化为
undefined
(var
定义的变量),或者保留未初始化(let
&const
定义的变量)。这就是为什么你能访问到
var
定义的未声明的变量(尽管是undefined
), 但是访问let
和const
定义的未声明的变量就会得到错误。这个就是我们所说的「变量提升」。
执行阶段
这是本篇文章中最简单的部分,在此阶段,完成对所有这些变量的分配,最后执行代码。
注意 --- 在执行阶段,如果 JavaScript 引擎不能够找到在源码中已经被声明过的
let
变量的值,那么它的值就会被赋为undefined
.结论
我们已经讨论了如何在内部执行 JavaScript 程序。虽然你没有必要将所有这些概念都理解为了成为一名出色的 JavaScript 开发人员,但对上述概念的理解将有助于您更轻松,更深入地理解其他概念,如变量提升,作用域和闭包。