JS 核心之执行上下文详细解释

执行上下文

基本概念:

**执行上下文:**一个函数运行之前,创建的一块内存空间,空间中包含有该函数执行所需要的数据,为该函数执行提供支持。这块内存空间与包含的数据就是执行上下文(可以理解为一个对象)。

**执行上下文栈:**call stack,所有执行上下文组成的内存空间。

函数执行过程: JS 引擎每次执行栈顶的执行上下文。

全局执行上下文: 可以理解为所有代码的入口函数(main)所形成的执行上下文。

函数执行与入栈过程

html 复制代码
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>

  <body>
    <script>
      console.log('global 1');

      function A() {
        console.log('A1');

        function B() {
          console.log('B');
        }

        B();
        console.log('A2');
      }
      A();
      console.log('global 2');
    </script>
  </body>
</html>

输出结果:

js 复制代码
global 1
A1
B
A2
global 2

执行上下文栈的变化过程:

注意点:

  1. 调用函数时会创建该函数的执行上下文(入栈)
  2. 函数调用完毕会销毁该函数的执行上下文(出栈)
  3. JS 引擎每次执行栈顶的执行上下文。

执行上下文包含的内容:

基础概念:

VO: variable object 中记录了该环境中所有声明的参数、变量和函数

GO: global object 全局执行上下文中的VO

AO: active object 当前正在运行的执行上下文中的VO

每次执行函数时,会创建一个新的执行上下文,执行上下文包含两部分内容,this 与 VO

this

this 的三种指向:

  1. 直接调用函数,this 指向全局对象
  2. 在函数外,this 指向全局对象
  3. 通过对象调用或 new 一个函数,this 指向调用的对象或新对象
vo

VO 是一个对象,记录该环境中所有声明的参数、变量和函数

函数执行过程与执行上下文栈的详细构造过程

注意点:

  1. 全局执行上下文在程序开始时默认初始化在栈底
  2. 函数调用时创建新的执行上下文
  3. 创建新的执行上下文时要确定执行上下文中的内容

将用以下实例代码说明执行上下文栈的变化过程:

html 复制代码
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>

  <body>
    <script>
      console.log(this.name);
      var a = 2;
      function A() {
        this.abc = 123;

        function B() {
          console.log(this);
        }
        B();
      }
      var a = new A();

      console.log(a.abc);
    </script>
  </body>
</html>

第一步:初始化执行上下文栈与全局执行上下文,并确定全局执行上下文中的 this 与 VO 的值

初始化第一阶段:

运行至 12 行:this 指向 window,window.name 是浏览器自带的属性它的默认值就是空字符串 "",用来存储窗口名称(用于跨页面通信、iframe 标识)

运行至 13 行:a 的值变为 2

运行至 22 行,有新函数运行,创建新的执行上下文,进入第二步:

第二步:初始化函数 A 的执行上下文,确定其 this 与 VO

运行至 15 行:this 指向新创建的对象 a,将其中的 abc 属性赋值为 123

运行至 20 行,有新函数执行,创建新的执行上下文,进入第三步:

第三步:初始化函数 B 的执行上下文,确定其 this 与 VO

运行至 18 行,输出 window。

B()执行完毕,其上下文出栈,B()执行完毕,A()也执行完毕,至此 22 行 new A()执行完毕,给 a 赋值并将 A()上下文出栈

执行 24 行:a.abc 的值为 123,至此,全部运行结束。

VO 中的值确定的过程:

  1. 确定所有形参值以及特殊变量arguments
  2. 确定函数中通过var声明的变量,将它们的值设置为undefined,如果VO中已有该名称,则直接忽略(因为已经是 undefined 了)。
  3. 确定函数中通过字面量声明的函数(function 的方式,不是变量的方式),将它们的值设置为指向函数对象(函数本身就是个对象),如果VO中已存在该名称,则覆盖。

这里也牵扯到变量与函数的声明提升的问题:

  1. var 声明的变量会有声明提升,在 VO 创建时会初始化为 undefined。

  2. 函数也会有声明提升,也是在 VO 创建时会初始化。

这里也牵扯到变量重新声明的问题 :

var 可以重复声明同一变量,因为 VO 已有该名称,VO 初始化时会忽略后面的声明

js 复制代码
function test() {
  console.log(a); // VO初始化后 a 是 undefined
  var a = 100; // 按顺序执行,a 被赋值为 100
  console.log(a);
  var a = 200; // 按顺序执行,a 被赋值为 200
  console.log(a);
}

test(); // undefined 100 200

这里也牵扯到函数重新声明的问题:

函数重新声明,会被覆盖,因为 VO 中将这个函数指向了另一个函数对象。

js 复制代码
function test() {
  console.log(a);
  var a = 100;
  console.log(a);
  var a = 200;
  console.log(a);
}  
// 上方的 test 会被覆盖
function test() {
  console.log(a);
  var a = 300;
  console.log(a);
  var a = 400;
  console.log(a);
}

test(); // undefined 300 400

引出作用域链:

当一个执行上下文中的代码执行的时候,如果执行上下文中不存在某个属性,则会从之前的上下文寻找(往栈底找,直到找到全局上下文中,若全局上下文中都没有则报错)

题目:

js 复制代码
var foo = 1;
function bar() {
  console.log(foo);
  if (!foo) {
    var foo = 10;
  }
  console.log(foo);
}

bar();

结果:undefined 10

注意点:

var 没有块级作用域,故 bar()的 AO 中也包含 foo,初始化为 undefined

执行到第 4 行时,if 条件为 true,foo 被赋值为 10,故 7 行输出 10

若 5 行改为 let 则连续输出两次 1

题目:

js 复制代码
var a = 1;
function b() {
    console.log(a); 
    a = 10;
    return;
    function a() { }
}
b();
console.log(a); 

结果:function 1

注意点:

函数声明会被提前,故 b 函数中的 a 初始为 undefined,再后来是 function,再后来是 10

9 行的 a 是全局执行上下文中的 a

题目:

js 复制代码
var foo = 1;

function bar(a) {
  var a1 = a;
  var a = foo;
  function a() {
    console.log(a);
  }
  a1();
}

bar(3);

结果:1

注意 点:

严格按照赋值顺序来,优先级分别是,变量赋值 undefined,形参赋值,同名变量忽略,同名函数覆盖,然后在按照顺序执行代码。

相关推荐
AlienZHOU3 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc7 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师8 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen8 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒9 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow9 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林8189 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang800789 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明9 小时前
第 6 章 组件进阶
前端·vue.js
江华森9 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端