js中的栈、堆、队列详解;执行栈、执行上下文理解

参考与:
  js中的栈、堆、队列、内存空间
《前端进击的巨人(一):执行上下文与执行栈,变量对象》

栈(stack) 、堆(heap)、 队列(queue)是js的三种数据结构。

一、栈

栈的特点:后进先出,数据存储时只能从顶部逐个存入,取出时也需从顶部逐个取出。
在js中使用数组模拟栈:

let arr = [1, 2, 3, 4, 5];
let a = arr.push(6);	// 存入数据(进栈) arr -> [1, 2, 3, 4, 5, 6]
let b = arr.pop();	// 取出数据(出栈) arr -> [1, 2, 3, 4, 5]
console.log(a,b)	//6 6

二、堆

存储方式:无序的键值对。堆的存取方式跟顺序没有关系,不局限出入口。使用key取出value。

三、队列

队列的特点:先进先出,数据存取时"从队尾插入,从队头取出"。
在js中使用数组模拟队列:

let arr = [1, 2, 3, 4, 5];
let a = arr.push(6);    // 存入数据(进栈) arr -> [1, 2, 3, 4, 5, 6]
let b = arr.shift();    // 取出数据(出栈) arr -> [2, 3, 4, 5, 6]
console.log(a,b)//6 1

四、栈、堆、队列在JavaScript中的应用

①、内存存储(栈、堆)

JavaScript中变量类型有两种:

  • 基础类型(Undefined, Null, Boolean, Number, String, Symbol)一共6种
  • 引用类型(Object,如数组(Array)、对象({})、函数(function)、日期(Data)等)
    JS中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问,数据在栈内存中的存储与使用遵循后进先出的原则。

JS的引用数据类型,比如数组(Array)、函数(function)、日期(Data)等,它们值的大小是不固定的保存在堆内存中的对象。JS不允许直接操作对象的堆内存空间,而是通过引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。
在这里插入图片描述

②、函数调用栈(执行栈)

这里参考了:《前端进击的巨人(一):执行上下文与执行栈,变量对象》
JavaScript中函数的执行过程,其实就是一个入栈出栈的过程:

  • 当脚本要调用一个函数时,JS解析器把该函数推入栈中(push)并执行
  • 当函数运行结束后,JS解析器将它从堆栈中推出(pop)

  说到执行栈,首先要明白一个概念—>执行上下文:当代码运行时,会产生一个对应的执行环境,在这个环境中,所有变量会被事先提出来(变量提升),有的直接赋值,有的为默认值 undefined,代码从上往下开始执行,就叫做执行上下文。也就是说执行上下文就是js代码的执行环境(Execution Context)。

  代码的常用执行环境有两个:全局环境和函数环境。与之对应的就有全局执行上下文和函数执行上下文。

  JavaScript运行时首先会进入全局环境并生成全局执行上下文。程序代码中基本都会存在函数,那么调用函数,就会进入函数执行环境并生成该函数的执行上下文。在js中,通过栈的存取方式来管理执行上下文,我们可称其为执行栈,也叫函数调用栈(Call Stack)。程序执行进入一个执行环境时其执行上下文就会被推入执行栈中(入栈);
程序执行完成时,它的执行上下文就会被销毁,并从栈顶被推出(出栈),控制权交由下一个执行上下文。
  因为js执行中最先进入全局环境,所以处于"栈底的永远是全局环境的执行上下文"。而处于"栈顶的是当前正在执行函数的执行上下文",当函数调用完成后,它就会从栈顶被推出(理想的情况下,闭包会阻止该操作,闭包后续文章深入详解)。全局环境只有一个,对应的全局执行上下文也只有一个,只有当页面被关闭之后它才会从执行栈中被推出,否则一直存在于栈底。

函数调用栈小结:

js是单线程,在主进程上运行
栈顶的执行上下文处于执行中,其它需要排队
全局上下文只有一个且处于栈底,页面关闭时出栈
函数执行上下文可存在多个,但应避免递归时堆栈溢出
函数调用时就会创建新的上下文,即使调用自身,也会创建不同的执行上下文

执行上下文的生命周期
执行上下文的生命周期有两个阶段:①创建阶段(进入执行上下文)②执行阶段(代码执行)
创建阶段:函数被调用时,进入函数环境,为其创建一个执行上下文,此时进入创建阶段。
执行阶段:执行函数中代码时,此时执行上下文进入执行阶段。

1)、创建阶段的操作
①创建变量对象

  • 函数环境会初始化创建 Arguments对象(并赋值)
  • 函数声明(并赋值)
  • 变量声明,函数表达式声明(未赋值)

②确定this指向(this由调用者确定)
③确定作用域(词法环境决定,哪里声明定义,就在哪里确定)
执行上下文构成如下:
在这里插入图片描述
2)、执行阶段的操作
①变量对象赋值

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

②调用函数
③顺序执行其它代码

执行上下文生命周期声明:
  当进入到一个执行上下文后,这个变量对象才会被激活,所以叫活动对象(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 = 1;
    function bar() {
    	return 2;
    }
    var bar = function() {
        return 3;
    }
}
foo();	// bar(){return 2;},函数声明优先 > 变量、函数表达式

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

③. 事件轮询(队列)

JavaScript中事件轮询(Event Loop)的执行机制,就是采用队列的存取方式,后面再讨论。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值