继续接着上篇文章,上篇咱们说到函数上下文的结构可表示为javascript
const ExecutionContextObj = { VO: window, // 变量对象 ScopeChain: {}, // 做用域链 this: window };
即每一个函数上下文,都要有这三个重要属性:java
今天再细说执行上下文中的变量对象函数
变量对象是与执行上下文的相关的数据做用域,存储了在上下文中定义的变量和函数声明。由于不一样执行上下文的变量对象略有不一样,因此变量对象通常分为全局上下文下的变量对象和函数上下文下的变量对象。this
变量对象的建立,属于执行上下文中的建立阶段,依次通过如下三个过程:线程
建立执行上下文有两个阶段:一个是建立阶段,一个是执行阶段。变量对象的建立,属于执行上下文中的建立阶段,会依次通过三个过程:code
在进入函数执行上下文时,会首先检查实参个数,接着对实参对象和形参进行赋值。若是没有实参,属性值设为undefined;当传入的实参数量小于形参数量,则会将没有被赋值的形参赋值为 undefined。对象
function fn(a, b, c){ console.log(a, b, c); // 1 2 undefined } fn(1, 2);
此时变量对象的结构为:ip
VO = { a: 1, b: 2, c: undefined }
遇到同名的函数时,后面函数会覆盖前面的函数。作用域
function fn() { console.log('先声明的'); } function fn() { console.log('后声明的'); } console.log(fn); //ƒ fn() { console.log('后声明的'); }
检查当前环境中经过变量声明(var)并赋值为undefined(变量提高产生的缘由)io
console.log(fn); // ƒ fn() { console.log('后声明的');} console.log(b); // undefined function fn() { console.log('先声明的'); } function fn() { console.log('后声明的'); } var b = 10; var fn = 20; console.log(b); // 10
由上面咱们看出,当变量名称与函数名称同名时,会忽略此变量声明,即同名时,函数声明优先
js虽然单线程的语言,执行顺序为顺序执行,但JS引擎并非一行一行地分析和执行程序,而是一段一段地分析执行。
让咱们从JS引擎的角度理一理上述三个过程:函数提高和变量提高,是全局执行上下文作的准备工做;当执行函数时,又会建立一个执行上下文,作的是这个函数内部的准备工做;这就是JS引擎分析代码的"预编译阶段",作完该工做才进入执行阶段。
在客户端 JavaScript 中,全局对象就是 Window 对象;
var a = 2; //在全局上下文使用var定义变量a,做为全局变量的宿主 console.log(this); // window对象 console.log(this.a); // 2
对于全局上下文中来讲,变量对象就是全局对象,
执行上下文的第二个阶段为执行阶段,此时会进行变量赋值,执行其余代码等工做,此时,变量对象变为活动对象(Active object, AO)。
活动对象和变量对象实际上是同个东西,只是规范概念上的差别。只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,因此才叫活动对象。而只有被激活的变量对象,也就是活动对象上的各类属性才能被访问。
因此明确,活动对象是在进入函数上下文时刻被建立的,它经过函数的 arguments 属性初始化。
console.log(fn); // ƒ fn() { console.log('后声明的');} console.log(b); // undefined function fn() { console.log('先声明的'); } function fn() { console.log('后声明的'); } var b = 10; console.log(b); // 10 var fn = 20; console.log(fn);//20
上述代码,真正开始执行是从第一行console.log(fn)。在此以前,变量对象VO是这样的:
// 建立过程 EC= { VO:{}, // 建立变量对象 scopeChain: [{VO}], // 做用域链 this: window // this绑定 } VO = { // argument: {}, // 当前为全局上下文,不存在arguments fn: reference to function fn(){}, // 函数fn的引用地址 b: undefiend // 变量提高 }
根据变量对象建立的三个过程,
到此,变量对象的建立阶段完成,接下来进行执行阶段:
1.执行console.log(fn);此时fn为声明的第二个函数,故输出结果:"后声明的"。 2.执行console.log(b),此时b已被赋值为undefined,故输出结果:"undefined"。 3.执行赋值操做: b = 10; 4.执行console.log(b) ,故输出b为10。 5.执行赋值操做: fn = 20; 6.执行console.log(fn) ,故输出fn为20。
执行到最后一步时,执行上下文以下:
// 执行阶段 EC = { VO = {}; scopeChain: {}; this: window; } // VO ---- AO AO = { argument: {}; fn: 20; b: 10; }
以上,就是变量对象在代码执行前及执行后的变化。