JS简单实现一个深拷贝

大家好,深拷贝在面试中屡见不鲜, 今天试试简单的深拷贝和大家分享一波~~  

首先, 常见的拷贝方法如下, 但是各有利弊:

  1. Object.assign( target, source)    //  target 是目标对象, source是数据源
    
  2. JSON.parse(JSON.stringify())   // 序列化 ,反序列化实现

建议:可以学习 loash 中的拷贝方法, 是一个比较全面的克隆处理解决方案, 此处不做说明; 因为本菜鸟还没研究过,后续研究研究, 哈哈~~

第一:Object.assign( target, source)

只能克隆第一层, 第二层克隆了引用地址, 无法实现深度克隆。 个人理解, 其实严格意义可以理解为浅拷贝

let obj = {
    name: '美妞',
    testObj : {
        a: 12
    },
    list: ['string', 123]
}
let newObj ={}

Object.assign(newObj, obj)

newObj.list[1] = 100
newObj.testObj.a = 200

console.log("obj", obj)
console.log("newObj", newObj)

打印结果为: 对克隆后数据进行修改,源数据也会被改掉, 所以, Object .assing()  是无法实现深拷贝的

第二: JSON.parse(JSON.stringify())

其过程说白了 就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象;序列化的作用是存储(对象本身存储的只是一个地址映射,如果断电,对象将不复存在,因此需将对象的内容转换成字符串的形式再保存在磁盘上 )和传输。

不过,这种实现深拷贝的方法有局限性, 它只适用于一般的数据拷贝(对象, 数组), 有些需要注意:

(1) 如果json里面有时间对象, 则序列化结果: 时间对象=>字符串形式

   let source = {
            name: '美妞',
            date: new Date()
        }

    let target = JSON.parse(JSON.stringify(source))
    
    console.log('source', source)
    console.log('target', target)

    console.log(typeof source.date)   // object
    console.log(typeof target.date)   // string

(2)如果Json里边有RegExp, Error对象, 则序列化的结果将只得到空的对象RegExp, Error ={}

let source = {
            name: '美妞',
            reg: new RegExp('\\w+'),
            err: new Error('error message')
        }

    let target = JSON.parse(JSON.stringify(source))

    console.log('source', source)
    console.log('target', target)

(3)如果对象中存在循环引用的情况, 无法实现深拷贝

   let source = {
            name: '美妞',
        }
    source.source = source

    let target = JSON.parse(JSON.stringify(source))

    console.log('source', source)
    console.log('target', target)

(4) 如果json中有undefined , function。 则序列化的结果会把function, undefined丢失

let source = {
            name: '美妞',
            fn:function () {
                console.log('fn')
            },
            aa: undefined
        }


    let target = JSON.parse(JSON.stringify(source))

    console.log('source', source)
    console.log('target', target)

(5)如果json里面有NaN, infinity 和 -infinity, 则序列化的结果会变成null

   let source = {
            name: '美妞',
            aa: NaN,
            isInfinite: 1.7976931348623157E+10308,
            minusInfinity: -1.7976931348623157E+10308
        }
    
    let target = JSON.parse(JSON.stringify(source))

    console.log('source', source)
    console.log('target', target)

  

 

(6)如果json里面有对象是由构造函数生成的, 则序列话的结果会丢弃对象的constructor

 function  Student(name) {
        this.name = name;
    }

    let source = {
            name: '美妞',
            s: new Student('活宝')
        }

    let target = JSON.parse(JSON.stringify(source))

    console.log('source', source)
    console.log('target', target)

以下是手写实现一个深拷贝

 // 1. 判断类型  2. 递归
    let isType = (obj, type) => {
        if (typeof obj !== 'object') return  false;
        let typeString = Object.prototype.toString.call(obj)
        let flag;
        switch (type) {
            case "Array":
                flag = typeString == "[object Array]";
                break;
            default:
                flag = false;
        }
        return  flag;
    }

    // source 原对象  target 克隆后的对象
    let argS = []; let argT = [];
    let deepClone = source => {
        if (typeof source === null)  return  null;
        if (typeof source !== 'object') return source;
        let target, proto;
        if (isType(source, "Array")){
           
            target = []
        } else {
            // 拿到source原型对象
            
            proto = Object.getPrototypeOf(source);
            target = Object.create(proto);
        }

        // 判断是是否克隆结束
        let index = argS.indexOf(source);
        if (index !== -1) {
            return target[index]
        }
        argS.push(source);
        argT.push(target);

        // 对象内部循环处理
        for (let i in source) {
            target[i] = deepClone(source[i]);
        }

        return target;
    }

    let sourceData = {
        a: '5555',
        b: {
            name: 'test'
        },
        c: [2,3,4],
        d: function () {
            console.log(222)
        }
    }

    let targetData = deepClone(sourceData);

    targetData.b.name = 'test--大'
    targetData.c[0] = 100
    targetData.d =function () {
        console.log(33333)
    }
    // sourceData.d()
    // targetData.d()
    // console.log(Object.prototype.toString.call(sourceData.c))
    // console.log(Object.prototype.toString.call(targetData.c))

    console.log("sourceData", sourceData)
    console.log("targetData", targetData)

 

 

实现方式比较啰嗦, 但是可以解决简单的拷贝。。。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值