影分身术——JS深克隆

分身术种类

  1. 普通分身术:有其形,无其实,不具有完备的主体
  2. 影分身术:从真身上得到完备的继承,具有和真身一样的完备性

普通分身术

普通分身术技术实现

/**
* 简单克隆实现
*/
const originObj = {
    a: 1,
    b: 2
};
const simpleCloneObj = JSON.parse(JSON.stringify(originObj));

普通分身术局限性

  1. 无法实现对函数、RegExp等特殊对象的克隆
  2. 会抛弃对象的constructor,所有的构造函数会指向Object
  3. 对象有循环引用的话会报错

影分身术

影分身术技术实现

/**
* 深度克隆实现
*/
const deepClone = parent => {
    // 判断类型
    const isType = (obj, type) => {
        if (typeof obj !== 'object') {
            return false;
        }
        const typeString = Object.prototype.toString.call(obj);
        let flag;
        switch (type) {
            case 'Array':
                flag = typeString === '[object Array]';
                break;
            case 'Date':
                flag = typeString === '[object Date]';
                break;
            case 'RegExp':
                flag = typeString === '[object RegExp]';
                break;
        }
        return flag;
    };

    // 处理正则
    const getRegExp = reg => {
        let flags = '';
        if (reg.global) {
          flags += 'g';  
        }
        if (reg.ignoreCase) {
            flag += 'i';
        }
        if (reg.mutiline) {
            flag += 'm';
        }
        return flags;
    };

    // 维护两个储存循环引用的数组
    const parents = [];
    const children = [];
    const _clone = parent => {
        if (parent === null) {
            return null;
        }
        if (typeof parent !== 'object') {
            return parent;
        }
        let child, proto;
        if (isType(parent, 'Array')) {
            // 对数组做特殊处理
            child = [];
        } else if (isType(parent, 'RegExp')) {
            // 对正则对象做特殊处理
            child = new RegExp(parent.source, getRegExp(parent));
            if (parent.lastIndex) {
                child.lastIndex = parent.lastIndex;
            }
        } else if (isType(parent, 'Date')) {
            child = new Date(parent.getTime());
        } else {
            // 处理对象原型
            proto = Object.getPrototypeOf(parent);
            // 利用Object.create切断原型链
            child = Object.create(proto);
        }
        // 处理循环引用
        const index = parents.indexOf(parent);
        if (index != -1) {
            // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
            return children[index];
        }
        parents.push(parent);
        children.push(child);
        for (let i in parent) {
            // 递归
            child[i] = _clone(parent[i]);
        }
        return child;
    };
    return _clone(parent);
};

const originObj = {
    a: 1,
    b: {
        aa: 11,
        bb: 22
    },
    c () {
        console.log('I am a function');
    },
    d: [1, 2, 3, 4]
};

const shadowObj = deepClone(originObj);
console.log(shadowObj);

影分身术进一步优化

  1. 增加对Buffer、Promise、Set、Map等特殊对象的处理
  2. 对于确保没有循环引用对象的,可以省去对循环引用的特殊处理,因为这很消耗时间

参考资料

  1. https://www.cxymsg.com/guide/jsWritten.html#%E5%AE%9E%E7%8E%B0%E8%8A%82%E6%B5%81%E5%87%BD%E6%95%B0%EF%BC%88throttle%EF%BC%89

延伸阅读


▶ Walkthrough007

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值