JS对象的深克隆

对象的深克隆与浅克隆

说在前面

深克隆对于平常代码用到的地方还是比较多的,而更重要的是,最近学长学姐在面试,可以说这个问题是绕不过去的,面试官基本会问,那就来整理一下吧。

可能平时用的时候深克隆并没有那么完整,但是一定要对其中的思想理解的透彻,如果这篇博客有没有说到的地方,希望大家能指出。

那么先说一下,深克隆针对的是Object这样的引用类型

1.浅克隆

在说深克隆之前我们先来了解一下浅客隆

顾名思义,浅客隆就是复制的很浅,并不深入复制,他只会将这个对象的各个属性进行依次复制,并不会进行递归复制,而JavaScript存储对象都是存地址的,这样的复制并不是完全的复制,通过一段代码来了解吧

function shallowClone(obj) {
  const _obj = {};
  for ( let i in obj) {
    _obj[i] = obj[i];
  }
  return _obj;
}
//被复制的对象
let oldObj = {
    a: 2,
    b: 'sss',
    c: [0,1,3,4],
    d: {
        name: 'zzy',
        score: {
            math: 100,
            Chinese: 100,
            English: 100
        }
    }
};
//复制后的对象
let newObj = shallowClone(oldObj);

我们声明一个浅客隆函数,并定义一个对象,然后调用浅客隆,现在我们改变新对象的属性

//改变新对象的属性
newObj.a = 3;
newObj.d = 'haha';

console.log(oldObj); // { a: 2, b: 'sss', c: [ 0, 1, 3, 4 ],d: { name: 'zzy', score: { math: 100, Chinese: 100, English: 100 } } }
console.log(newObj); // { a: 3, b: 'sss', c: [ 0, 1, 3, 4 ], d: 'haha' }

可以看出来对象确实复制成功,然后我们再改变一下嵌套对象中的属性

newObj.d.name = 'ppp';
console.log(oldObj.d.name); //ppp
console.log(newObj.d.name); //ppp

发现嵌套的对象并没有复制成功,他依然是按引用传递的,改变其中一个属性,另一个会随之改变。

原因就是我们只是复制了一层,浅客隆的代码完全可以看出来,只是复制了上面的一层,内层嵌套的并没有复制到,所以oldObj.d.name和newObj.d.name指向的还是同一块内存地址。

现在有许多数组或者对象的复制方法都是浅复制,比如Object.assign或者扩展运算符实现的复制

2.深克隆

深克隆的方法还是挺多的,但是有一些方法是存在问题的。

(1)JSON.parse方法

JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,这两个方法结合起来就能产生一个便捷的深克隆

let newObj = JSON.parse(JSON.stringify(oldObj));

我们还是用之前的例子
然后修改一下

newObj.d.name = 'ppp';
console.log(newObj.d.name); //ppp
console.log(oldObj.d.name); //zzy

确实,他实现了深克隆,但是这样简单的方法就一定会存在一些问题的

  1. 对于函数和正则这类特殊对象的克隆,返回会为空
  2. 对稀疏数组无法复制
  3. 对象的原型并不会复制

咱们来试一下

function set() {
    this.fn = function() {
        return 'sss';
    };
    this.arr = new Array(1);
    this.reg = new RegExp('a+c', 'i');
}
function parent() {
    this.dad = 'zjf';
    this.mom = 'my';
}
set.prototype = new parent();

let oldObj = new set();
let newObj = JSON.parse(JSON.stringify(oldObj));
console.log(c); // parent { fn: [Function], arr: [ \<1 empty item> ], reg: /a+c/i }
console.log(x); // { arr: [ null ], reg: {} }

可以看到出问题了,首先对象的原型并没有复制上,函数和正则复制也出现了问题,稀疏数组也有问题。

那么这个方法是不太靠谱的,但是如果平常确实没有这么多的限制,只是一个普通的简单的对象,可以试一试这个方法,毕竟太方便了~如果有很多的限制,那就得分类处理了!

(2)递归方法

之前的问题我们来一个一个解决

首先是函数和正则特殊对象的克隆,这个好办,只需要判断一下类型,而函数和正则直接返回即可。

稀疏数值通过递归就可以返回,数组里的值为undefined

通过Object.getPrototypeOfObject.create即可将原型复制

来看代码

//判断传值类型
function getType(obj) {
    let _toString = Object.prototype.toString;
    let map = {
        '[object Boolean]'  : 'boolean', 
        '[object Number]'   : 'number', 
        '[object String]'   : 'string', 
        '[object Function]' : 'function', 
        '[object Array]'    : 'array', 
        '[object Date]'     : 'date', 
        '[object RegExp]'   : 'regExp', 
        '[object Undefined]': 'undefined',
        '[object Null]'     : 'null', 
        '[object Object]'   : 'object'
    };
    return map[_toString.call(obj)];
}
//深克隆核心函数
function deepClone(data) {
    let obj = null;
    let type = getType(data);
    if(type === 'array') {
        obj = [];
        for(let i = 0; i < data.length; i++) {
            obj.push(deepClone(data[i]));
        }
    } else if(type === 'object') {
        //获取原型,并创建新的对象,新对象原型为之前对象的
        let _proto = Object.getPrototypeOf(data);
        obj = Object.create(_proto);
        for(let key in data) {
            //判断属性是否是实例的,如果是复制
            if(data.hasOwnProperty(key)) {
                obj[key] = deepClone(data[key]);
            } 
        }
    } else {
        return data;
    }
    return obj;
}

//构造函数
function set() {
    this.fn = function() {
        return 'sss';
    };
    this.arr = new Array(1);
    this.reg = new RegExp('a+c', 'i');
}
function parent() {
    this.dad = 'zjf';
    this.mom = 'my';
}
set.prototype = new parent();

let oldObj = new set();
let newObj = deepClone(oldObj);

console.log(oldObj);
//
parent {
  name: 'ddd',
  sex: 'man',
  num: '04161083',
  reg: /a+c/i,
  aR: [ <2 empty items> ] }

console.log(newObj);
//
parent {
  name: 'ddd',
  sex: 'man',
  num: '04161083',
  reg: /a+c/i,
  aR: [ undefined, undefined ] }

这样就是一个相对还不错的深克隆函数了,将大部分的问题都考虑到了

如果平时要用到深克隆的话,推荐lodash,自己实现一下就是为了学习思想~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值