大话javascript 2期:执行上下文与执行上下文栈

news/2024/7/24 4:39:33 标签: javascript, 数据结构与算法

一、什么是执行上下文?

执行上下文(Execution Context): 函数执行前进行的准备工作(也称执行上下文环境)

JavaScript在执行一个“代码段”之前,即解析(预处理)阶段,会先进行一些“准备工作”,例如扫描JS中var定义的变量、函数名等,进而生成执行上下文。

name-
变量对象(VO, variable object)当前函数定义的变量、函数、参数
作用域链(Scope chain)源代码定义时形成的作用域链
this

JS中的“代码段”分为三种:全局代码段、函数体代码段、eval代码段。(注:ES6之前,JS不存在“代码块”作用域的概念,即除了函数之外所有“{}”里的代码,都属于全局作用域)

全局代码段“准备工作”包括:

1.变量、函数表达式 —— 变量声明,默认赋值为undefined;
2.this —— 赋值;
3.函数声明 —— 赋值。

函数体代码段“准备工作”包括:

1.变量、函数表达式 —— 变量声明,默认赋值为undefined;
2.this —— 赋值;
3.函数声明 —— 赋值;
4.参数 —— 赋值;
5.argument —— 赋值;
6.自由变量的取值作用域 —— 赋值。

evel()不推荐使用,所以不再分析evel代码段。

至此,“执行上下文”的定义可以通俗化为 —— 在执行代码段之前(预处理阶段),把将要用到的所有变量都事先拿出来,有的直接赋值,有的先用undefined占个空,这些变量共同组成的词法环境,即为执行上下文环境

二、执行上下文栈

javaScript是单线程语言,简单理解下单线程,就是同个时间段只能做一件任务,完成之后才可以继续下一个任务。
函数编程中,代码中会声明多个函数,对应的执行上下文也会存在多个。在JavaScript中,通过栈的存取方式来管理执行上下文,我们可称其为执行栈,或函数调用栈(Call Stack)。

1.栈数据结构

要简单理解栈的存取方式,我们可以通过类比乒乓球盒子来分析。如下图左侧。
clipboard.png
栈遵循"先进后出,后进先出"的规则,或称LIFO ("Last In First Out") 规则。

如图所示,我们只能从栈顶取出或放入乒乓球,最先放进盒子的总是最后才能取出。
栈中"放入/取出",也可称为"入栈/出栈"。

总结栈数据结构的特点:

  • 后进先出,先进后出
  • 出口在顶部,且仅有一个

2.执行上下文栈ECS(Execution Context Stack)(函数调用栈)

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

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

"全局环境只有一个,对应的全局执行上下文也只有一个,只有当页面被关闭之后它才会从执行栈中被推出,否则一直存在于栈底"

function foo () {
    function bar () {
        return 'I am bar';
    }
    return bar();
}
foo();

clipboard.png

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

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

  1. 创建阶段(进入执行上下文)
  2. 执行阶段(代码执行)

创建阶段:函数被调用时,进入函数环境,为其创建一个执行上下文,此时进入创建阶段
执行阶段:执行函数中代码时,此时执行上下文进入执行阶段

创建阶段的操作

  1. 创建变量对象

    • 函数环境会初始化创建Arguments对象(并赋值)
    • 函数声明(并赋值)
    • 变量声明,函数表达式声明(未赋值)
  2. 确定this指向(this由调用者确定)
  3. 确定作用域(词法环境决定,哪里声明定义,就在哪里确定)

执行阶段的操作

  1. 变量对象赋值

    • 变量赋值
    • 函数表达式赋值

2.调用函数
3.顺序执行其它代码

变量对象和活动对象的区别:

当进入到一个执行上下文后,这个变量对象才会被激活,所以叫活动对象(AO),这时候活动对象上的各种属性才能被访问。

"创建阶段对函数声明做赋值,变量及函数表达式仅做声明,真正的赋值操作要等到执行上下文代码执行阶段"

代码例子1:变量提升

function foo() {
  console.log(a);         // 输出undefined
  var a = 'I am here';    // 赋值
}
foo();

// 实际执行过程

function foo() {
  var a;                // 变量声明,var初始化undefined
  console.log(a); 
  a = 'I am here';     // 变量重新赋值
}

代码例子2:函数声明优先级

function foo() {
    console.log(bar);
    var bar = 20;
    function bar() {
      return 10;
    }
    var bar = function() {
        return 30;
    }
}
foo();  // 输出bar()整个函数声明

函数声明,变量声明,函数表达式的优先级

  1. 函数声明,如果有同名属性,会替换掉
  2. 变量,函数表达式
  3. 函数声明优先 > 变量,函数表达式

4.执行上下文的数量限制(堆栈溢出)

执行上下文可存在多个,虽然没有明确的数量限制,但如果超出栈分配的空间,会造成堆栈溢出。常见于递归调用,没有终止条件造成死循环的场景。
// 递归调用自身
function foo() {
  foo();
}
foo();

// 报错: Uncaught RangeError: Maximum call stack size exceeded

三、执行上下文流程图

clipboard.png


http://www.niftyadmin.cn/n/1202899.html

相关文章

js将秒转化为时分秒

function formateTime(time) {const h parseInt(time / 3600)const minute parseInt(time / 60 % 60)const second Math.ceil(time % 60) const hours h < 10 ? 0 h : hconst formatSecond second > 59 ? 59 : secondreturn ${hours > 0 ? ${hours}: : }$…

数据结构java版之冒泡排序及优化

冒泡排序的时间用大O表示法是O(N^2). 传统的冒泡排序&#xff1a; /** * param total 要排序的数组长度 */ public void sort(int total){ int num[]; if(total < 0){ System.out.println("请输入大于0的正整数"); }else{ num new int[total]; for (int i 0 ; i…

element时间抽el-timeline触发点击事件的方法

直接在element的时间轴组件el-timeline-item上挂在点击事件是不生效的&#xff0c;只有点击在连接线的位置才能触发&#xff0c;这是因为在点击过程中&#xff0c;可能点击到的是el-timeline-item的子元素&#xff0c;比如el-timeline-item__timestamp is-bottom"><…

vue判断定时任务此刻是否在任务时间段内

定时任务是每天都需要执行的操作&#xff0c;因此不能使用时间戳判断&#xff0c;时间戳是新建任务当天的时间。 思路&#xff1a;抛弃日期&#xff0c;转化成时间来操作。可以转化成秒来判断&#xff0c;这样可以省去判断时&#xff0c;然后分&#xff0c;然后秒的逻辑。 解…

vue使div滚动到最底部动画

例如添加更多任务按钮等等&#xff0c;每次点击都要新生成一条记录&#xff0c;为了方便用户操作&#xff0c;使页面点击的时候可以自己动画懂div的最底端 // <div ref"scroll"> ...</div>handleAddTask() {this.dataList.push({ //自己初始化的数据格…

【收藏干货】axios配置大全

一、安装 1、 利用npm安装npm install axios --save 2、 利用bower安装bower install axios --save 3、 直接利用cdn引入<script src"https://unpkg.com/axios/dist/...;></script>二、例子 1、 发送一个GET请求 axios.get(/user?ID12345)//通过给定的ID来发…

vue判断图片是否存在,图片加载失败后显示默认图片

关于图片的一些小utils 1.判断图片是否存在 export function CheckImgExists(imgurl) {var ImgObj new Image() // 判断图片是否存在ImgObj.src imgurl// 存在图片if (ImgObj.fileSize > 0 || (ImgObj.width > 0 && ImgObj.height > 0)) {return true} el…

让 AI 教机器自己玩俄罗斯方块

人工智能大火的今天&#xff0c;如果还是自己玩俄罗斯方块未免显得太 LOW&#xff0c;为什么不对游戏升级&#xff0c;让机器自己去玩俄罗斯方块呢?有了这个想法之后&#xff0c;我用了两天时间去搜集了大量资料&#xff0c;在电脑死机好多次之后终于将 AI 俄罗斯方块实现了。…