JavaScript中对象的深复制

参考资料:

  1. MDN: Object.assign()
  2. 知乎:javascript中的深拷贝和浅拷贝?
  3. 深入剖析 JavaScript 的深复制
  4. js 深克隆(考虑到类型检查,递归爆栈,相同引用,Date和Function等特殊类型克隆,原型克隆)

要理解 JS 对象的深浅复制,首先需要理解:

  • JS 中数据类型分为基本类型和引用类型;
  • 基本类型的比较(赋值)是值的比较(赋值)
  • 引用类型的比较(赋值)是引用的比较(赋值)

参考这篇文章:segmentfault:[ JS 进阶 ] 基本类型 引用类型 简单赋值 对象引用

有了上面的基础,再来讨论对象深浅复制。

基本复制

  1. let me = { name: "zyh", age: "24"};
  2. let meCloned_1 = me;
  3. let meCloned_2 = Object(me);
  4. console.log(me === meCloned_1); // expect true
  5. console.log(me === meCloned_2); // expect true

最基本的复制实际只是实现了引用地址的复制

浅复制

  1. let obj = {
  2. o: {
  3. count: 9,
  4. name: "haha",
  5. },
  6. num: 34,
  7. str: "xixi",
  8. arr: [3, 9],
  9. }
  10. // let objCloned = deepClone({},obj);
  11. let objCloned = Object.assign({}, obj);
  12. console.log(obj === objCloned); // expect false
  13. console.log(obj.o === objCloned.o); // expect true
  14. console.log(obj.arr === objCloned.arr); // expect true

可以看出来ES6的Object.assign()只能实现对象的浅复制

ES6 Object.assign() 的实现原理如下 deepClone() 函数:

  1. // ES6 Object.assign() 的实现原理如下 deepClone() 函数
  2. function deepClone(target, varArgs) {
  3. if (target === null) {
  4. throw new TypeError("Cannot convert undefined or null to object");
  5. }
  6. let result = Object(target);
  7. for (let i = 1; i < arguments.length; i++) {
  8. let nextSource = arguments[i];
  9. if (nextSource !== null) {
  10. for (let nextKey in nextSource) {
  11. if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
  12. result[nextKey] = nextSource[nextKey];
  13. }
  14. }
  15. }
  16. }
  17. return result;
  18. }
  19. let objCloned = deepClone({},obj);
  20. console.log(obj === objCloned); // expect false
  21. console.log(obj.o === objCloned.o); // expect true
  22. console.log(obj.arr === objCloned.arr); // expect true

参考资料:MDN: Object.assign()

深复制

主要参考深入剖析 JavaScript 的深复制

各种深复制方法都存在局限性,没有最好,只有更好

下面是个人实现的深复制函数,仅适用于处理属性值为object和array类型的数据,较为简单,仅供参考

  1. //
  2. let deepClone = function (obj) {
  3. if (obj === null) {
  4. throw new TypeError("Cannot convert null to object")
  5. }
  6. let result = {};
  7. let flag = Object.prototype.toString.call(obj);
  8. if (flag === "[object Object]") {
  9. for (let key in obj) {
  10. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  11. let keyFlag = Object.prototype.toString.call(obj[key]);
  12. if (["[object Object]", "[object Array]"].includes(keyFlag)) {
  13. result[key] = deepClone(obj[key]);
  14. } else {
  15. result[key] = obj[key];
  16. }
  17. }
  18. }
  19. } else if (flag === "[object Array]") {
  20. result = Array.prototype.map.call(obj, ele => ele);
  21. } else {
  22. result = obj;
  23. }
  24. return result;
  25. }
  26. let objCloned = deepClone(obj);
  27. console.log(obj === objCloned); // expect false
  28. console.log(obj.o === objCloned.o); // expect false
  29. console.log(obj.arr === objCloned.arr); // expect false
  30. console.log(objCloned);