每日一知识:手写深拷贝和浅拷贝(解决了循环引用的问题)

一.浅拷贝

如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址。拷贝时,开辟一块新的内存,将原始的属性的值(基本数据类型)或地址(引用数据类型)拷贝到新开辟的内存。
浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象

即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

JavaScript中,存在浅拷贝的现象有:

  • Object.assign
  • Array.prototype.slice(),Array.prototype.concat()
  • 使用拓展运算符实现的复制
//Object.assign
var obj = {
    age: 18,
    nature: ['smart', 'good'],
    names: {
        name1: 'fx',
        name2: 'xka'
    },
    love: function () {
        console.log('fx is a great girl')
    }
}
var newObj = Object.assign({}, obj);
obj.age = 20;
obj.nature.push('999')
console.log(newObj.age);//20
console.log(newObj.nature);//['smart', 'good',99]

//slice()  
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.slice(0)
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]

//concat()
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.concat()
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]

//拓展运算符
let a = {
	b:{
		c:1
	}
}

let aCopy = {...a}
a.b.c = 99
console.log(aCpoy.b.c) // 99

二.深拷贝

深拷贝开辟一个新的栈,两个对象的属性完全相同,但是对应两个不同的地址,不会改变另一个对象的属性。
即深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象

常见的深拷贝:

  • ._cloneDeep()
  • JQuery.extend()
  • JSON.stringify()
  • 手写循环递归(腾讯云智面试题)
JSON.parse(JSON.stringify(obj))
cosnt obj2 = JSON.parse(JSON.stringify(obj1))

这种方法有弊端,会忽略undefined、Symbol、和函数

  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。

  • 函数、undefined 被单独转换时,会返回 undefined,如 JSON.stringify(function(){}) 或者 JSON.stringify(undefined)

  • 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误等。对于有循环引用的对象来说,它的嵌套层级是无限深的,所以序列化的时候会进入死循环或者死递归

循环递归(解决了循环引用)

腾讯云智面试问到了,循环引用,那个时候我不会…

3.手写深拷贝和浅拷贝(解决了循环引用的问题)

    <script>
        /* 
       1.浅拷贝
       常见的浅拷贝有:
           Object.prototype.assign
           Array.prototype.slice() 
           Array.prototype.concat()
           拓展运算符
       */
        // 手写浅拷贝
        // 对基础类型做最基本的拷贝;
        // 对引用类型开辟新的存储,并且拷贝一层对象属性。
        function shallowClone(obj) {
            // 判断是否为引用数据类型 
            if (!obj || typeof obj !== 'object') return obj;
            // 若是,则根据obj的类型判断是新建数组还是对象
            let newObj = Array.isArray(obj) ? [] : {};
            //for in返回对象属性值,或数组的下标,数组不建议用for...in,这里就不计较了
            for (let key in obj) {
                //判断当前对象是否有自身的属性 不包括继承
                if (obj.hasOwnProperty(key)) {
                    //直接将该属性赋值给新对象
                    newObj[key] = obj[key]
                }
            }
            return newObj
        }
        // const str = '123'
        // const str2 = shallowClone(str)
        // console.log(str2); //123

        // const obj1 = {
        //     age:18,
        //     school:{
        //         name:'htu'
        //     }
        // }
        // const obj2 = shallowClone(obj1)

        // obj1.age = 99
        // obj1.school.name = '清华'
        // console.log(obj2); //{age: 99, school: {name:'清华'}}

        // 浅拷贝
        const obj1 = {
            name: 'init',
            arr: [1, [2, 3], 4],
        };
        const obj3 = shallowClone(obj1) // 一个浅拷贝方法
        console.log(obj1 === obj3);//false
        obj3.name = "update";
        obj3.arr[1] = [5, 6, 7]; // 属性的引用类型,新旧对象还是共享同一块内存

        console.log('obj1', obj1) // obj1 { name: 'init',  arr: [ 1, [ 5, 6, 7 ], 4 ] }
        console.log('obj3', obj3) // obj3 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }


        /* 
        2. 深拷贝
            常见的深拷贝:
                _.cloneDeep()
                jQuery.extend()
                JSON.parse(JSON.stringify(obj))
                手写循环递归
        */
        //    JSON.parse(JSON.stringify(obj))
        const obj = {
            name: 'A',
            name1: undefined,
            name3: function () { },
            name4: Symbol('A')
        }
        const obj2 = JSON.parse(JSON.stringify(obj))
        console.log(obj2); //{name:'A'}

        //  循环递归
        function deepClone(obj) {
            if (typeof obj !== 'object' || obj == null) return obj
            let result = null;
            //result初始化
            obj instanceof Array ? result = [] : result = {}

            for (let key in obj) {
                //不复制继承的属性
                if (obj.hasOwnProperty(key)) {
                    result[key] = deepClone(obj[key]);
                }
            }
            return result
        }
        // 测试
        oldObj = {
            name: 'oldObj',
            age: 18,
            friend: {
                name: 'oldFriend',
                school: {
                    name: '清华'
                }
            },
            fn: function () {
                console.log(1);
            }
        }
        const newObj = deepClone(oldObj)
        oldObj.friend.name = 'oldFriend1111'
        oldObj.friend.school.name = '北大'
        console.log(oldObj);
        console.log(newObj);

        // 深拷贝完整版(解决循环引用)
        function deepClonePlus(obj, hash = new WeakMap()) {
            if (obj == null) return obj; // 如果是null或者undefined我就不进行拷贝操作
            if (obj instanceof Date) return new Date(obj);
            if (obj instanceof RegExp) return new RegExp(obj);
            if (obj instanceof Error) return new Error(obj.message)
            // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
            if (typeof obj !== "object") return obj;
            // 是对象的话就要进行深拷贝,hash判断是否已存在当前对象
            if (hash.has(obj)) return hash.get(obj);

            let cloneObj = new obj.constructor(); //新建空数组或空对象
            // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身(构造函数)
            hash.set(obj, cloneObj);
            for (let key in obj) {
                if (obj.hasOwnProperty(key)) {
                    // 实现一个递归拷贝
                    cloneObj[key] = deepClonePlus(obj[key], hash);
                }
            }
            return cloneObj;
        }
        let newObject = {
            name: 'shuaige',
            age: 12,
            boo: true,
            n: null,
            un: undefined,
            sy: Symbol('xx'),
            big: 10n,
            child: {
                ele: 'boby',
                x: 100
            },
            arr: [1, 2, 3],
            reg: /^\d+$/,
            fn: function () {
                console.log(this.name);
            },
            time: new Date(),
            err: new Error()
        }
        let newObjectClone = deepClonePlus(newObject)

        newObject.loop = newObject//循环引用.
        console.log(newObject);
        console.log(newObjectClone);
    </script>

4.总结

前提为拷贝的类型为引用类型的情况下:

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址(注意是属性的,而不是拷贝的对象本身指向同一地址)
  • 深拷贝是递归拷贝深层是,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值