JavaScript的深浅拷贝

605 阅读2分钟

浅拷贝

浅拷贝:简单拷贝对象的最外层属性,对于对象更深层次的对象属性不做处理,那就会导致拷贝的对象和原始对象的深层次属性指向的是同一块内存。如果一个对象改变了这个内存的地址,那么会影响到另一个对象。 把一个对象序列化成JSON字符串,再通过JSON.parse()方法将字符串生成一个新的对象,实现深拷贝。

object.assign()

该方法可以进行浅拷贝,语法:

Object.assign(target,...sources)

但是object.assign(): 1、不拷贝对象的继承属性 2、不拷贝对象的不可枚举属性 3、可以拷贝symbol类型的属性

扩展运算符

通过扩展运算符,可以在构造对象的同时完成浅拷贝的功能。

concat

拷贝数组

slice()

可以通过截取数组的方式实现拷贝功能

实现浅拷贝源码:

        const shallowClone = (target) => {
            if (typeof target === 'object' && target !== null) {
                const cloneTarget = Array.isArray(target) ? [] : {};
                for (let prop in target) {
                    if (target.hasOwnProperty(prop)) {
                        cloneTarget[prop] = target[prop];
                    }
                }
                return cloneTarget;
            } else {
                return target;
            }
        }

深拷贝

把一个对象从内存中完整的复制一份给一个新对象,并且在堆内存中开出一个全新的空间来存放新对象,当对新对象的修改并不会影响到原对象。

JSON.stringify

但是,JSON.stringify()方法会出现几种情况: 1、拷贝的对象的值有函数、undefined、symbol,JSON.stringify序列化后该键值对会消失 2、拷贝Date类型引用类型会变为字符串 3、无法拷贝不可枚举类型的属性 4、无法拷贝对象的原型链 5、拷贝RegExp引用类型会转变为空对象 6、对象中含有NaN,Infinity,-Infinity,JSON.stringify的结果变成null 7、不能拷贝对象的循环应用

        function Obj() {
            this.func = function () { alert(90) };
            this.obj = { age: 18 };
            this.arr = [1, 24, 4];
            this.und = undefined;
            this.reg = /123/;
            this.date = new Date();
            this.NaN = NaN;
            this.infinity = Infinity;
            this.sym = Symbol(2);
        }

        let obj0 = new Obj();

        Object.defineProperty(obj0, "innumerble", {
            enumerable: false,
            value: "45678"
        })

        console.log("obj0", obj0)
        let obj1 = JSON.stringify(obj0);
        console.log("obj1", obj1);

实现深拷贝的源码:

 		const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)

        const deepClone = function (obj, hash = new WeakMap()) {
            if (obj.constructor === Date)
                return new Date(obj)       // 日期对象直接返回一个新的日期对象
            if (obj.constructor === RegExp)
                return new RegExp(obj)     //正则对象直接返回一个新的正则对象
            //如果循环引用了就用 weakMap 来解决
            if (hash.has(obj)) return hash.get(obj)
            let allDesc = Object.getOwnPropertyDescriptors(obj)
            //遍历传入参数所有键的特性
            let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
            //继承原型链

            hash.set(obj, cloneObj)
            for (let key of Reflect.ownKeys(obj)) {
                cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
            }
            return cloneObj
        }