深拷贝与浅拷贝

谈到深拷贝与浅拷贝首先要将数据类型的概念。js中有两种类型的数据,基本类型和引用类型。基本类型有undefined,null,String,Number,Boolean,Symbol,BigInt。引用类型有Object,Array,Function,Date,Map,Math等。为什么要在这里说这个呢?

深拷贝和浅拷贝都是针对引用类型的。因为基本类型在存储中都是直接存在栈中的,而引用类型的内容是存在堆中,栈中存放的是这个引用类型的地址,然后有个指针指向堆中的数据。

  • 浅拷贝

浅拷贝(一层),仅仅是复制了引用,彼此之间的操作会互相影响。

  • 深拷贝

深拷贝(多层),在堆中重新分配内存,不同的地址,相同的值,互不影响

浅拷贝实现

// 使用Object.assign解决
// 使用Object.assign(),你就可以没有继承就能获得另一个对象的所有属性,快捷好用。 
// Object.assign 方法只复制源对象中可枚举的属性和对象自身的属性。
let obj = { a:1, arr:[2,3]};
let res = Object.assign({}, obj)

console.log(res.arr === obj.arr); // true,指向同一个引用
console.log(res === obj); // false
// 使用扩展运算符(…)来解决
let obj = { a:1, arr:[2,3]};
let res = {...obj};

console.log(res.arr === obj.arr); // true,指向同一个引用
console.log(res === obj); // false

深拷贝实现

// 开发中常用JSON序列化来进行深拷贝,很好用
// 可以通过 JSON.parse(JSON.stringify(object)) 来解决
let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

// 但是该方法也是有局限性的:
// 1.会忽略 undefined
// 2.不能序列化函数(会忽略函数)
// 3.不能解决循环引用的对象(对象成环)
//     并且该函数是内置函数中处理深拷贝性能最快的。当然如果你的数据中含有以上三种情况下,可以使用 lodash 的深拷贝函数。

// 拓展一下,MessageChannel(异步通信,一般用于多个web worker并想要在两个web worker之间实现通信的时候)的postMessage传递的数据也是深拷贝的,这和web worker的postMessage一样。而且还可以拷贝undefined和循环引用的对象(函数不行)。
function deepCopy(obj) {
    return new Promise((resolve) => {
        const {port1, port2} = new MessageChannel();
        port2.onmessage = ev => resolve(ev.data);
        port1.postMessage(obj);
    });
}

deepCopy(obj).then((copy) => {
    let copyObj = copy;
    console.log(copyObj, obj)
    console.log(copyObj == obj)
});
// 递归实现深拷贝(递归比较耗性能,容易造成栈溢出,所以有尾递归的诞生,得看浏览器兼容性,理解尾递归得理解函数的调用帧,之后会专门有一篇讲这个。)
const deepClone = function (source, nullVal) {
    if (!source || typeof source !== 'object') {
        return source
    }
    const targetObj = source.constructor === Array ? [] : {}
    for (const keys in source) {
        if (source.hasOwnProperty(keys)) {
            if (source[keys] && typeof source[keys] === 'object') {
                targetObj[keys] = deepClone(source[keys])
            } else {
                // 传null防止undefined被过滤
                if (nullVal === null) {
                    targetObj[keys] = source[keys] === undefined ? nullVal : source[keys]
                } else {
                    targetObj[keys] = source[keys]
                }
            }
        }
    }
    return targetObj
}
Copyright © lujiannb@qq.com 2021 all right reserved,powered by Gitbook该文章修订时间: 2024-06-06 11:32:07

results matching ""

    No results matching ""