美文网首页WEB前端程序开发前端开发笔记JavaScript 进阶营
javascipt-实现对象的拷贝(深拷贝和浅拷贝)

javascipt-实现对象的拷贝(深拷贝和浅拷贝)

作者: 家里有棵核桃树 | 来源:发表于2018-10-29 18:29 被阅读10次

    1、前言

    将一个变量的值赋值给另外一个变量,如果是基本数据类型就很简单直接赋值就可以了。如果是引用数据类型赋值的是栈中的地址,它们共用同一块堆内存,所以对对象的(Object/Array)赋值就要做一些其它处理。


    image.png

    2、浅拷贝

    浅拷贝后两个变量指向的地址不一样,但是当属性值为对象类型时,只拷贝了对象数据的引用(用的还是同一个数据对象),导致新旧数据没有完全分离,还会互相影响。

    实现浅拷贝的几种方式:

    • 利用Object.assign()方法:let copyArr = Object.assign([], arr); let copyObj = Object.assign({}, obj);
    • 利用es6语法 扩展运算符"...": let copyArr = [...arr]; let copyObj = {...obj};
    • jquery extend方法: let copyArr = $.extend([], arr); let copyObj = $.extend({}, obj);

    3、深拷贝

    深拷贝后两个变量指向的地址不一样,并且两个变量属性值为引用类型的也指向不同的地址,两个变量互不影响。


    以下是相关的测试数据,以及判断两个引用类型变量是否相同或者部分相同的工具:

    let originObj = {
        name: 'Ada',
        age: 23,
        hobby: ['烘焙', '定期观影', '一阵一阵的看书', '...'],
        address: {
            province: 'sichuang',
            city: 'chengdu'
        },
        date: new Date(),
        reg: /123/,
        print: function (name) {
    
            console.log('hello~~' + name);
        }
    };
    let originArr = [1, 2, 3, {
        address: {
            province: 'sichuang',
            city: 'chengdu'
        }
    }];
    
    // 判断深拷贝是否成功
    function printDeepCopyResult(origin, deepCopy) {
    
        let copyFailArr = [];
        let copySuccessArr = [];
        for (let key in deepCopy) {
    
            if (!deepCopy.hasOwnProperty(key)) return;
    
            if (origin[key] instanceof Object) {
                // 判断引用类型的数据指向地址是否相同
                deepCopy[key] == origin[key] ? copyFailArr.push(key) : copySuccessArr.push(key);
            } else {
                // 判断基本数据类型值是否相同
                deepCopy[key] === origin[key] ? copySuccessArr.push(key) : copyFailArr.push(key);
            }
        }
    
        // 拷贝结果输出
        let result = copyFailArr.length ? copyFailArr.toString() : copySuccessArr.toString();
        return console.log(`深拷贝${copyFailArr.length ? '不' : ''}成功----${result}`);
    }
    

    2.1 循环+递归处理【最好用,考虑到的数据类型也比较完整】

    引用类型不仅仅只是'object' === typeof val;现在很多实现深拷贝的函数就只是考虑到了数据类型是数组和对象数据,诸如函数、时间对象、正则表达式等数据类型考虑的都不是很全。下面实现的深拷贝函数对大部分数据类型都考虑到了(symbol没有去实现)。

    function deepCopy1(origin) {
    
        let constructor = origin.constructor;
        let isArray = Array === constructor; // origin instanceof Array
        let copy = isArray ? [] : {}; // new origin.constructor;
    
        if (Function === constructor) {
    
            // 1、利用eval实现拷贝函数体---缺点:不能用es6的对象简写,不然会报错(就规规矩矩的写属性值为函数的属性吧)
            return eval(`(${origin.toString()})`);
    
            // 2、利用apply,这么做其实只是变相的调用了origin的方法,并没有实现函数的复制
            /*return function () {
                return origin.apply(null, arguments);
            };*/
        }
        if (RegExp === constructor) return new RegExp(origin);
        if (Date === constructor) return new Date(origin);
    
        for (let key in origin) {
    
            // 过滤掉继承的可枚举的属性
            if (origin.hasOwnProperty(key)) {
    
                // 1、copy[key] = 'object' == typeof origin[key] ? deepCopy1(origin[key]) : origin[key];
                // 2、copy[key] = 'object' == typeof origin[key] ? arguments.callee(origin[key]) : origin[key];
    
                // 1和2只考虑了为数组或者是对象的情况,未考虑到是正则、函数和日期对象等的情况
                copy[key] = origin[key] instanceof Object ? deepCopy1(origin[key]) : origin[key];
            }
        }
    
        return copy;
    }
    let deepCopyObj1 = deepCopy1(originObj);
    let deepCopyArr1 = deepCopy1(originArr);
    printDeepCopyResult(originObj, deepCopyObj1);
    printDeepCopyResult(originArr, deepCopyArr1);
    deepCopyObj1.print('world');
    
    打印结果

    2.2 利用JSON.stringify 和 JSON.parse

    这种方法能简单达到深拷贝效果,但是只能拷贝几种常见的数据类型:字符、数字、Object和Array。

    function deepCopy2(origin) {
    
        return JSON.parse(JSON.stringify(origin));
    }
    
    let deepCopyObj2 = deepCopy2(originObj);
    let deepCopyArr2 = deepCopy2(originArr);
    printDeepCopyResult(originObj, deepCopyObj2);
    printDeepCopyResult(originArr, deepCopyArr2);
    
    打印结果

    2.3 利用jquery extend()方法

    这种方法能简单达到深拷贝效果,但是只能拷贝几种常见的数据类型:字符、数字、Object和Array。

    let deepCopyObj3 = $.extend(true, {}, originObj);
    let deepCopyArr3 = $.extend(true, [], originArr);
    printDeepCopyResult(originObj, deepCopyObj3);
    printDeepCopyResult(originArr, deepCopyArr3);
    
    打印结果

    3、相关链接

    相关文章

      网友评论

        本文标题:javascipt-实现对象的拷贝(深拷贝和浅拷贝)

        本文链接:https://www.haomeiwen.com/subject/bjkltqtx.html