Javascript中的堆栈存储

堆、栈

  • 堆: 动态分配的内存,大小不定也不会自动释放。
  • 栈:自动分配的内存空间,它由系统自动释放;

js的数据类型

  • 基础数据类型(存储于栈内存)
    1. undefined
    2. null
    3. boolean
    4. string
    5. number
    6. symbol
    7. bigint
  • 引用数据类型(存储于堆内存,栈内存存储指针,指向堆内存地址)
    1. object
      a. function
      b. array
      c. date
      d. math
      e. regexp
      因为引用类型值的大小是不固定的,所以只能将其存储在可以动态分配内存的堆空间中。

深拷贝和浅拷贝

对于引用类型来说存在深拷贝和浅拷贝,深拷贝是在堆中再开辟一片空间存储拷贝的值,而浅拷贝则是直接拷贝指向堆内存的指针。

  • 浅拷贝:
    直接赋值或者用es6的object.assign ,可以将多个源对象复制到一个目标对象上。或者使用扩展运算符达到同一效果。

    • Object.assign()方法接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。它会修改目标对象,因此会触发 ES6 setter。
    • 扩展操作符(…)使用它时,数组或对象中的每一个值都会被拷贝到一个新的数组或对象中。它不复制继承的属性或类的属性,但是它会复制ES6的 symbols 属性。
  • 深拷贝

function deepClone (src, cache=[]) {
    // 在缓存中查找是否已经处理过
    for (let i = 0; i < cache.length; i++) {
        if (src === cache[i].src) {
            return cache[i].dist
        }
    }
    let type = Object.prototype.toString.call(src).slice(8, -1)
    let dist = null
    if (['Number', 'String', 'Boolean', 'Null', 'Undefined'].includes(type)) {
        return src
    } else if (type === 'Array') {
        dist = []
    } else if (type === 'Object') {
        dist = {}
    } else if (type === 'Date') {
        dist = new Date(src)
    } else if (type === 'RegExp') {
        dist = new RegExp(src.source, src,flags)
    } else if (type === 'Function') {
        dist = src.bind(this)
    }
    // 放入缓存
    cache.push({src, dist})
    for (let key in src) {
        // 验证是否是自身的属性
        if (src.hasOwnProperty(key)) {
            dist[key] = deepClone(src[key], cache)
        }
    }
    return dist
}
let obj = {
    a: 1, b: 2,
    c: {
        aa: 1, bb: 22
    }
}
obj.o = obj
let obj2 = deepClone(obj)
console.log(obj)
console.log(obj2)
console.log(obj2 === obj)

JS函数中参数的传递方式

  • 基础类型直接按值传递
  • 引用类型传值实际上传递的是参数地址
function test(person) {
  person.age = 26
  person = {
    name: 'hzj',
    age: 18
  }
  return person
}
const p1 = {
  name: 'fyq',
  age: 19
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?

p1:{name: “fyq”, age: 26}
p2:{name: “hzj”, age: 18}

原因: 在函数传参的时候传递的是对象在堆中的内存地址值,test函数中的实参person是p1对象的内存地址,通过调用person.age
= 26确实改变了p1的值,但随后person变成了另一块内存空间的地址,并且在最后将这另外一份内存空间的地址返回,赋给了p2。

内存回收

为了使程序运行时占用的内存最小,通常要实现垃圾回收机制。
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈存里,随着方法的执行结束,这个方法的栈存也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;
当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本开销较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。

栈的实现

class Stack {
    constructor (arr) {
        this.arr = arr
    }
    push (val) {
        this.arr.push(val)
    }
    pop () {
        return this.arr.pop()
    }
    peek () {
        return this.arr.peek()
    }
}

function transform (num) {
    let stackArr = new Stack([]);
    while (num > 0) {
        stackArr.push(num % 2);
        num = Math.floor(num / 2);
    }
    return stackArr.arr.reverse().join('');
}
let a = transform(100)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值