对象克隆-浅拷贝与深拷贝

前端开发
2020年02月19日
0

对象的克隆(clone)也叫拷贝、复制。

我们都知道,对象在赋值的过程中其实是复制了该对象的引用地址,所以一方面改变,另一方也会跟着变。

js
var person1 = { name: '张三', age: 18 } var person2 = person1; person2.name = '李四'; console.log(person1); // {name: "李四", age: 18} console.log(person2); // {name: "李四", age: 18}

那么,我们如何去克隆一个对象呢。

浅拷贝

浅拷贝的实现方式还是挺多的,这里列举两种方式:

  1. 使用Object.assign()

    js
    var person1 = { name: '张三', age: 18 } var person2 = Object.assign({}, person1); person2.name = '李四'; console.log(person1); // {name: "张三", age: 18} console.log(person2); // {name: "李四", age: 18}
  2. 遍历

    js
    function clone (origin, target) { var tar = target || {}; for (var key in origin) { if (origin.hasOwnProperty(key)) { // 只复制对象自身的属性 tar[key] = origin[key]; } } return tar; } var person1 = { name: '张三', age: 18 } var person2 = clone(person1); person2.name = '李四'; // 浅拷贝无法解决引用值的问题 person2.son.push('张小三'); console.log(person1); // {name: "张三", age: 18, son: ["张小一", "张小二", "张小三"]} console.log(person2); // {name: "李四", age: 18, son: ["张小一", "张小二", "张小三"]}

深拷贝

为了解决对象内的引用值的问题,我们需要使用深拷贝。

js
function deepClone (origin, target) { var tar = target || {}, toStr = Object.prototype.toString, arrType = '[object Array]', item; for (var key in origin) { if (origin.hasOwnProperty(key)) { item = origin[key]; if (typeof item === 'object' && item !== 'null') { // 引用值,并且不是null的情况 if (toStr.call(item) === arrType) { // 值是一个array tar[key] = []; } else { tar[key] = {}; } // 递归克隆 deepClone(item, tar[key]); } else { tar[key] = origin[key]; } } } return tar; } var person1 = { name: '张三', son: { first: { name: '张小一', car: ['Benz'] }, second: { name: '张小二', car: [] } }, car: ['Mazda'] } var person2 = deepClone(person1); person2.son.third = { name: '张小三' } person2.son.second.car.push('BMW'); console.log(person1.son); // { // first: { // name: '张小一', // car: ['Benz'] // }, // second: { // name: '张小二', // car: [] // } // } console.log(person2.son); // { // first: { // name: '张小一', // car: ['Benz'] // }, // second: { // name: '张小二', // car: ['BMW'] // }, // third: { // name: '张小三' // } // }

深拷贝的另一种方式

使用JSON的方法也可以快速的实现对象的深拷贝。

js
var person1 = { son: { name: '张三', car: ['Benz'] } } var str = JSON.stringify(person1), // 先将对象转换成字符串的形式 person2 = JSON.parse(str); // 然后再将JSON字符串转换成对象 person2.son.name = '李四'; person2.son.car.push('BMW'); console.log(person1); // { // son: { // name: '张三', // car: ['Benz'] // } // } console.log(person2); // { // son: { // name: '李四', // car: ['Benz', 'BMW'] // } // }

虽然使用JSON的方式很方便,但是这种方式却存在着不少的问题:

  1. 会丢失functionundefined

  2. 对于RegExpError对象,只会得到空对象;

  3. 对于date对象,只会得到一个String

  4. 对于NaNInfinity,会变成null;

    js
    var test = { a: function () {}, b: undefined, c: /abc/, d: new Error('err'), e: new Date(), f: NaN, g: Infinity, h: -Infinity } var test2 = JSON.parse(JSON.stringify(test)); console.log(test2); // c: {} // d: {} // e: "2020-02-19T08:34:46.962Z" // f: null // g: null // h: null // 当然,这里面对其它的引用类型并未做修改,所以得出的结果也不尽人意 var test3 = deepClone(test); console.log(test3); // a: ƒ () // b: undefined // c: {} // d: {} // e: {} // f: NaN // g: Infinity // h: -Infinity
  5. 无法处理循环引用

    js
    var test = {}; test.obj = test; console.log(JSON.parse(JSON.stringify(test))); // Uncaught TypeError: Converting circular structure to JSON // 当然,没人这样写一个对象