浅度和深度拷贝

浅拷贝:只复制指向某个对象的指针,而不是复制对象本身,新旧对象还是共享同一块内存。

这不是真正的拷贝,你不是真正的自我

深拷贝:另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会影响原对象。

突然的自我

接下描述三种拷贝方式,嗯。。。

拷贝方式

一、JSON.parse(JSON.stringify(obj))

非常的简单,深度拷贝,就这一行,当然戳手可得的东西总是藏着很多隐患。列举一二┗( ▔, ▔ )┛

  1. undefined、任意的函数以及 symbol 三个特殊的值无效
  • undefined、任意的函数以及 symbol 作为对象属性值时 JSON.stringify() 对跳过它们进行序列化,没了就是没了
  • undefined、任意的函数以及 symbol 作为数组元素值时,JSON.stringify() 会将它们序列化为 null
  • undefined、任意的函数以及 symbol 被 JSON.stringify() 作为单独的值进行序列化时,都会返回 undefined
  • 所有以 symbol 为属性键的属性都会被完全忽略掉
  1. 会抛弃对象的constructor,所有的构造函数会指向Object
  2. 对象循环引用会报错
  3. 不可枚举属性是不能拷贝的

二、Object.assign(target, …sources)

用于将所有可枚举属性的值从一个或多个sources复制到target(相当于两者求并集)。它将返回目标对象。
针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
比第一个好的地方在于可以拷贝三个特殊值,不会出错。
当然,不可枚举属性也是不能拷贝的。

三、递归调用(考虑循环引用)

WeakMap的设计目的在于,有时我们想在某个对象上面存放一些数据,Map会形成对于这个对象的引用。如果不删除这个引用,垃圾回收机制就不是释放。而WeakMap是弱引用,不计数。

function isObj(obj) {
    return typeof obj === 'object' && obj !== null
}
/**
* 深度拷贝
* @param obj 需要拷贝的对象
* @param hash 此参数不需要传参数
*/
 function clone (obj:any, hash = new WeakMap()):any {
    if(hash.has(obj)) return hash.get(obj)
    let cloneObj = Array.isArray(obj) ? [] : {}
    hash.set(obj, cloneObj)
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? clone(obj[key], hash) : obj[key];
    }
    return cloneObj;
 }

没解决的问题

当然啦,第三种方法还有不够全面,有些对象不能正确复制。

特殊对象:

Bool、Number、String、String、Date、Error、Map、Set这几种类型,我就称为特殊对象。

function cloneSymbol(targe) {
    return Object(Symbol.prototype.valueOf.call(targe));
}

function cloneReg(targe) {
    const reFlags = /\w*$/;
    const result = new targe.constructor(targe.source, reFlags.exec(targe));
    result.lastIndex = targe.lastIndex;
    return result;
}

function cloneOtherType(target) {
    const Ctor = target.constructor;
    switch (Ctor) {
        case Boolean:
        case Number:
        case String:
        case Error:
        case Date:
            return new Ctor(target.valueOf());
        case RegExp:
            return cloneReg(target);
        case Symbol:
            return cloneSymbol(target);
        case Map:
        case WeakMap:
        case Set:
        case WeakSet:
            return new Ctor([...target]);
        default:
            return null;
    }
}
函数:

普通函数直接浅复制就可以了,但是箭头函数浅复制不行。因为 this在函数执行时确认,不在定义时(this永远指向最后调用它的那个对象),但是箭头函数的this在定义时确定。 会导致什么问题呢?
那就是箭头函数如果是浅拷贝,它运行时this还指向拷贝的对象,而不是拷贝后的新对象,是不是很可怕?|墙|ョ゚ェ゚;)

//判断是否为箭头函数
function isArrowFunction(obj) {
    return typeof obj === 'function' && obj.prototype  === undefined;
}
//拷贝箭头函数
function copyFunction(func) {
    const bodyReg = /(?<={)(.|\n)+(?=})/m;
    const paramReg = /(?<=\().+(?=\)\s+{)/;
    const funcString = func.toString();
    if (func.prototype) {
        console.log('普通函数');
        const param = paramReg.exec(funcString);
        const body = bodyReg.exec(funcString);
        if (body) {
            console.log('匹配到函数体:', body[0]);
            if (param) {
                const paramArr = param[0].split(',');
                console.log('匹配到参数:', paramArr);
                return new Function(...paramArr, body[0]);
            } else {
                return new Function(body[0]);
            }
        } else {
            return null;
        }
    } else {
        return eval(funcString);
    }
}
不可枚举属性和属性名为Symbol的 :

Object.getOwnPropertyNames() 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。

最终版本

function isObj(obj) {
    return typeof obj === 'object'  && obj !== null;
}

function isArrowFunction(obj) {
    return typeof obj === 'function' && obj.prototype  === undefined;
}

function copyFunction(func) {
    const bodyReg = /(?<={)(.|\n)+(?=})/m;
    const paramReg = /(?<=\().+(?=\)\s+{)/;
    const funcString = func.toString();
    if (func.prototype) {
        console.log('普通函数');
        const param = paramReg.exec(funcString);
        const body = bodyReg.exec(funcString);
        if (body) {
            console.log('匹配到函数体:', body[0]);
            if (param) {
                const paramArr = param[0].split(',');
                console.log('匹配到参数:', paramArr);
                return new Function(...paramArr, body[0]);
            } else {
                return new Function(body[0]);
            }
        } else {
            return null;
        }
    } else {
        return eval(funcString);
    }
}
function cloneSymbol(targe) {
    return Object(Symbol.prototype.valueOf.call(targe));
}

function cloneReg(targe) {
    const reFlags = /\w*$/;
    const result = new targe.constructor(targe.source, reFlags.exec(targe));
    result.lastIndex = targe.lastIndex;
    return result;
}

function cloneOtherType(target) {
    const Ctor = target.constructor;
    switch (Ctor) {
        case Boolean:
        case Number:
        case String:
        case Error:
        case Date:
            return new Ctor(target.valueOf());
        case RegExp:
            return cloneReg(target);
        case Symbol:
            return cloneSymbol(target);
        case Map:
        case WeakMap:
        case Set:
        case WeakSet:
            return new Ctor([...target]);
        default:
            return null;
    }
}

function clone (obj, hash = new WeakMap()) {
    if(hash.has(obj)) return hash.get(obj)
    let cloneObj = Array.isArray(obj) ? [] : {};
    hash.set(obj, cloneObj);
    let names = Object.getOwnPropertyNames(obj);
    names.push(...Object.getOwnPropertySymbols(obj));
    for (let key of names) {
        cloneObj[key] = null;
        if(isArrowFunction(obj[key])) cloneObj[key] = copyFunction(obj[key])
        else if(isObj(obj[key])) cloneObj[key] = cloneOtherType(obj[key]);
        console.log(cloneObj[key]);
        
        if(cloneObj[key]===null) cloneObj[key] = isObj(obj[key]) ? clone(obj[key], hash) : obj[key];
    }
    return cloneObj;
}


//测试代码
class A {
    a = 1;
} 
class B extends A{
    b=2;
    f = ()=>{return this};
    f2 = function(){return this};
    date = new Date();
    reg = new RegExp();
    u = undefined;
    s = Symbol("foo");
    map = new Map();
}
var o1 = new B();
o1.map.set("1111",1);
o1[Symbol("b")] = "b";

var o =clone(o1);
Object.getOwnPropertySymbols(o)

参考文章:如何写出一个惊艳面试官的深拷贝?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值