咱们一般将 JavaScript 归类为动态或解释执行语言,但实际上它也是一门编译语言,它有本身的编译器形式,运行在 JavaScript 引擎
中。web
每一个 Web 浏览器都有本身的 JavaScript 引擎形式:Chrome 有 V8
,Mozilla 有 SpiderMonkey
等。这些 JavaScript 引擎的共同点都是将 JavaScript 代码转换为编译器能够理解的语言,而后执行它。浏览器
当 JavaScript 代码运行的时候,运行 JavaScript 代码的环境造成了执行上下文 ,执行上下文决定代码能够访问哪些变量、函数、对象等。微信
咱们将执行上下文简单视为运行当前代码的 environment / scope
,咱们知道做用域分为 global scope
和 local scope
。app
相似的,执行上下文也分为不一样的类型:ide
全局执行上下文 - 代码首次执行时候的默认环境,在代码的整个执行过程当中,只用一个全局执行上下文。函数
函数执行上下文 - 每当执行流程进入到一个函数体内部的时候,就会建立一个函数执行上下文,能够有任意数量的函数执行上下文。this
JavaScript 是单线程的,浏览器只分配给 JavaScript 一个主线程,一次只能执行一个任务(函数),所以它在执行栈中对其余操做(事件和函数执行)造成一个任务队列,排队等候执行。spa
每当在浏览器中加载脚本时,栈 stack
中的第一个元素就是全局执行上下文
。当有函数执行时,将建立一个函数执行上下文
,并将其置于全局执行上下文
之上。一旦函数执行完成,它就会从执行堆栈中弹出,并将控制权交给它下面的上下文中。结合上面说到的,咱们看一个例子:线程
var name = "global variable"; console.log(name) function func1() { console.log("func1 被调用了。") func2(); } function func2() { console.log("func2 被调用了。"); } func1();
当上述代码在浏览器中加载时:指针
global execution context
并将其推送到当前执行栈。func1()
被调用,而后 Javascript 引擎为该函数建立一个新的函数执行上下文 function execution context
并将其推送到全局执行上下文的顶部。func1()
过程当中,发现 func2()
被调用,Javascript 引擎为该函数建立一个新的执行上下文,并将其推送到 func1()
执行上下文的顶部。func2()
函数完成时,其执行上下文从当前堆栈弹出,将控制权交给其下面的执行上下文,即 func1()
函数执行上下文。func1()
完成后,其执行堆栈将从堆栈中删除,将控制权交给全局执行上下文。一旦执行了全部代码,JavaScript 引擎就会从当前堆栈中删除全局执行上下文。执行上下文主要有两个阶段:建立阶段
和执行阶段
,接下来咱们将逐一进行介绍。
在函数执行发生以前,JavaScript 引擎会作以下事情:
scope chain
。做用链告诉执行上下文它应该包含什么,以及它应该在哪里查找解析函数的引用和变量的值。(对于全局环境,外部环境为 null。在全局做用域内的全部环境都将把全局环境做为其外部环境)。环境存储器
,其中全局上下文
的建立和引用(Web浏览器中的窗口),变量、函数和函数参数的建立和引用在内存
中完成。this
关键字的值(对于全局执行上下文,this 指向 window)。咱们能够将建立阶段
使用伪代码这样表示:
creationPhase = { // 建立阶段 'outerEnvironmentConnection': { // 建立外部链接 // 造成做用域链 }, 'variableObjectMapping': { // 变量、函数和函数参数的建立和引用在内存中完成。 }, 'valueOfThis': {}, // 肯定 this 的值 }
这是代码在建立阶段
造成的执行上下文中的运行的阶段,而且逐行分配变量值。
当执行开始时,JavaScript 引擎在其建立阶段对象中查找执行函数的引用。若是在当前对象中没有找到,它将沿着做用域链
继续向上查找,直到它到达全局环境。
若是在全局环境中找不到函数引用,则将返回错误。若是找到了引用而且函数正确执行,那么这个特定函数的执行上下文
将从栈中弹出,接着 JavaScript 引擎将移动到下一个函数,它们的函数执行上下文
将被加入到栈中并执行,以此类推。
让咱们经过示例来看看上面的两个阶段,以便更好地理解它。
let name = "webinfoq"; var title = "execution context"; const message = "hello world"; function func1(num) { var author = "deepak"; let value = 3; let func2 = function multiply() { return num * value; } const fixed = "Divine"; function addFive() { return num + 5; } } func1(10);
所以,全局执行上下文
将以下表示:
globalExecutionObj = { // 全局执行s上下文 outerEnvironmentConnection: null, // 全局上下文外部环境为 null variableObjectMapping: { name: uninitialized, // 在建立阶段,let声明的变量是未初始化状态 title: undefined, // var 声明的变量表示为未定义 date: uninitialized, // 在建立阶段,const声明的变量是未初始化状态 func1: <func1 reference>, func1 地址引用 }, this: window //Global Object }
注意:let
和const
定义的变量在建立阶段没有任何与之关联的值,但var
定义的变量在建立阶段为undefined
,
这就是为何能够在va
r 声明以前访问变量,(获得的是undefined
), 在let
和const
声明以前访问会报错(暂时性死区)。
这就是所谓的变量提高
,全部使用 var
声明的变量都会被提高到做用域的顶部。
在执行阶段
,完成对变量的赋值等操做。所以,在执行阶段
,全局执行上下文global execution
看起来像这样:
globalExectutionObj = { // 全局执行上下文 outerEnvironmentConnection: null, variableObjectMapping: { name: "webinfoq", title: "execution context", message: "hello world", func1: pointer to function func1, // 指向func1的指针 }, this: window //Global Object }
当执行到 func1()
时,将造成新的函数执行上下文 function execution global
,建立阶段以下所示:
func1ExecutionObj = { // func1 函数执行上下文 outerEnvironmentConnection: Global, // 外部环境为全局环境 variableObjectMapping: { arguments: { 0: 10, length: 1 }, num: 10, author: undefined, // var 声明的 value: uninitialized, // let 声明的 func2: uninitialized, // let 声明的 fixed: uninitialized, // const 声明 addFive: pointer to function addFive() // 指向函数addFive的指针 }, this: Global Object or undefined }
执行阶段:
func1ExecutionObj = { outerEnvironmentConnection: Global, variableObjectMapping: { arguments: { // 先处理 arguments 参数 0: 10, length: 1 }, num: 10, author: "deepak", //变量f赋值 val: 3, func2: pointer to function func2() fixed: "Divine" addFive: pointer to function addFive() }, this: Global Object or undefined }
Javascript 引擎建立执行上下文
,调用栈
。当有函数执行时,引擎就会建立一个新的函数执行上下文
。最后所用函数执行完成后,将更新全局环境,而后全局代码完成,程序结束。
了解更多请关注微信公众号:webinfoq
。