浅克隆

只把第一级克隆一份过来,第二级及以后和原始对象公用相同的堆地址

slice 实现

Array 中的 sliceconcat 方法,不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组

let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];

let arr2 = arr1.slice(0);

console.log(arr1 === arr2); // false
console.log(arr1[2] === arr2[2]); // true

concat 实现

Array 中的 sliceconcat 方法,不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组

let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];

let arr2 = arr1.concat([]);

console.log(arr1 === arr2); // false
console.log(arr1[2] === arr2[2]); // true

展开运算符实现

let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];

let arr2 = [...arr1];

console.log(arr1 === arr2); // false
console.log(arr1[2] === arr2[2]); // true

Object.assign() 实现

let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = Object.assign([], arr1);

console.log(arr1);
console.log(arr2);
// [ 10, 20, { name: '旺旺' }, /^\d+$/, [Function] ]
// [ 10, 20, { name: '旺旺' }, /^\d+$/, [Function] ]

arr1[2].name = "张三"
console.log(arr1);
console.log(arr2);
// [ 10, 20, { name: '张三' }, /^\d+$/, [Function] ]
// [ 10, 20, { name: '张三' }, /^\d+$/, [Function] ]

jQuery 中的 $.extend

jQuery$.extend(deep, target, object1, objectN) 方法可以进行深浅拷贝,各参数说明如下:

  • deep:如过设为 true 为深拷贝,默认是 false 浅拷贝;
  • target:要拷贝的目标对象;
  • object1:待拷贝到第一个对象的对象;
  • objectN:待拷贝到第 N 个对象的对象;
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script>
        let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
        let arr2 = [];

        /* arr1 合并到 arr2 中 */
        $.extend(false, arr2, arr1);
        console.log(arr2);
    </script>
</body>

深克隆

基于 JSON 方法实现

局限性:

  • 不能复制 函数undefinedSymbolBigInt
  • 不能处理循环引用
  • 丢失原型链
  • 不能正确处理 DateRegExpMapSet 等特殊对象
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = JSON.parse(JSON.stringify(arr1));
  
// [ 10, 20, { name: '旺旺' }, {}, null ]
// 正则变成 {}
// 函数变成 null
console.log(arr2);

jQuery 中的 $.extend

jQuery$.extend(deep, target, object1, objectN) 方法可以进行深浅拷贝,各参数说明如下:

  • deep:如过设为 true 为深拷贝,默认是 false 浅拷贝;
  • target:要拷贝的目标对象;
  • object1:待拷贝到第一个对象的对象;
  • objectN:待拷贝到第 N 个对象的对象;
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script>
        let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
        let arr2 = [];

        /* arr1 合并到 arr2 中 */
        $.extend(true, arr2, arr1); // 第一个参数为 true 为深度合并
        console.log(arr2);
    </script>
</body>

递归实现

function _type(value) {
    return Object.prototype.toString.call(value);
}

// WeakMap 弱连接,key 必须是对象,这意味着,如果没有其他引用指向一个对象,那么这个对象可以被垃圾回收器回收,而不需要手动从 WeakMap 中删除
function _deepClone(obj, hash = new WeakMap()) {
    if (obj == null) return obj; // null、undefined 直接返回
    if (typeof obj !== "object") return obj; // 基本数据值、函数 直接返回

    if (_type(obj) === "[object RegExp]") return new RegExp(obj); // 正则
    if (_type(obj) === "[object Date]") return new Date(obj); // 日期

    // 对象(object、array、......) 放入 WeakMap 备忘录
    if (hash.has(obj)) return hash.get(obj);

    // obj.constructor:找到的是所属类原型上的 constructor,保证传递进来什么类型的值,最后创建的 newObj 也是对应类型的实例
    let newObj = new obj.constructor();

    // 存储在备忘录中,避免重复克隆
    hash.set(obj, newObj);

    // 遍历数组和对象
    for (let key in obj) {
        if (!obj.hasOwnProperty(key)) break;
        // 递归判断每一个属性值
        newObj[key] = _deepClone(obj[key], hash);
    }

    return newObj;
}

// 测试
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/];
arr1.push(arr1); // 循环引用
console.log(deepClone(arr1) === arr1); // false

MessageChannel 实现

使用 MessageChannel 实现的深拷贝只能解决 undefined循环引用对象 的问题,对于 Symbolfunction 依然束手无策;

function deepClone(obj) {
    return new Promise((resolve) => {
        const { port1, port2 } = new MessageChannel();
        port1.postMessage(obj);
        port2.onmessage = (msg) => {
            resolve(msg.data);
        };
    });
}

// 测试
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/];
arr1.push(arr1); // 循环引用
console.log(deepClone(arr1) === arr1); // false
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 浅克隆
    1. 1.1. slice 实现
    2. 1.2. concat 实现
    3. 1.3. 展开运算符实现
    4. 1.4. Object.assign() 实现
    5. 1.5. jQuery 中的 $.extend
  2. 2. 深克隆
    1. 2.1. 基于 JSON 方法实现
    2. 2.2. jQuery 中的 $.extend
    3. 2.3. 递归实现
    4. 2.4. MessageChannel 实现