[javascript 进阶]实现深拷贝的几种方法

1. 前言

这几天估计是嗑书嗑疯了,晚上死活睡不着估计神经衰弱,今天就不做react-native专栏了。正好前几天面试被问到深拷贝的问题,今天就做个专栏讨论吧。

2.深拷贝的几个实现方式:

1. JSON实现方式

这个是大家都知道的一种方式,不过它有三点问题:

var obj = {
	name:"foo",
	fun:function(){},
	obj:obj,
	age:undefined
}

let cloned_obj = JSON.parse(JSON.stringify(obj));
//结果就是{name: "foo"}

从上面的例子可以知道:

  • function无法被复制
  • 循环引用直接被移除.
  • undefined将被移除

emmmmm,果然简单的东西大部分都有坑.

2. Object.create

我也听说过这个方法,不过嘛Object.create并不能复制一个对象,只不过是调用对象的constructor重新构造一个对象.

3.DIY

有这么多的坑,所以咱们还是自己动手丰衣足食吧……

    class Utils {
        constructor() {
            this.map = new WeakMap();
        }

        deepCopy=(obj)=> {
            let { map ,deepCopy} = this;
            //如果是基本类型则直接返回
            if (typeof obj !== "object") {
                return obj;
            }

            //如果是引用类型,则判断是否是循环引用的对象
            if (map.get(obj)) {
                return {};
            }

            //如果是null
            if (!obj) {
                return null;
            }

            //此时细分类型
            const obj_type = Object.prototype.toString.call(obj);

            let temp_obj = null;
            //如果是数组则迭代deepcopy
            if (obj_type.includes("Array")) {
                //缓存obj,留待下次判断是否循环引用
                map.set(obj, obj_type);
                temp_obj = [];
                obj.forEach((item) => {
                    temp_obj.push(deepCopy(item));
                });
            }

            //如果是对象的话,迭代deepcopy直到基本类型
            if (obj_type.includes("Object")) {
                map.set(obj, obj_type);
                temp_obj = {};
                Reflect.ownKeys(obj).forEach((key) => {
                    temp_obj[key] = deepCopy(obj[key]);
                });
            }

            return temp_obj;
        }
    }
    let u = new Utils();
    console.log(u.deepCopy(sourceObj));
/**********************结果**************************    
{fun: ƒ, name: "fun", version: null, arrs: Array(4)}
arrs: (4) [undefined, 1, 2, "3"]
fun: ƒ ()
name: "fun"
version: null
__proto__: Object
****************************************************/

唔,总算是清净了~

另外附上一个打印对象所有属性的方法,调试用的:

    function obj2Str(obj = {}) {
        let curItem = null;
        let itemObj = {};
        Reflect.ownKeys(obj).forEach((key) => {
            curItem = obj[key];
            //生成检测对象类型
            const itemType = Object.prototype.toString.call(curItem);
            if (itemType.includes("Null")) {
                itemObj[key] = "null";
            }
            else if (itemType.includes("Undefined")) {
                itemObj[key] = "undefined";
            }
            else if (itemType.includes("Array")) {
                let arrayStr = "[";
                curItem.forEach((item) => {
                    arrayStr += obj2Str(item);
                });
                arrayStr += "]";
                itemObj[key] = arrayStr;
            }
            else if (itemType.includes("Object")) {
                itemObj[key] = obj2Str(curItem);
            }
            else {
                itemObj[key] = curItem.toString();
            }

        });
        return JSON.stringify(itemObj);
    }


3.后记

这个功能看似简单但是实际上有很多的坑:

  • 类型判断的方式选用:

1.typeof
形如 var x = “xx”; typeof x == ‘string’ typeof(x);
返回类型有:‘undefined’ “string” ‘number’ ‘boolean’ ‘function’ ‘object’
缺点:对于object类型不能细分是什么类型
优点:对空null的判断 'undefined’的应用

2.instanceof
形如 var d = new String(‘test’); d instanceof String ==true;
返回的类型有:String Number Boolean Function Object Array Date
优点:能区分出更细的类型如 Date Array 如 var num = 3; num instanceof Number 能返回具体的类型
缺点:直变量不能区分 必须采用new 的对象

3.constructor
形如:var x = []; x.constructor==Array;
优点:可以返回继承的类型
缺点: 不能对象的细分,如继承 必须手动修正

4.Object.prototype.toString.call();
优点:通用,返回"[objectString]" 具体object的类型
缺点:不能返回继承的类型

  • 循环引用的问题
    要解决这个问题就涉及到对真实数据的取舍,基于深拷贝的初衷–和原数据脱离关系以及循环引用本身就是个错误的做法,所以我还是选择了置undefined

不过最终咱们还是自己解决了这个问题,而且自己写的东西以后还能根据项目实际需求再做调整不是吗??

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值