js堆与栈的应用

堆与栈

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

栈(stack)

栈的特点是 “LIFO,即后进先出(Last in, First out)”。数据存储时只能从顶部逐个存入,取出时也去要从顶部逐个取出。例如搭建积木

堆(heap)

堆的特点是 “无序” 的key-value “键值对”储存方式。例如书架存书

堆的存取方式跟顺序无关,不局限出入口

队列(queue)

队列的特点是 “FIFO,即先进先出(First in First out)”。数据存取时 “从尾部插入,从头部取出”

stack_heap_queue

堆/栈/队列在js中堆应用

代码运行方式(栈应用/函数调用栈)
javascript中的函数的执行过程,其实就是一个入栈出栈的过程:
1.当脚本要调用一个函数时,js解释器把该函数推入栈中并执行
2.当函数运行结束后,ja解释器将它从堆栈中弹出
事件轮询(队列)

javascript中事件轮询(Event Loop)的执行机制,就是采用队列的存取方式

内存存储(堆/栈)

javascrip中变量类型有两种:

基础类型:string/number/boolean/null/undefined/symbol

引用类型:object

js中的基础数据类型,这些只都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按照值访问,数据在内存中的存储与使用方式类似于数据结构中的堆栈数据结构,遵循后见先出的原则。

js中引用数据类型,比如数组,他们值的大小都是不固定的。引用数据类型的值是保存在堆内存中的对象。js不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。每次使用const或者let去初始化一个变量的时候,会首先便利当前的内存栈,看看有没有重名的变量,有的话就反悔错误。stack_heap

赋值与赋址

引擎不能直接操作堆内存中的数据,这就造成了堆同一个变量赋不同的值,会出现完全不同的效果:为一个变量赋基本值时,实际上是创建一个新值,然后把该值赋给新的变量,可以说这是一种真正意义上的“赋值”;为一个变量赋引用值时,实际上是为新变量添加一个指针,指向堆内存中的一个对象,属于一种“赋址”操作。

// 基本值
var a = 1
var b = a
b = 2

console.log(a) // 1
console.log(b) // 2

// 引用值
var c = [0, 1, 2]
var d = c
c[0] = 5

console.log(c) // [5, 1, 2]
console.log(d) // [5, 1, 2]

赋值与赋址

浅拷贝

浅拷贝可以简单理解为,发生在栈中的拷贝行为,只能拷贝基本值和引用值的地址

Object.assign() 数组的slice/concat都属于浅拷贝

let a = {
  name: 'Tom',
  obj: {
    age: 19
  }
}
let b = Object.assign({}, a) // 浅拷贝
let c = a // 赋值

a.name = 'Amy'
a.obj.age = 20

console.log(a) // {name: 'Amy', obj: {age: 20}}
// 浅拷贝拷贝了基础值‘Tom’ 和 对象obj的地址
console.log(b) // {name: 'Tom', obj: {age: 20}}
// a为对象,所以在此是‘赋址’
console.log(c) // {name: 'Amy', obj: {age: 20}}



var arr1 = [0, [1], [2]]
var arr2 = arr1.slice(0)
arr1[0] = 8
arr1[1][0] = 9
arr1[2] = 10

console.log(arr1) // [8, [9], 10]
// 浅拷贝arr2拷贝了arr1[0]的值 8 ,
// arr1[1]的数组地址p1 = [1],
// arr1[2]的数组地址p2 = [2]
// 改变p1指的的数组的下标0的值为9,自然会同步到arr2
// 把arr1[2]的值由 p2 改为 10,并不影响arr2的值仍然是 p2,所以arr2[2] 依然是[2]
console.log(arr2) // [0, [9], [2]]

深拷贝

深拷贝可以理解为,同时发生栈中和堆中的拷贝行为,除了拷贝基本值和引用值的地址之外,地址指向的堆中的对象也会发生拷贝

function deepClone(target) {
  if (typeof target !== 'object' || target === null) return target
  var result = target instanceof Array ? [] : {}
  for (let key in target) {
    if (Object.prototype.hasOwnProperty.call(target, key)) {
      if (target[key] && typeof target === 'object') {
        result[key] = deepClone(target[key])
      } else {
        result[key] = target[key]
      }
    }
  }
  return result
}

var a = [1, 2, 3, { a: 'a' }]
var b = deepClone(a)
a[3].a = 'b'
console.log(b) // [1, 2, 3, { a: 'a' }]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s-alone

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值