JavaScript深刻系列第七篇,结合以前所讲的四篇文章,以权威指南的demo为例,具体讲解当函数执行的时候,执行上下文栈、变量对象、做用域链是如何变化的。git
在《JavaScript深刻之执行上下文栈》中讲到,当 JavaScript 代码执行一段可执行代码(executable code)时,会建立对应的执行上下文(execution context)。github
对于每一个执行上下文,都有三个重要属性:面试
变量对象(Variable object,VO)闭包
做用域链(Scope chain)app
this函数
而后分别在《JavaScript深刻之变量对象》、《JavaScript深刻之做用域链》、《JavaScript深刻之从ECMAScript规范解读this》中讲解了这三个属性。ui
阅读本文前,若是对以上的概念不是很清楚,但愿先阅读这些文章。this
由于,这一篇,咱们会结合着全部内容,讲讲执行上下文的具体处理过程。code
在《JavaScript深刻之词法做用域和动态做用域》中,提出这样一道思考题:对象
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope();
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()();
两段代码都会打印'local scope'。虽然两段代码执行的结果同样,可是两段代码究竟有哪些不一样呢?
紧接着就在下一篇《JavaScript深刻之执行上下文栈》中,讲到了二者的区别在于执行上下文栈的变化不同,然而,若是是这样笼统的回答,依然显得不够详细,本篇就会详细的解析执行上下文栈和执行上下文的具体变化过程。
咱们分析第一段代码:
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope();
执行过程以下:
1.执行全局代码,建立全局执行上下文,全局上下文被压入执行上下文栈
ECStack = [ globalContext ];
2.全局上下文初始化
globalContext = { VO: [global, scope, checkscope], Scope: [globalContext.VO], this: globalContext.VO }
2.初始化的同时,checkscope 函数被建立,保存做用域链到函数的内部属性[[scope]]
checkscope.[[scope]] = [ globalContext.VO ];
3.执行 checkscope 函数,建立 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈
ECStack = [ checkscopeContext, globalContext ];
4.checkscope 函数执行上下文初始化:
复制函数 [[scope]] 属性建立做用域链,
用 arguments 建立活动对象,
初始化活动对象,即加入形参、函数声明、变量声明,
将活动对象压入 checkscope 做用域链顶端。
同时 f 函数被建立,保存做用域链到 f 函数的内部属性[[scope]]
checkscopeContext = { AO: { arguments: { length: 0 }, scope: undefined, f: reference to function f(){} }, Scope: [AO, globalContext.VO], this: undefined }
5.执行 f 函数,建立 f 函数执行上下文,f 函数执行上下文被压入执行上下文栈
ECStack = [ fContext, checkscopeContext, globalContext ];
6.f 函数执行上下文初始化, 如下跟第 4 步相同:
复制函数 [[scope]] 属性建立做用域链
用 arguments 建立活动对象
初始化活动对象,即加入形参、函数声明、变量声明
将活动对象压入 f 做用域链顶端
fContext = { AO: { arguments: { length: 0 } }, Scope: [AO, checkscopeContext.AO, globalContext.VO], this: undefined }
7.f 函数执行,沿着做用域链查找 scope 值,返回 scope 值
8.f 函数执行完毕,f 函数上下文从执行上下文栈中弹出
ECStack = [ checkscopeContext, globalContext ];
9.checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出
ECStack = [ globalContext ];
第二段代码就留给你们去尝试模拟它的执行过程。
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()();
不过,在下一篇《JavaScript深刻之闭包》中也会说起这段代码的执行过程。
《JavaScript深刻之从ECMAScript规范解读this》
本文写的太好,给了我不少启发。感激涕零!
JavaScript深刻系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript深刻系列预计写十五篇左右,旨在帮你们捋顺JavaScript底层知识,重点讲解如原型、做用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。
若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎star,对做者也是一种鼓励。