深拷贝知识梳理
深浅拷贝已经是我们谈论无数次的话题了,其实答案也都大差不差,总归是那几个方法。
但是现在我们多了一个选择,而且是JavaScript的原生级方法--structuredClone。
先让我们回顾一下之前的深拷贝方案,如果不想看可以直接跳过到最新API,毕竟大家对这块已经太熟悉了。
使用JSON.stringify和JSON.parse
使用 JSON.stringify 将对象转换为 JSON 字符串,再使用 JSON.parse 将其转换回对象。这种方法只适用于对象中不含有日期对象、函数对象或循环引用的情况。因为该方法会将对象中的函数和日期转换为字符串并且不能处理循环引用。
//简单对象的深层复制实现方式
const obj = {
name: "Alice",
age: 30,
address: {
street: "123 Main St",
city: "Anytown",
state: "CA",
zip: "12345"
}
};
const copy = JSON.parse(JSON.stringify(obj)); //使用JSON解析
console.log(copy);
递归枚举对象
使用递归枚举实现深层复制,该方法可以处理循环引用和日期对象。
//高级深层复制
function deepClone(obj, hash = new WeakMap()) {
if (Object(obj) !== obj) return obj;
if (obj instanceof Set) return new Set(obj);
if (obj instanceof Map) return new Map(obj);
if (hash.has(obj)) return hash.get(obj); //循环引用
const cloneObj = obj instanceof Date ? new Date(obj)
: obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
: obj.constructor ? new obj.constructor()
: Object.create(null);
hash.set(obj, cloneObj);
return Object.assign(cloneObj, ...Object.keys(obj).map(
key => ({ [key]: deepClone(obj[key], hash) })
));
}
//使用方法
const obj = {
name: "Alice",
age: 30,
address: {
street: "123 Main St",
city: "Anytown",
state: "CA",
zip: "12345"
}
};
const copy = deepClone(obj);
console.log(copy);
使用第三方库
也可以使用第三方库,例如 Lodash 的 _.cloneDeep()
方法来实现深层复制。这种方法比较方便,但需要将库加载到项目中。
//使用Lodash库的深层复制
const _ = require('lodash');
const obj = {
name: "Alice",
age: 30,
address: {
street: "123 Main St",
city: "Anytown",
state: "CA",
zip: "12345"
}
};
const copy = _.cloneDeep(obj);
console.log(copy);
structuredClone
全局的 structuredClone()
方法使用结构化克隆算法将给定的值进行深拷贝。
该方法还支持把原始值中的可转移对象转移到新对象,而不是把属性引用拷贝过去。 可转移对象与原始对象分离并附加到新对象;它们不可以在原始对象中访问被访问到。
上面是MDN给出的说明,相信大家看起来还是有点懵的,我给大家逐一梳理一下。
首先,structuredClone
可以将给定的值进行深拷贝。
其次,它使用的是结构化克隆算法。
结构化克隆算法
结构化克隆算法(Structured clone algorithm),也叫结构化克隆算法,是一种JavaScript内置的通用序列化算法,用于将一个JavaScript对象序列化为可在不同执行上下文中传递的结构化数据格式,并在此基础上进行反序列化操作。它是HTML5规范中定义的一部分。
结构化克隆算法可以序列化JavaScript对象、ArrayBuffer、Date、RegExp、Blob、File和FileList等数据类型。它通过递归地遍历对象图,对每个值进行序列化,并使用循环引用处理来处理对象之间的引用关系,以便在不同执行上下文中进行传递。
结构化克隆算法是一个可靠、高效、安全的序列化算法,它可以应用于Web Workers、IndexedDB、postMessage和WebSocket等场景中,提供了一个标准的、跨浏览器、跨平台、跨窗口的数据传输方式。下面是使用结构化克隆算法对一个JavaScript对象进行序列化和反序列化的例子:
// 序列化
const obj = {
name: 'Alice',
age: 25,
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA'
}
};
const serialized = self.postMessage(obj);
// 反序列化
self.onmessage = (event) => {
const copy = event.data;
console.log(copy);
}
在上面的例子中,我们使用了postMessage API将一个JavaScript对象传递给另一个执行上下文,并使用结构化克隆算法将其序列化为可传递的格式。在另一个执行上下文中,我们使用onmessage事件处理程序接收到序列化后的数据,并反序列化为原始对象。在这个过程中,结构化克隆算法帮助我们自动处理了对象之间的引用关系,使得数据能够在不同执行上下文中进行传递。
然后,该方法还支持把原始值中的可转移对象转移到新对象,什么是可转移对象呢?
可转移对象
可转移对象(Transferrable Objects)是一种特殊的对象类型,可以在不同的Web Worker之间共享和传递,同时保证高效的数据传输和处理。
可转移对象通常是一些二进制数据对象,包括ArrayBuffer、MessagePort、ImageBitmap、OffscreenCanvas等类型。它们可以在Web Workers之间进行共享和传递,并且能够在发送出去的同时立即释放内存,在接收方重新绑定并使用。
可转移对象的传递过程使用的是“转移所有权”的方式,即将所有权从一个执行上下文(如主线程)转移到另一个执行上下文(如Web Worker),使得在接收方操作该对象时,发送方无法再对其进行任何操作。这种方式能够避免多线程环境下因为对共享数据进行同时读写时导致的数据不一致等问题。
总结
总结一下呢,就是structuredClone能够进行深拷贝,其他的和我们前端开发关系不大。
使用
structuredClone使用起来很简单。
const cloneData = {
name:'枫',
age:25,
address:{
city: '深圳'
}
}
const copyFeng = structuredClone(cloneData)
copyFeng.age = 35
window.console.log(cloneData,copyFeng)
总的来说就是,直接用。
其实structuredClone还有一种使用方法:
structuredClone(value, { transfer })
value
被克隆的对象。可以是任何结构化克隆支持的类型。
transfer
可选
是一个可转移对象的数组,里面的 值
并没有被克隆,而是被转移到被拷贝对象上。
const original = new Uint8Array(1024);
const clone = structuredClone(original);
console.log(original.byteLength); // 1024
console.log(clone.byteLength); // 1024
original[0] = 1;
console.log(clone[0]); // 0
// Transferring the Uint8Array would throw an exception as it is not a transferable object
// const transferred = structuredClone(original, {transfer: [original]});
// We can transfer Uint8Array.buffer.
const transferred = structuredClone(original, { transfer: [original.buffer] });
console.log(transferred.byteLength); // 1024
console.log(transferred[0]); // 1
// After transferring Uint8Array.buffer cannot be used.
console.log(original.byteLength); // 0
支持的类型
- 基本类型(string、number、bigint、boolean、null、undefined和Symbol)。
- 对象类型,包括普通Object对象、Array数组、Set集合、Map映射、Date日期、RegExp正则表达式、Blob数据块、File文件、ImageData图像数据和TypedArray类型数组等。
- JavaScript中的特殊类型,包括MessagePort、MessageChannel、BroadcastChannel、ImageBitmap、OffscreenCanvas等。
缺点
- 不允许克隆
Error
、Function
和DOM
对象,如果对象中含有,将抛出DATA_CLONE_ERR
异常。 - 不保留
RegExp
对象的lastIndex
字段。 - 不保留属性描述符,
setters
以及getters
(以及其他类似元数据的功能)。例如,如果一个对象用属性描述符标记为read-only
,它将会被复制为read-write
。 - 不保留原形链。
转载自:https://juejin.cn/post/7231735955793805368