实现deepClone的三种方式

553 阅读2分钟

文章的开头总是很难水的,从前的作文也没有到过800字

深拷贝 和 浅拷贝(仅对引用类型,简单类型没有深浅拷贝的说法)

深拷贝和浅拷贝的区别

深拷贝:值拷贝,原数据改变不会影响拷贝后的数据

浅拷贝:引用地址拷贝,与其说是数据相互影响,实际上是同一个数据,就像你和你弟弟的爸爸,你弟弟的爸爸捡到了100元,等同于你的爸爸也捡到了100元

image.png

赋值操作也是最常见的浅拷贝

const obj1 = { name:"小帅哥" }
const obj2 = obj1

obj1.name = '大帅哥'
console.log(obj2.name)   // 大帅哥

如何实现深拷贝? 这里提供3种方式,很多人不知道这第3种

利用 JSON.stringify() 和 JSON.parse()

JSON.stringify()  方法将一个 JavaScript 对象或值转换为 JSON 字符串

JSON.parse()  方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象

优点:最简单, JavaScript 原生方法

缺点:

  1. 当值为 function , undefined ,symble 时会丢失
  2. 当值为 NaN , + - infinity 时,会被序列化为 null
  3. 循环引用直接报错
const obj  = {
    a:Symbol(1),
    b:1,
    c:NaN,
    d:()=>{},
    e:Infinity
}

console.log(JSON.parse(JSON.stringify(obj)));  // { b: 1, c: null, e: null }

当然是自己写一个简单的了

应该算是简单,我觉得 function 没有必要拷贝,如果在对象里面,直接返回就行了,Date Error RegExp

function deepClone(object) {
    // 如果是简单类型 或者 null 或者 function 直接返回
    if (typeof object !== 'object' || object == null) {
        return object
    }
    // 类型映射对象
    const hashMap = {
        '[object Object]': {},
        '[object Array]': []
    }
    // 处理循环引用
    const map = new Map()
    // 递归调用的函数
    function deepCloneTool(object) {
        // 如果是简单类型 或者 null 或者 function 直接返回
        if (typeof object !== 'object' || object == null) {
            return object
        }
        
        用来处理循环引用
        if (map.has(object)) {
            return map.get(object)
        }
        
        // 获取数据类型
        const type = Object.prototype.toString.call(object)
        
        // 返回结果
        const result = hashMap[type] || null
        
        map.set(object,result)
        
        // 这里可能存在 Date Error RegExp Map WeakMap Set WeakSet (有兴趣可以自己补一下)
        if (!result) {

            return '出现意料之外的类型'

        }

        for (let key in object) {

            const val = object[key]

            result[key] = deepCloneTool(val)

        }

        return result
    }

    return deepCloneTool(object)

}


通过 MessageChannel

MessageChannel 接口允许我们创建一个新的消息通道,并通过它的两个 MessagePort 属性发送数据。

信息在通道中传输的时候,也会有一个序列化的和反序列化的过程

缺点:当属性中含有 function symble 会报错

function deepClone(obj) {
    return new Promise((resolve, reject) => {
        const { port1, port2 } = new MessageChannel()

        port1.postMessage(obj)

        port2.onmessage = msg => {

            resolve(msg.data)

        }
    })
}
deepClone({ name: '大帅哥' }).then(res => {
    console.log(res);  // { name:'大帅哥' }
})