深浅拷贝的几种方法

717 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

深浅拷贝的含义

  • 浅拷贝:新建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型时,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址(值),源对象也会被修改。
  • 深拷贝:完整的开拷贝一份新的对象,在内存的堆区域重新开辟空间存放新对象,且修改新对象不会对原对象造成影响。

浅拷贝常见方法

  1. 直接赋值新对象
let person = {
    name : '我想藏在罐头里',
    age : 20
}
let person2 = person;
person2.name = "我是迪迦";
console.log(person); // { name: '我是迪迦', age: 20 }
console.log(person2); // { name: '我是迪迦', age: 20 }
  1. Object.assign() 方法

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。

注意:当修改基本数据类型时,原对象不变。

let obj1 = { 
  person: { 
    name: "jack", 
    age: 20
  },
  game: 'PUBG' 
};
let obj2 = Object.assign({}, obj1);
obj2.person.name = "alan";
obj2.game = 'DNF'
console.log(obj1); // { person: { name: 'alan', age: 20 }, game: 'PUBG' }
console.log(obj2); // { person: { name: 'alan', age: 20 }, game: 'DNF' }
  1. ES6 拓展运算符
let obj1 = { 
  person: { 
    name: "jack", 
    age: 20
  },
  game: 'PUBG' 
};
let obj2 = { ...obj1 };
obj2.person.name = "alan";
obj2.game = 'DNF'
console.log(obj1); // { person: { name: 'alan', age: 20 }, game: 'PUBG' }
console.log(obj2); // { person: { name: 'alan', age: 20 }, game: 'DNF' }
  1. 手写浅拷贝
function shallowClone (target) {
    let cloneTarget = {};
    for (const key in target) {
        cloneTarget[key] = target[key];
    }
    return cloneTarget;
}
​
let obj1 = { 
  person: { 
    name: "jack", 
    age: 20
  },
  game: 'PUBG' 
}
let obj2 = shallowClone(obj1);
obj2.person.name = "alan";
obj2.game = 'DNF';
// console.log(obj1); // { person: { name: 'alan', age: 20 }, game: 'PUBG' }
// console.log(obj2); // { person: { name: 'alan', age: 20 }, game: 'DNF' }

深拷贝常见方法

  1. JSON.parse(JSON.stringify(Object))
let obj1 = { 
  person: { 
    name: "jack", 
    age: 20
  },
  game: 'PUBG' 
}
​
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.person.name = "alan";
console.log(obj1); // { person: { name: 'jack', age: 20 }, game: 'PUBG' }
console.log(obj2); // { person: { name: 'alan', age: 20 }, game: 'PUBG' }

该方法是最为简陋的深拷贝,缺点很多,如拷贝其他引用类型、拷贝函数、循环引用等情况。

面试时你只说出这一个方法是肯定完蛋了的。

  1. jQuery.extend()
// $.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝var $ = require('jquery');
let obj1 = { 
  person: { 
    name: "jack", 
    age: 20
  },
  game: 'PUBG' 
}
let obj2 = $.extend(true, {}, obj1);
obj2.person.name = "alan";
console.log(obj1); // { person: { name: 'jack', age: 20 }, game: 'PUBG' }
console.log(obj2); // { person: { name: 'alan', age: 20 }, game: 'PUBG' }
  1. 手写深拷贝
function deepClone (target) {
  if (typeof target === 'object') {
    let cloneTarget = {};
    for (let key in target) {
      cloneTarget[key] = deepClone(target[key]);
    }
    return cloneTarget;
  } else {
    return target;
  }
}
let obj1 = { 
  person: { 
    name: "jack", 
    age: 20,
    like: {
      footBall: 'footBall',
      basketBall: 'basketBall'
    }
  },
  game: 'PUBG',
  array: [1, 2, 3] 
}
const obj2 = deepClone(obj1);
console.log(obj2);

console.log 输出结果如下:

{ person: 
   { name: 'jack',
     age: 20,
     like: { footBall: 'footBall', basketBall: 'basketBall' } },
  game: 'PUBG',
  array: { 0: 1, 1: 2, 2: 3 } 
}

这是一个最基础版本的深拷贝,这段代码可以用递归解决问题,但是显然,他还有非常多的缺陷,比如,还没有考虑数组。

那我们稍微修改一下 deepClone() 代码:

function deepClone (target) {
  if (typeof target === 'object') {
    let cloneTarget = (target instanceof Array) ? [] : {};
    for (let key in target) {
      cloneTarget[key] = deepClone(target[key]);
    }
    return cloneTarget;
  } else {
    return target;
  }
}

之后在console.log 输出结果如下:

{ person: 
   { name: 'jack',
     age: 20,
     like: { footBall: 'footBall', basketBall: 'basketBall' } },
  game: 'PUBG',
  array: [ 1, 2, 3 ] 
}

OK,搞定了,没问题。

总结

以上就是本次分享的全部内容~~

在手写深拷贝中还没考虑到对循环引用、拷贝函数和 Date 类型等做处理哦,如果小伙伴的业务有这些需求的话,可以进行二次改造哦。

如果觉得文章写得不错,对你有所启发的,请不要吝啬 点个 关注 并在 评论区 留下你宝贵的意见哦~~😃