正常拷贝

假设我们要复制一个对象,如果不对其进行深拷贝,那么改变其中一个对象后,另外一个对象也会跟着改变。

  1. const a = {
  2. age: 20,
  3. };
  4. const b = a;
  5. b.age = 30;
  6. console.log(a.age); // 30

这并不是我们想要的效果,所以想要复制一个全新的对象时,我们可以通过浅拷贝来实现。

浅拷贝

方式一

  1. const a = {
  2. name: 'xiaoming',
  3. age: 20,
  4. };
  5. const b = { name: a.name, age: a.age };
  6. b.age = 30;
  7. console.log(a.age); // 20

这种方式就不太灵活,要拷贝的对象属性越多,自己手写的属性就越多。

方式二

  1. const a = {
  2. name: 'xiaoming',
  3. age: 20,
  4. };
  5. const b = {};
  6. for (const key in a) {
  7. b[key] = a[key];
  8. }
  9. b.age = 30;
  10. console.log(a.age); // 20

通过遍历的方式,无论旧对象的属性有多少个,都可以自动拷贝到新的对象上。
以上方式由于是浅拷贝只能拷贝对象的一层属性,对于对象有多层属性依然是存在同样的问题。

  1. const a = {
  2. name: 'xiaoming',
  3. age: 20,
  4. children: {
  5. name: 'libai',
  6. age: 20,
  7. },
  8. };
  9. const b = {};
  10. for (const key in a) {
  11. b[key] = a[key];
  12. }
  13. b.children.age = 30;
  14. console.log(a.children.age); // 30

我们把对象 b children 属性改变后,对象 a children 属性也会跟着改变,这也不是我们想要的效果,所以我们可以通过对象的深度拷贝实现(拷贝无限深的层级)。

深拷贝

方式一

  1. const a = {
  2. name: 'xiaoming',
  3. age: 20,
  4. children: {
  5. name: 'libai',
  6. age: 20,
  7. },
  8. };
  9. const b = JSON.parse(JSON.stringify(a));
  10. b.children.age = 30;
  11. console.log(a.children.age); // 20

通过 JSON 类来实现,实现起来非常简单,首先把对象转成 JSON 字符串,然后在把 JSON 字符串转成对象。但是这种方式依然存在问题,假设我的对象里面有 function 属性等其他非值类型属性,那就无法拷贝了。

  1. const a = {
  2. name: 'xiaoming',
  3. age: 20,
  4. children: {
  5. name: 'libai',
  6. age: 20,
  7. },
  8. eat: function () {
  9. console.log('i am eat');
  10. },
  11. };
  12. const b = JSON.parse(JSON.stringify(a));
  13. console.log(b.eat); // undefined

方式二

使用递归(深度优先搜索)方式,这种就是最稳定的一种方式,但是实现起来也是比较复杂,就是将对象的每一层属性都遍历出来,赋值给新的对象。

这里简单介绍一下什么是递归,就是在一个函数内部中再次调用自己,当达到一定条件后停止调用。

  1. // 类型字典
  2. function type(data) {
  3. let dist = {
  4. '[object Array]': 'array',
  5. '[object Object]': 'object',
  6. '[object Number]': 'number',
  7. '[object Function]': 'function',
  8. '[object String]': 'string',
  9. '[object Null]': 'null',
  10. '[object Undefined]': 'undefined',
  11. };
  12. return dist[Object.prototype.toString.call(data)];
  13. }
  14. // 深度优先遍历
  15. function dfs(data) {
  16. let newData;
  17. if (type(data) === 'array') {
  18. newData = [];
  19. data.map((item, index) => {
  20. newData[index] = dfs(item);
  21. });
  22. } else if (type(data) === 'object') {
  23. newData = {};
  24. Object.keys(data).map((item) => {
  25. newData[item] = dfs(data[item]);
  26. });
  27. } else {
  28. newData = data;
  29. }
  30. return newData;
  31. }
  1. const a = {
  2. name: 'xiaoming',
  3. age: 20,
  4. children: {
  5. name: 'libai',
  6. age: 20,
  7. },
  8. eat: function () {
  9. console.log('i am eat');
  10. },
  11. };
  12. const b = dfs(a);
  13. b.eat(); // i am eat