js 执行上下文

第13章 执行上下文


13-1 执行上下文介绍


执行上下文概念

什么是执行上下文

执行上下文(Execution Context):函数执行前进行的准备工作(也称执行上下文环境)。
运行JavaScript代码时,当代码执行进入一个环境时,就会为该环境创建一个执行上下文,它在运行代码前做一些准备工作,比如说:确定作用域,创建局部变量对象等。
####JavaScript中执行环境
1:全局环境
2:函数环境
3:eval函数环境(已经被淘汰)
####相对应的执行上下文类型同样也有三种
1:全局执行上下文
2:函数执行上下文
eval函数执行上下文

JavaScript运行时首先会进入全局环境,对应会生成全局上下文。程序代码中基本都会存在函数,那么调用函数,就会进入函数执行环境,对应就会产生该函数的执行上下文。

因为JavaScript是单线程,所以同一个时间段只能做一件任务,完成之后才可以继续下一个任务,所以就必须有一个排队机制。

####JavaScript 中管理多个执行上下文
函数编程中,代码中会声明多个函数,对应的执行上下文也会存在多个。在JavaScript中,通过栈的存取方式来管理执行上下文,我们可称其为执行栈
,或函数调用栈(Call Stack)。

####栈数据结构

栈遵循"先进后出,后进先出"的规则,或称LIFO(“Last in First Out”)的规则。
栈数据结构的特点:
1:后进先出,先进后出
2:出口在顶部,且仅有一个

执行栈(函数调用栈)

JavaScript中如何通过栈来管理多个执行上下文。

程序执行进入一个执行环境时,它的执行上下文就会被创建,并被推入执行栈中(入栈)。程序执行完成时,它的执行上下文就会被摧毁,并从栈顶被推出(出栈),控制权交由下一个执行上下文。

因为 JavaScript 执行中最先进入全局环境,所以处于"栈底的永远是全局环境的执行上下文"。而处于"栈顶的是当前正在执行上下文",当函数调用完成后,它就会从栈顶被推出(通常情况下,闭包会阻止该操作)。

全局环境只有一个,对应的全局执行上下文也只有一个,只有当页面被关闭之后才会从执行栈中被推出,否则一直存在于栈底。至于函数上下文的个数是没有任何限制的,每到调用执行一个函数时,引擎就会自动新建出一个函数上下文。

例:

(function foo(i) {
if (i == 3) { 
return;  
} else {   
console.log(i);   
foo(++i);   
} 
})(0);
//步骤如下
//全局上下文
//函数上下文 0
//函数上下文 1
//函数上下文 2
//函数上下文

####执行上下文的生命周期
执行上下文的生命周期有两个阶段:

1:创建阶段(进入执行上下文):函数被调用时,进入函数环境,为其创建一个执行上下文,此时进入创建阶段。
2:执行阶段(代码执行):执行函数中代码时,此时执行上下文进入执行阶段。
####创建阶段的操作
1:创建变量的对象

函数环境会初始化创建的Arguments对象,形式参数(并赋值)
普通函数声明(并赋值)
局部变量声明,函数表达式声明(未赋值)

2:初始化作用域链
3:确定this指向(this由调用者确定)
4:确定作用域(语法环境决定,哪里声明定义,就在哪里确定)

####执行阶段的操作

1:变量对象赋值

变量赋值
函数表达式赋值

2:*调用函数
3:顺序执行其他代码

####执行上下文与作用域区别
注:作用域和执行上下文并不是同一个概念
执行代码时,会产生一个执行上下文环境,每次调用函数都有会执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除(除了闭包),处于活动状态的执行上下文环境只有一个。

而作用域在函数定义时就已经确定了,不是在函数调用时确定(区别于执行上下文环境,并且this也是上下文环境里的成分)

// 全局作用域
let x = 100;
function bar() { 
console.log(x); 
} 
// fn ֢作用域
function fn() {    
let x = 50; 
// bar作用域
bar();
}
fn(); // 100

作用域只是一个"地盘",其中没有变量。变量是通过作用域对应的执行上下文环境中的变量对象来实现的,所以作用域是静态观念的,而执行上下文环境是动态上的,两者并不一样。

有闭包存在时,一个作用域存在两个上下文环境也是有的。也就是说,作用域只是用于划分你在这个作用域里面定义的变量的有效范围,出了这个作用域就无效。

同一个作用域下,对同一个函数的不同的调用就会产生不同的执行上下文你环境,然而产生不同的变量的值,所以,作用域中变量的值时执行过程中确定的,而作用域是在函数创建时确定的。

如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中找到变量的值。


13-2 变量对象


以上介绍上下文的概念。整个执行上下文的生命周期包含2个阶段:建立阶段和执行阶段。

当处于执行上下文的建立阶段时,可以将整个上下文环境看作是一个对象。该对象拥有3个属性

executionContextObj = { 
   variableObject : {}, //变量对象,里面包含 Arguments 对象,形参和实参,函数和局部变量
scopeChain : {},//作用域链,包含内部上下文所有变量对象的列表
this :{}//上下文中 this 的指向对象
}

以上执行上下文抽象成为了一个对象,拥有3个属性,分别是变量对象作用域链以及this指向

变量对象里面所拥有的东西

在函数的建立阶段,首先会建立 Arguments 对象。然后确定形式参数,检查当前上下文的函数声明,每当找到一个函数声明,就会在 variableObject 下面用函数名建立一个属性,属性值就指向该函数在内存中的地址的一个引用。如果上述函数名已经存在于 variableObject (简称VO)下面,那么对应的属性值就会被新的引用给覆盖。最后,是确定当前上下文中的局部变量,如果遇到和函数名同名的变量,则会忽略该变量。

函数的这两个阶段以及变量对象是如何变化的
例:

const foo = function(i){
var a = "Hello"; 
var b = function privateB(){}; 
function c(){} } 
foo(10);

首先在建立阶段的变量对象如下

fooExecutionContext = {
variavleObject : {
arguments : {0 : 10,length : 1}, // 确定Arguments 对象   
i : 10, // 确定形式参数
c : pointer to function c(), // 确定函数引用
a : undefined, // 局部变量 undefined       
b : undefined  // 局部变量 undefined
},
scopeChain : {},
this : {} 

由此可见,在建立阶段,除了Arguments,函数的声明,以及形式参数被赋予了具体的属性值外,其他的变量属性默认的都是 undefined 。并且普通形式声明的函数的提升是在变量的上面的。

一旦上述建立阶段结束,引擎就会进入代码执行阶段,这个阶段完成后,上述执行上下文对象如下,变量就会被赋上具体的值。

fooExecutionContext = { 
variavleObject : {
arguments : {0 : 10,length : 1},
i : 10,
c : pointer to function c(), 
a : "Hello",// a  变量被赋值为 Hello
b : pointer to function privateB() // b 变量被赋值为 privateB()函数},
scopeChain : {},
this : {} 
}

只有在代码执行阶段,局部变量才会被赋予具体的值。在建立阶段局部变量的值都是undefined。这其实也就是解释了变量提升的原理。

加深对函数这两个阶段的过程理解

(function () {
console.log(typeof foo);
console.log(typeof bar);
var foo = "Hello";
var bar = function () {
return "World";
}
function foo() {
return "good";
}
console.log(foo, typeof foo); 
})()

定义一个IIFE,该函数在建立阶段的变量对象如下:

fooExecutionContext = {
variavleObject : {
arguments : {length : 0},
foo : pointer to function foo(), 
bar : undefined    }, 
scopeChain : {},
this : {}
}

首先确定 Arguments 对象,接下来就是形式参数,由于本例中不存在形式参数,所以接下来开始确定函数的引用,找到foo()函数后,创建 foo 标识符来指向这个foo()函数,之后同名的 foo 变量不会被创建,会直接避恶忽略。然后创建 bar 变量,不过初始值为 undefined 。

建立阶段完成之后,接下来进入代码执行阶段,开始一句一句的执行代码,结果如下:

(function () {
console.log(typeof foo); // function
console.log(typeof bar); // undefined 
var foo = "Hello"; // foo被重新赋值 变成一个字符串
 var bar = function () {
 return "World";
 }
function foo() {
return "good";
}
console.log(foo, typeof foo); //Hello string 
})()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值