[学习笔记] JavaScript 理解Execution Context基本概念以及延伸

本篇内容参考连结

执行环境PJCHENder笔记

Execution Context

Definition

首先了解其定义, 当JS引擎执行一段程式码(script)时,便会创造执行环境(execution context). 并执行在下面三种环境之一

-全域 Gloabal : 预设当程式执行时的环境
-函式 Function : 当开始跑函式内部程式码时的环境
-Eval : 把一串字串当作指令来执行时的环境

用下方程式码来理解

// Global context, JS 最外层的程式码部分属于全域var greeting = "Hi";function person() {    // 从大括号开始到结束进入另外一个执行环境  var _firstName = "Marco";  var _lastName = "Polo";  function firstName() {    // 另外一个执行环境    return _firstName;  }    alert(greeting + firstName());}function otherPerson() {  var _firstName = "John";  var _lastName = "Doe";}

其中只有一个Global的context, 并代表最上层, 所有的其他的context都可以存取global的状态以及资料像是程式中的变数greeting. 接下来每一个function都会创造自己的context, 并拥有自己的範围以及状态.任何里面宣告的所有东西皆不能被外面直接存取.像是在otherPerson的执行环境下无法直接存取person得资料_firstName.

Execution Stack

浏览器的JS直译器通常式单执行续(Single Threaded)的意思代表一次只能做一件事, 当在执行其他任务的时候, 其他事件就会被丢在执行序列中等待执行, 而这个我们就称执行堆叠(execution Stack). 请参考以下程式来理解

 function b() { }  function a() { }  a();

首先Global Execution Context会被建立, 这时候所有的objects都会一併建立. 在程式开始执行的过程中会因为hoisting(稍后解释)的缘故, 会先被建立在记忆体上, 接着才会开始逐行执行程式.

接着, 会开始执行a()的部分, 然后会建立a的execution context,并放置到execution stack里面. 这个execution stack中, 最上面的context会是正在执行的.
alt text

在function a的context建立后, 便会开始执行里面的内容. 由于里面会直接到function b, 所以再一次的, 建立的function b的context, 并且stack的最上层, 变成了 function b的 execution context.
alt text

当function b执行完后就会从stack离开,然后再继续逐行执行funciton a, 然后当执行完毕也依样会从stack抽离, 最后到global execution context逐行执行.
alt text

Hoisting

一般在写程式, 都会先定义好变项, 然后才会去使用他, 在尚未定义的情况下直接去使用这个变项, 通常会出现错误讯息. 在JS中有个蛮特别的概念就是Hoisting.

w3schools 的定义 : Hoisting is JavaScript's default behavior of moving declarations to the top

在JS中, 会把定义好的变项移到最前面执行, 但实际上并不是真的改变程式的顺序, 而是在Compilation phase时, 先把所有定义项(关键字如下)储存在记忆体里面.

let
const
class
var
function

参考下面的例子

 b();   console.log(a);  var a = "Hello";  function b() {  console.log('Called Function B'); }

执行的结果会是

Called Function B
undefined

结果并没有任何错误, 首先会先把 var a 和 function b移到最前执行, 这些宣告(declare)的变项都存在记忆体中, 但是a的值("Hello")并没有存进去, 这使得a的值得到了 undefined. 更精确的说法是, 在定义变项的过程中可分为宣告(declaration)和给值(initialization)的两个过程. 只有declaration会在逐行执行之前先被执行并储存在记忆体中(hoisted);给值则是在hoisted后逐行执行程式时才会被执行到.

w3schools 的定义 :
JavaScript Declarations are Hoisted
JavaScript Initializations are Not Hoisted

此外, 假如把宣告a的那行注解掉, 就会得到错误 a is not defined.

Hoisting - let, const and class

let, const 和 class实际上也会跟function跟var一样会做Hoisting. 比较不一样的是他们的hoisting会在初始化时(execution phase)才会发生. 来比较下面两个程式

console.log(name) // undefinedvar name = "Andrew";
console.log(name); // Uncaught ReferenceError: name is not definedlet name = "Andrew";

var在编译阶段就被提升, 所以在执行阶段就会得到已经初始化的undefined, let并没有在编译阶段做提升, 所以会出现错误, 并要等到执行到let name = "Andrew";时, name才会被提升

Variable in Execution Context

首先, 先来看这段程式

function b() {  var myVar  console.log(myVar);}function a() {  var myVar = 2;  b();  console.log(myVar);}var myVar = 1;console.log(myVar);a();

得到的结果是 :

1
undefined
2

从建立的逻辑一步一步解释, 首先Global Execution Context会被建立, 宣告项 function a, function b和var myVar会被执行并存在记忆体, 之后开始逐行执行.

var myVar = 1; 会使在Global的myVar值为1,

console.log(myVar); 会输出Global context的值
alt text

a(); 开始建立function a的execution context, 并放进execution stack做堆叠. 然后开始逐行执行

var myVar = 2; 因为是在function a里面的context, 所以不会影响到Global并使值为2
alt text

b(); 执行到function b, 开始建立function b的execution context, 并在execution stack中再次堆叠然后逐行执行

var myVar; 在function b的context中仅有宣告, 并未给予任何值

console.log(myVar); 因为上一行未给值, 所以这边会输出 undefined, 并执行完function b, 然后抽离Execution stack
alt text

-console.log(myVar); 会输出在function a的myVar值 2,并执行完和抽离

最后就回到Global execution context.
alt text

由上面的程式可知, 在不同的execution context宣告同一个变项, 彼此之间是不会影响的. 虽然是同一个名字, 但实际上是三个变项.

补充
在上方程式中, 函式没有重新宣告变项直接取用,JS Engine在自己的execution context找不到该变项, 则会往他的外层(outer lexical environment)去寻找. 假如上方把function b里面的宣告注解掉则会得到不一样的结果.

function b() {  //var myVar  console.log(myVar);}function a() {  var myVar = 2;  b();  console.log(myVar);}var myVar = 1;console.log(myVar);a();

得到的结果是 :

1
1
2

Summarize the Execution Context

Compilation Phase : 当function被呼叫了, 在开始执行之前建立一个scope chain建立变数, function, 和参数设定this的值

参考andyyou的流程模拟流程

寻找呼叫 function 的程式码在执行 function 之前建立 执行环境进入 建立阶段初始化 scope chain建立 variable object:建立 arguments object 检查执行环境的参数,初始化参数的名称,值以及建立参考扫描 function 的宣告根据找到的每一个 function 在 variable object 建立,在这边其实就是建立 function 名称在记忆体中的参考指标如果 function 名称已经存在那么指标就会被覆写扫描执行环境里的变数每一个变数的宣告都会被加入 variable object 的属性中,并且初始化为 undefined,注意在这个阶段并不会赋值如果变数名称存在就略过,继续处理下一个变数判断决定 this 的值
Executing Phase : 执行阶段, 赋值, 逐行执行

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章