五一长假过去了,又该努力工作了呀,先回忆一下深拷贝吧,毕竟这是面试高发笔试题。

变量的存储方式

  1. javascript变量包含两种不同数据类型的值:基本类型和引用类型
    • 基本类型值指的是简单的数据段,包括es6里面新增的一共是有6种,具体如下:number、string、boolean、null、undefined、symbol。
    • 引用类型值指那些可能由多个值构成的对象,只有一种如下:object。
  2. javascript的变量的存储方式:栈(stack)堆(heap)
    • 栈:自动分配内存空间,系统自动释放,里面存放的是基本类型的值和引用类型的地址(一个指针)
    • 堆:动态分配的内存,大小不定,也不会自动释放。里面存放引用类型的值。

基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。

引用类型的值是保存在内存中的对象。JavaScript 不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。 在操作对象时, 实际上是在操作对象的引用而不是实际的对象。

浅拷贝

想当年很长时间都觉得 Object.assign() 就是深拷贝,其实不然。

  1. 直接赋值
  2. Object.assign(target,source)
  3. …展开运算符

这些方法都不会深拷贝一个对象。
直接赋值相当于直接赋值对象的内存引用地址,assign和…都相当于是展开赋值,如果值是基本类型,那么会直接赋值,如果是引用类型,那么会赋值其引用对象的地址。

深拷贝

  1. JSON.parse(JSON.stringify(object))
    简单直接的方法,但是当对象出现循环引用是会报错,并且无法拷贝Symbol,undefined,function,不可枚举属性
  2. 递归赋值。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    function isObj(source){
    return (typeof source == 'object' || typeof source == 'function')&& source!null
    }
    // 只解决date,reg类型,其他的可以自己添加

    function deepCopy(obj, hash = new Map()) {
        let cloneObj
        let Constructor = obj.constructor
        switch(Constructor){
            case RegExp:
                cloneObj = new Constructor(obj)
                break
            case Date:
                cloneObj = new Constructor(obj.getTime())
                break
            default:
                if(hash.has(obj)) return hash.get(obj) // 针对环
                cloneObj = new Constructor()
                hash.set(obj, cloneObj)
        }
        for (let key in obj) {
            cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
        }
        return cloneObj
    }