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

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

时间2025-08-05 15:32:07分类IT科技浏览3846
导读: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
wordpress下载(WordPress一键获取TDK插件) python变量有哪些(如何使用python变量?)