JavaScript执行上下文

🌸 您好,欢迎您的阅读,等君久矣,愿与君畅谈.
🔭 § 始于颜值 § 陷于才华 § 忠于人品 §
📫 希望我们可以进一步交流,共同学习,共同探索未知的技术世界 稀土掘金 OR GitHub.


# 1. 概念

  1. 在函数执行的时,会创建一个内部对象,即称为执行上下文,同时定义了一个函数执行时的环境
  2. 在一个函数被调用的时,会创建一个活动记录,被称为执行上下文,在这个里面会包含函数调用栈、函数调用方式、传入参数等信息
  3. 每一个函数在被定义的时,会存在一个 [scope] 属性,该属性保存着作用域链

# 2. 类型

  1. # 全局执行上下文
    1. 只有一个,浏览器的全局对象就是 window 对象,this 指向这个全局对象,在执行全局代码前将 window 确定为全局执行上下文

    2. 对其全局数据进行处理

    3. 开始执行全局代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      //例子
      console.log(a1)
      console.log(a2)
      a2()
      console.log(this)
      var a1 = 2;
      function a2(){
      console.log("a2")
      }
  2. # 函数执行上下文
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //例子
    function fn(a1){
    console.log(a1)
    console.log(a2)
    a3()
    console.log(this)
    console.log(arguments)
    var a2 = 3
    function a3(){
    console.log("a3")
    }
    }
    fn(2,3)
    1. 只有函数被调用时创建,每次调用都会创建一个新的对应的函数执行上下文对象
    2. 对局部数据进行预处理
    3. 开始执行函数体代码
  3. # Eval函数 执行上下文
    1. 运行在该函数中的代码

概念:EC : 执行上下文、ECS : 执行上下文栈、VO : 变量对象、AO : 活动对象、scope chain : 作用域链

VO变量对象 :创建执行上下文时与之关联的会有一个变量对象,该上下文中的所有变量和函数全都保存在这个对象中

AO活动对象 :进入到一个执行上下文时,此执行上下文中的变量和函数都可以被访问到,可以理解为被激活

# 2. 执行上下文的具体过程

1
2
3
4
5
6
7
//
function foo(i) {
var a = 'hello';
var b = function privateB() {};
function c() {}
}
foo(22);

# 1. 创建阶段(函数被调用,但是还未执行函数中的代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//创建时函数体中相关代码(代码1)
fooExecutionContext = {
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c(),
a: undefined,
b: undefined
},
scopeChain: { ... },
this: { ... }
}
  1. 创建变量对象 (VO)
    1. 创建 arguments,检查当前上下文的参数,建立该对象下的属性和属性值
    2. 扫描上下文的函数申明
      1. 每扫描到一个函数,就会在 VO 里面用函数名创建一个属性,为一个指针,指向该函数在内存中的地址
      2. 如果函数名在 VO 中已经存在,对应的属性值会被新的引用覆盖
      3. 即为 var(undefined)定义的全局变量为 window 属性、function(fun)声明全局函数为 window 方法、this 赋值为 widow
    3. 扫描上下文的变量申明:
      1. 每扫描到一个变量就会用变量名作为属性名,其值初始化为 undefined
      2. 如果该变量名在 VO 中已经存在,则直接跳过继续扫描
  2. 初始化作用域链
  3. 确定上下文中 this 的指向

# 2. 代码执行阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//执行时函数体中相关代码(代码2)
fooExecutionContext = {
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c(),
a: 'hello',
b: pointer to function privateB()
},
scopeChain: { ... },
this: { ... }
}
  1. 执行函数体中的代码,给 VO 中的变量赋值

# 3. 执行上下文栈 Execution context stack

1
2
3
4
5
6
7
8
9
10
11
12
//知识小练
var a = 10
var bar = function(x){
var b = 5
foo(x + b)
}
var foo = function(y){
var c = 5
foo1(a + c + y)
}
bar(10)

# 1. 执行栈(调用栈)

  1. 具有 LIFO 结构(后进先出),用于存储在代码执行期间创建的上下文(函数的执行顺序和定义顺序没有关系,可以通过执行栈理解)

# 2. 原理

  1. 每当执行 js 代码时,创建一个全局执行上下文并且 push 当前的执行上下文栈顶,此后每发生一次函数调用,搜索引擎会创建一个新的函数执行上下文,并且将其 push 当前执行栈的栈顶,当栈顶的函数执行完毕,执行栈中对应的执行上下文就会 pop,同时变量对象 VO 也会销毁,栈指针将会指向下一个执行上下文,直至应用关闭时将会销毁全局上下文

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//课前小练
//问题一
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()();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//经典面试题1
问题1:依次输出?
问题2:一共产生多少执行山下文对象?

console.log('gb:'+i)
var i = 1
foo(1)
function foo(i){
if(i == 4){
return
}
console.log('fb:'+i)
foo(i + 1)
console.log('fe:'+i)
}
console.log('ge:'+i)

输出:
undefined
1
2
3
3
2
1
1

JavaScript执行上下文
http://example.com/2022/06/23/4004_JavaScript执行上下文/
作者
XGG
发布于
2022年6月23日
更新于
2023年6月3日
许可协议