首页IT科技js深拷贝和浅拷贝的区别简书(JavaScript 浅拷贝和深拷贝)

js深拷贝和浅拷贝的区别简书(JavaScript 浅拷贝和深拷贝)

时间2025-05-05 11:01:45分类IT科技浏览3060
导读:JavaScript 中的拷贝分为两种:浅拷贝和深拷贝。...

JavaScript 中的拷贝分为两种:浅拷贝和深拷贝          。

一          、浅拷贝

浅拷贝是指在拷贝过程中           ,只拷贝一个对象中的指针                ,而不拷贝实际的数据                。所以     ,浅拷贝中修改新对象中的数据时     ,原对象中的数据也会被改变      。

JavaScript 中浅拷贝可以通过如下几种方式实现:

使用结构赋值的方式                ,例如 let newObject = {...oldObject} 使用 Object.assign() 方法          ,例如 let newObject = Object.assign({}, oldObject)

二                、深拷贝

深拷贝是指在拷贝过程中     ,拷贝一个对象中的所有数据                ,并创建一个新对象          ,对新对象进行操作并不会影响到原对象          。

1      、常规场景

JavaScript 中深拷贝可以通过如下几种方式实现:

使用 JSON.parse(JSON.stringify(object))方法

需要注意的是:该方法会忽略 undefined 以及正则表达式类型的属性               。

const A = { a: 7788, b: undefined, c: new RegExp(/-/ig) }, B = JSON.parse(JSON.stringify(A)); console.log(A, A); console.log(B, B); 使用递归的方式,手动拷贝对象的每一层 function deepCopy(obj) { if (typeof obj !== object || obj === null) { return obj; } let copy; if (Array.isArray(obj)) { copy = []; for (let i = 0; i < obj.length; i++) { copy[i] = deepCopy(obj[i]); } } else { copy = {}; for (let key in obj) { copy[key] = deepCopy(obj[key]); } } return copy; } const objA = { a: 123 }, objB = { b: 456 }; // 浅拷贝 const objC = objA; console.log(objA.a, objA.a); // objA.a 123 console.log(objC.a, objA.a); // objC.a 123 objC.a = 788; console.log(objA.a, objA.a); // objA.a 788 console.log(objC.a, objC.a); // objC.a 788 // 深拷贝 const objD = deepCopy(objB); console.log(objB.b, objB.b); // objB.b 456 console.log(objD.b, objD.b); // objD.b 456 objD.b = 899; console.log(objB.b, objB.b); // objB.b 456 console.log(objD.b, objD.b); // objD.b 899

这个函数接受一个参数 obj                ,如果它不是对象或者是 null               ,那么直接返回该参数      。如果它是数组,则创建一个新数组并递归复制每一项     。否则           ,创建一个新对象并递归复制每一个属性               。

使用 lodash 类库的_.cloneDeep函数          、 underscore 中的 _.clone() 函数等第三方库

2               、特定场景一:内置对象类型的深拷贝

JavaScript 中复制内置对象类型(例如 Date               ,RegExp 等)的深拷贝可以使用特定的构造函数来重新创建该对象           。

例如     ,对于 Date 对象           ,可以使用 new Date(originalDate.getTime()) 来创建一个新的日期对象                ,其中 originalDate.getTime() 返回原始日期对象的时间戳     。

对于 RegExp 对象     ,可以使用 new RegExp(originalRegExp) 或 new RegExp(originalRegExp.source, originalRegExp.flags) 来创建一个新的正则表达式对象               。

下面是一个使用构造函数来复制内置对象类型的深拷贝示例:

function deepCopy(obj) { let copiedObjects = new WeakMap(); if (typeof obj !== object || obj === null) { return obj; } if (copiedObjects.has(obj)) { return copiedObjects.get(obj); } let copy; if (obj instanceof Date) { copy = new Date(obj.getTime()); } else if (obj instanceof RegExp) { copy = new RegExp(obj); } else if (Array.isArray(obj)) { copy = []; } else { copy = {}; } copiedObjects.set(obj, copy); for (let key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] === object && obj[key] !== null) { copy[key] = deepCopy(obj[key], copiedObjects); } else { copy[key] = obj[key]; } } } return copy; }

这个示例的深拷贝函数首先检查当前对象是否为内置对象类型     ,如果是                ,则使用相应的构造函数重新创建该对象          ,否则创建一个普通对象或数组           。然后进行递归复制每一个属性。

需要注意的是     ,使用构造函数复制内置对象类型只适用于部分内置对象类型                ,对于其他的内置对象类型          ,可能需要使用其他的方法来进行复制,或者使用第三方库来进行复制               。

总之                ,深拷贝复制内置对象类型需要考虑使用构造函数来重新创建对象               ,如果需要对这些对象进行深拷贝操作,可以使用上述方法或其他库来实现                。

3      、特定场景二:自定义对象类型的深拷贝

JavaScript 中自定义对象的深拷贝可以使用同样的递归方式实现           ,可以使用 WeakMap 方法。

function deepCopy(obj) { let copiedObjects = new WeakMap(); if (typeof obj !== object || obj === null) { return obj; } if (copiedObjects.has(obj)) { return copiedObjects.get(obj); } let copy; if (obj instanceof MyCustomObject) { copy = new MyCustomObject(); } else if (Array.isArray(obj)) { copy = []; } else { copy = {}; } copiedObjects.set(obj, copy); for (let key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] === object && obj[key] !== null) { copy[key] = deepCopy(obj[key], copiedObjects); } else { copy[key] = obj[key]; } } } return copy; }

这个函数首先检查当前对象是否为自定义对象               ,如果是     ,则创建一个新的自定义对象           ,否则创建一个普通对象或数组          。然后进行递归复制每一个属性                。

注意                ,如果自定义对象中包含循环引用     ,需要使用 WeakMap 来避免出现死循环      。

4     、特定场景三:对象中存在函数或循环引用

对于函数     ,通常会忽略它们                ,因为函数不能被复制          ,而是需要重新定义          。

对于循环引用     ,可以使用 WeakMap 来存储已经复制过的对象               。每次遇到循环引用时                ,可以检查 WeakMap 中是否已经有该对象的副本          ,如果有,则直接使用副本                ,而不是重新创建      。

function deepCopy(obj) { let copiedObjects = new WeakMap(); if (typeof obj !== object || obj === null) { return obj; } if (copiedObjects.has(obj)) { return copiedObjects.get(obj); } let copy; if (Array.isArray(obj)) { copy = []; } else { copy = {}; } copiedObjects.set(obj, copy); for (let key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] === object && obj[key] !== null) { copy[key] = deepCopy(obj[key], copiedObjects); } else { copy[key] = obj[key]; } } } return copy; }

这是使用 WeakMap 的一种示例               ,这个示例的深拷贝函数递归地复制对象,但检查 WeakMap 中是否已经存在该对象的副本           ,如果存在则直接使用副本               ,而不是重新创建     。

此外     ,使用 JSON.parse(JSON.stringify(obj)) 方法会自动忽略函数和循环引用           ,但是会忽略 undefined 以及正则表达式类型的属性               。

还有                ,还可以使用第三方库     ,如 lodash 中的 _.cloneDeep() 函数               、 underscore 中的 _.clone() 函数等来实现对象中存在函数或循环引用的深拷贝           。

5           、特定场景四:对象中有对其他对象的引用或者包含 Symbol 属性的对象

对于对象中有对其他对象的引用     ,可以使用 WeakMap 来存储已经复制过的对象     。每次遇到对其他对象的引用时                ,可以检查 WeakMap 中是否已经有该对象的副本          ,如果有     ,则直接使用副本                ,而不是重新创建               。

对于对象中包含 Symbol 属性的对象          ,可以使用 Object.getOwnPropertySymbols() 方法来获取该对象所有的 Symbol 属性,然后使用 Object.getOwnPropertyDescriptor() 方法来获取这些 Symbol 属性的值                ,最后将这些值赋给新对象           。

function deepCopy(obj) { let copiedObjects = new WeakMap(); if (typeof obj !== object || obj === null) { return obj; } if (copiedObjects.has(obj)) { return copiedObjects.get(obj); } let copy; if (Array.isArray(obj)) { copy = []; } else { copy = {}; } copiedObjects.set(obj, copy); for (let key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] === object && obj[key] !== null) { copy[key] = deepCopy(obj[key], copiedObjects); } else { copy[key] = obj[key]; } } } let symbols = Object.getOwnPropertySymbols(obj); symbols.forEach(symbol => { let descriptor = Object.getOwnPropertyDescriptor(obj, symbol); Object.defineProperty(copy, symbol, descriptor); }); return copy; }

这是使用 WeakMap 和 Symbol 属性的一种示例               ,这个示例的深拷贝函数首先检查 WeakMap 中是否已经存在该对象的副本,如果存在则直接使用副本           ,而不是重新创建。然后使用 Object.getOwnPropertySymbols() 方法获取该对象所有的 Symbol 属性               ,最后使用 Object.getOwnPropertyDescriptor() 方法获取这些 Symbol 属性的值     ,并将这些值赋给新对象               。

这种方法可以保证深拷贝对象中包含的所有属性           ,包括对其他对象的引用和 Symbol 属性                ,但还是不能复制内置对象类型     ,这些对象类型是不可枚举的                。

三     、循环引用

JavaScript 中的循环引用指的是两个或多个对象之间相互引用的情况。这种情况通常发生在将一个对象赋给另一个对象的属性时     ,同时还将另一个对象赋给第一个对象的属性          。

以下是一个示例:

let obj1 = {}; let obj2 = {}; obj1.prop = obj2; obj2.prop = obj1;

这样就会产生一个循环引用                ,因为 obj1 和 obj2 相互引用                。

循环引用可能导致 JavaScript 引擎无法正确处理内存          ,并导致内存泄漏      。因此     ,在编写 JavaScript 代码时需要特别注意避免循环引用          。如果您需要在两个对象之间建立关系                ,可以使用弱引用来避免循环引用               。

在深拷贝中遇到循环引用就会导致死循环          ,因此需要使用特殊的算法来解决这个问题      。可以使用递归算法和深度优先遍历来实现深拷贝,在遍历过程中跟踪已经遍历过的对象                ,如果遇到循环引用就直接返回已经遍历过的对象的引用     。

总的来说               ,在使用浅拷贝和深拷贝时,需要根据需求和对象的结构来进行选择               。通常来说           ,如果需要对对象进行修改并且不希望对原对象造成影响               ,那么应该使用深拷贝           。如果只是需要读取对象中的数据而不需要修改     ,那么可以使用浅拷贝     。在实现深拷贝时           ,需要特别注意循环引用和特殊属性问题               。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
c语言指针指向数组中的数据(C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com)