深入理解执行上下文和执行栈

编程入门 行业动态 更新时间:2024-10-28 10:28:01

一、执行上下文

1. 什么是执行上下文

执行上下文简而言之,就是JavaScript代码解析和执行时所在的环境

2.执行下文分类

全局执行上下文
(1) 不在任何函数中的代码都位于全局执行上下文中。
(2) 在JavaScript代码执行时,会创建一个全局执行上下文,并将其压入执行栈中
(3) 全局执行上下文做了两件事:1.创建一个全局对象,这个对象在浏览器中是window,在node.js中是global。2.将this指针指向这个全局对象
(4)一个程序只能有一个全局执行上下文函数执行上下文
(1) 每次调用函数时,都会为该函数创建一个新的执行上下文。
(2) 每个函数都有自己的执行上下文,但是只有在函数被调用的时候才会创建
(3) 一个程序中可以存在任意数量的函数执行上下文。
(4) 函数执行上下文的生命周期分为两个阶段。创建阶段和执行阶段。Eval执行上下文

3.执行上下文的生命周期

创建阶段
(1)创建变量对象

创建arguments对象,并用实参赋值(这个对象只有函数执行上下文中的变量对象才有)

检查函数声明:每检查到一个函数声明,就在变量对象中以函数名建立一个属性,属性值指向函数所在内存地址;

检查变量声明:每检测到一个var声明的变量,如果vo中已经存在该function属性名则跳过,如果不存在则用该属性名创建一个属性,赋值undefined.
注:函数表达式不在VO对象中,立即执行函数也不在VO对象中,函数表达式只有在执行赋值语句的时候才会创建这个函数。

在一段 JS 脚本执行之前,要先解析代码(所以说 JS 是解释执行的脚本语言),解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来。变量先暂时赋值为 undefined,函数则先声明好可使用。这一步做完了,然后再开始正式执行程序。

(2)创建作用域链

(3)确定this指向

执行阶段
(1) 变量赋值、函数表达式赋值,使变量对象(VO)变成活跃对象(AO),代码执行
(2) 函数当执行完毕后,执行上下文出栈,等待垃圾回收机制回收。

function a(name, age){var a = 1function c(){}var d = funciton (){}(function e(){})var f = function g(){}
}
a('John')
// 如上代码。
//在创建预编译阶段生成的AO对象如下
AO = {arguments:{0: 'John',1: undefined,length: 2},name: 'John',age: undefined,c: reference to function c(){},a: undefined,d: undefined,f: undefined,
}
// //函数表达式中的函数,只有在被赋值的时候才会创建。
// 函数g不在AO中// 函数执行时AO对象如下
AO = {arguments:{0: 'John',1: undefined,length: 2},name: 'John',age: undefined,c: reference to function c(){},a: 1,d: reference to FunctionExpression "d",f: reference to FunctionExpression "f",
}

4.关于函数提升和变量提升

Q:为什么会出现变量提升和函数提升
A:从创建变量对象的角度来说,因为在创建变量对象的时候,会先检查函数声明,再检查用var声明的变量,从外部代码执行来看,就相当于把函数声明和变量声明提升了。

(1) 变量提升

function func(name, age){function a(){console.log("hello")};var a;a();
}
func();		//hello
function func(name, age){function a(){console.log("hello")};var a = 1;a();
}
func();		//a is not a function

(2) 函数提升

创建函数的方式有两种,一种是通过函数声明来创建函数,一种是通过函数表达式来创建函数。那么这两种方式创建的函数在函数提升的时候有什么区别。

function test(){inner1();			//inner1console.log(v);		//undefinedv();				//v is not a functionfunction inner1() {console.log("inner1");}var v = function (){console.log("inner2");}
}
test();

再来一个:

function test(a){//形参a为"bubu"console.log(a);		//输出  f  a(){console.log("a function")}var a = "hello";	//忽略变量a的声明,a = "hello"function a(){		//函数声明提升console.log("a function");	}console.log(a);		//hello
}
test("bubu");

在检查变量声明的时候,遇到与函数同名的变量,是不会在VO中创建同名的属性的,所以函数的声明会覆盖变量的声明。但是可以重新赋值。

5.关于this指向

this 的值是在执行的时候才能确认,定义的时候不能确认! 为什么呢 —— 因为 this 是执行上下文环境的一部分,而执行上下文需要在代码执行之前确定,而不是定义的时候。
例:1:

var length = 10;function handler(){length = 20;
}
function say(){console.log(this.length);
}var arr = [say, say, say]; 
var obj2 = {length:40,handler:function(){console.log(this.length);}
}
var obj = {length:30,say:say,handler:function(h, o){h();o.handler();}
}
//由window来调用函数say,函数say的this指向window
say();
//由window来调用函数handler,函数handler的this指向window,length是全局变量,赋值后window.length变为20。		
handler()	
//再调用say函数this依旧指向window,所以this.length=20
say()		//obj的属性say指向say函数的地址,由obj.say()来调用函数say,调用者是obj,所以,say函数中的this指向obj。this.length=30
obj.say()	//obj的属性handler指向匿名函数的地址,由obj.handler(say,obj2)调用匿名函数,并将外部的say函数和obj2对象作为实参传入。
//匿名函数内的this指向obj,但是该匿名函数内的h(),是由window调用了传进来的say函数,所以h函数也就是say函数的this指向的是window对象。
//而匿名函数内的o.handler()是指obj2调用了obj2内部的方法handler,所以this指向obj2。
obj.handler(say,obj2)	

例2:

function Func(name, age){
this.name = name;this.age = age;
}
var a = new Func();

首先我们要理解new创建对象的时候做了哪些事
1.在内存中创建一个空对象
2.将该对象的__proto__指向构造函数的原型对象
3.调用构造函数,将构造函数的this指向新对象。
4.判断执行结果,如果结果为null或undefined则返回空对象,否则返回这个对象
所以用new调用构造函数时,构造函数中的this指向新建的对象。

二、执行栈

1.什么是执行栈

执行栈是一个先进后出的栈结构,是JS引擎用来管理执行上下文的。

一开始浏览器执行全局的代码时,首先创建全局的执行上下文,压入执行栈的顶部。每当进入一个函数的执行就会创建函数的执行上下文,并且把它压入执行栈的顶部。当前函数执行完成后,当前函数的执行上下文出栈,并等待垃圾回收。浏览器的 JS 执行引擎总是访问栈顶的执行上下文。全局上下文只有唯一的一个,它在浏览器关闭时出栈。JavaScript 执行在单线程上,所有的代码都是排队执行。

三、词法环境与变量环境

1.词法环境

ES3之前的变量对象与活动对象的概念在ES5之后由词法环境,变量环境来解释,两者概念不冲突,后者理解更为通俗易懂

1.1 词法环境是什么

词法环境是一个包含标识符变量映射的结构,这里的标识符表示变量/函数的名称,变量是对实际对象【包括函数类型对象】或原始值的引用。

1.2 词法环境的分类

全局词法环境:全局执行上下文,他没有外部环境的引用,拥有一个全局对象window和关联的方法和属性eg: Math,String,Date等。还有用户定义的全局变量,并将this指向全局对象。函数词法环境: 用户在函数定义的变量将储存在环境记录中。对外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境。环境记录中包含。用户声明的变量。函数。还有arguments对象。

1.3词法环境的组成

环境记录:储存变量和函数声明的实际位置对外部环境的引用: 当前可以访问的外部词法环境

2.变量环境

变量环境也是一个词法环境。他具有词法环境中所有的属性
在ES6中,LexicalEnvironment和VariableEnvironment 的区别在于前者用于存储函数声明和变量let 和 const 绑定,而后者仅用于存储变量 var 绑定。

伪代码:

// 全局执行上下文
GlobalExectionContext = {// 词法环境LexicalEnvironment: {// 环境记录EnvironmentRecord: {Type: "Object", //类型为对象环境记录// 标识符绑定在这里 },//对外部环境的引用outer: < null >}
};
// 函数执行上下文
FunctionExectionContext = {// 词法环境LexicalEnvironment: {// 环境纪录EnvironmentRecord: {Type: "Declarative", //类型为声明性环境记录// 标识符绑定在这里 },outer: < Global or outerfunction environment reference >}
};

例子:

let a = 20;  
const b = 30;  
var c;function multiply(e, f) {  var g = 20;  return e * f * g;  
}c = multiply(20, 30);
//全局执行上下文
GlobalExectionContext = {// this绑定为全局对象ThisBinding: <Global Object>,// 词法环境LexicalEnvironment: {  //环境记录EnvironmentRecord: {  Type: "Object",  // 对象环境记录// 标识符绑定在这里 let const创建的变量a b在这a: < uninitialized >,  b: < uninitialized >,  multiply: < func >  }// 全局环境外部环境引入为nullouter: <null>  },VariableEnvironment: {  EnvironmentRecord: {  Type: "Object",  // 对象环境记录// 标识符绑定在这里  var创建的c在这c: undefined,  }// 全局环境外部环境引入为nullouter: <null>  }  }// 函数执行上下文FunctionExectionContext = {//由于函数是默认调用 this绑定同样是全局对象ThisBinding: <Global Object>,// 词法环境LexicalEnvironment: {  EnvironmentRecord: {  Type: "Declarative",  // 声明性环境记录// 标识符绑定在这里  arguments对象在这Arguments: {0: 20, 1: 30, length: 2},  },  // 外部环境引入记录为</Global>outer: <GlobalEnvironment>  },VariableEnvironment: {  EnvironmentRecord: {  Type: "Declarative",  // 声明性环境记录// 标识符绑定在这里  var创建的g在这g: undefined  },  // 外部环境引入记录为</Global>outer: <GlobalEnvironment>  }  }

我们发现使用let和const声明的变量在词法环境创建时是未赋值初始值。而使用var定义的变量在变量环境创建时赋值为undefined。这也就是为什么const、let声明的变量在声明钱调用会报错,而var声明的变量不会。

参考以下博客
深入浅出执行上下文、词法环境、变量环境
一篇文章看懂JS执行上下文
深入理解 JavaScript 执行上下文和执行栈
图解Javascript——变量对象和活动对象

更多推荐

上下文

本文发布于:2023-05-26 18:04:42,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/275005.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:上下文

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!