• JS中的执行上下文和执行栈


    一、执行上下文

    简单的来说,执行上下文是一种对Javascript代码执行环境的抽象概念,也就是说只要有Javascript代码运行,那么它就一定是运行在执行上下文中

    执行上下文的类型分为三种:

    • 全局执行上下文:只有一个,浏览器中的全局对象就是 window 对象,this 指向这个全局对象
    • 函数执行上下文:存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文
    • Eval 函数执行上下文: 指的是运行在 eval 函数中的代码,很少用而且不建议使用

    下面给出全局上下文和函数上下文的例子:
    执行上下文

    紫色框住的部分为全局上下文,蓝色和橘色框起来的是不同的函数上下文。只有全局上下文(的变量)能被其他任何上下文访问

    可以有任意多个函数上下文,每次调用函数创建一个新的上下文,会创建一个私有作用域,函数内部声明的任何变量都不能在当前函数作用域外部直接访问
    特点:

    • 单线程。
    • 同步执行。
    • 只有一个全局上下文。
    • 可有无数个函数上下文。 每个函数调用都会创建一个新的 执行上下文,哪怕是递归调用。

    二、执行上下文的生命周期

    执行上下文的生命周期包括三个阶段:创建阶段 → 执行阶段 → 回收阶段,本文重点介绍创建阶段。

    1. 创建阶段

    当函数被调用,但未执行任何其内部代码之前,会做以下三件事:

    • 创建变量对象:首先初始化函数的参数 arguments,提升函数声明和变量声明。
    • 创建作用域链(Scope Chain):在执行期上下文的创建阶段,作用域链是在变量对象之后创建的。作用域链本身包含变量对象。作用域链用于解析变量。当被要求解析变量时,JavaScript始终从代码嵌套的最内层开始,如果最内层没有找到变量,就会跳转到上一层父作用域中查找,直到找到该变量。
    • 确定 this 指向:包括多种情况(联系this指向看)

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

    另外,一个函数在执行之前,也会创建一个函数执行上下文环境,跟全局上下文差不多,不过 函数执行上下文中会多出 this arguments 和函数的参数。

    1. 执行阶段
      执行变量赋值、代码执行

    2. 回收阶段
      执行上下文出栈等待虚拟机回收执行上下文

    三、执行上下文栈

    在浏览器中的 JavaScript 解释器是单线程的。这实际上意味着,在浏览器中一次只会发生一件事,其他行为或者事件在所谓的执行栈 中排队等待。下为单线程的栈的一个抽象的表示:
    执行上下文栈

    解析:
    浏览器第一次加载脚本, 它将默认进入 全局执行上下文 中。 如果,你在全局环境中调用了一个函数, 你的程序序列流会进入被调用的函数的当中,创建一个新的 执行上下文 并且将这个上下文压入执行栈之中。
    如果你在当前函数里面又调用了另外一个函数, 也会发生同样的事情。代码的执行流进入内部函数,这将创建一个新的执行上下文,它被压入现有栈的顶部。浏览器永远会执行当前栈中顶部的执行上下文 一旦函数在当前执行上下文执行完毕,它会被从栈的顶部弹出,然后将控制权移交给当前栈的下一个上下文当中。

    例1

    //递归函数以及程序的 执行上下文
    (function foo(i) {
       if (i === 3) {
           return;
       }
       else {
           foo(++i);
       }
    }(0));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行栈
    例2

    var color = "blue";
    function changeColor() {
        var anotherColor = "red";
        function swapColors() {
            var tempColor = anotherColor;
            anotherColor = color;
            color = tempColor;
        }
        swapColors();
    }
    changeColor();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    步骤:

    • 当上述代码在浏览器中加载时,JavaScript 引擎会创建一个全局执行上下文并且将它推入当前的执行栈
    • 调用 changeColor 函数时,此时 changeColor 函数内部代码还未执行,js 执行引擎立即创建一个changeColor 的执行上下文(简称 EC),然后把这执行上下文压入到执行栈(简称 ECStack)中。
    • 执行 changeColor 函数过程中,调用 swapColors 函数,同样地,swapColors 函数执行之前也创建了一个
      swapColors 的执行上下文,并压入到执行栈中。
    • swapColors 函数执行完成,swapColors 函数的执行上下文出栈,并且被销毁。
    • changeColor 函数执行完成,changeColor 函数的执行上下文出栈,并且被销毁。
      执行栈
  • 相关阅读:
    计算机毕业设计JavaWeb医学院校大学生就业信息管理系统(源码+系统+mysql数据库+lw文档)
    ubuntu16.04部署postgrest(无网络)
    AI推介-大语言模型LLMs论文速览(arXiv方向):2024.05.05-2024.05.10
    java springboot获取GitLab上的文件内容
    基于springboot和vue的校园二手物品交易管理系统
    Redis
    K8s API快速入门
    ipv6地址概述——了解ipv6地址
    投你500万,你想做什么?头脑风暴一下 无限想像 有想法的人都能投 数学
    全链路压测(11):聊聊稳定性预案
  • 原文地址:https://blog.csdn.net/m0_51055743/article/details/126461487