注意文中所指作用域和执行上下文都有全局和局部之分,为方便理解采用局部。本文参考引自:思否:执行上下文和作用域的理解
定义
作用域:js 中的作用域是词法作用域,即由函数声明时所在的位置决定的。
执行上下文:每次函数调用时,都会产生一个新的执行上下文环境。
深入理解
作用域:js 中的作用域是词法作用域,是在编译阶段就产生的,一整套函数标识符的访问规则。作用域只是一个“空地盘”,其中并没有真实的变量,但是却定义了变量如何访问的规则。
作用域链本质上是一个指向变量对象的指针列表,它只引用不包含实际变量对象。定义了当变量访问不到的时候如何向上查询的一套规则。
执行上下文:每次函数执行都会产生执行上下文,JavaScript 引擎会以栈的方式来处理它们,这个栈,我们称其为函数调用栈 (call stack)。栈底永远都是全局上下文,而栈顶就是当前处于活动状态的正在执行的上下文,也称为活动对象(running execution context,图中蓝色的块),区别与底下被挂起的上下文(变量对象)。
let fn, bar; // 1、进入全局上下文环境
bar = function(x) {
let b = 5;
fn(x + b); // 3、进入 fn 函数上下文环境
};
fn = function(y) {
let c = 5;
console.log(y + c); //4、fn 出栈,bar 出栈
};
bar(10); // 2、进入 bar 函数上下文环境
函数执行过程
建立执行上下文阶段 (发生在 函数被调用时 && 函数体内的代码执行前)
PS:个人觉得这个阶段也可以叫编译阶段:首先 js 是解释型语言,解释一条执行一条。其次函数内部的代码在执行前并没有做特殊处理,比如函数体内有语法错误,只有调用了函数才会报错,而在初始化 js 解析的时候并没有发生任何报错信息。
1、生成变量对象:创建 arguments 对象 –> 创建 function 函数声明 –> 创建 var 变量声明;2、生成作用域链 3、确定 this 的指向
函数执行阶段
变量赋值,函数引用,执行其它代码
作用域和执行上下文联系
由定义可知,作用域是在函数声明的时候就确定的一套变量访问规则,而执行上下文是函数执行时才产生的一系列变量的集合体。也就是说作用域定义了执行上下文中的变量的访问规则,执行上下文是在这个作用域规则的前提下执行代码的。