structuredClone能直接拷贝Date和Map,原生支持Chrome 98+、Firefox 97+、Node.js 17.0+,保留类型和循环引用,不依赖polyfill;JSON会将Date转字符串、Map变空对象,lodash.cloneDeep存在键类型失真和环境兼容问题。

structuredClone 能否直接拷贝 Date 和 Map
能,而且是原生支持——structuredClone 从 Chrome 98、Firefox 97、Node.js 17.0 开始就正确处理 Date、Map、Set、RegExp、ArrayBuffer 等内置可转移对象,不需要额外 polyfill 或手动递归逻辑。
它不像 JSON.parse(JSON.stringify()) 那样把 Date 变成字符串、把 Map 变成空对象,也不会像某些手写深拷贝函数那样在遇到 Map.prototype.forEach 时抛 TypeError: Illegal invocation。
为什么不用 JSON 方法或 lodash.cloneDeep
JSON.stringify() 会丢失 Date 的类型(只保留 ISO 字符串),且完全忽略 Map、Set、undefined、function、Symbol;lodash.cloneDeep 虽然能处理这些,但存在两个实际问题:
- 对
Map/Set的键值类型敏感(比如用new Date()作 Map 键,clone 后键可能被序列化为字符串,导致查找失效) - 在某些环境(如 Web Worker)中无法访问
globalThis或依赖的原型链,引发Cannot read property 'prototype' of undefined
structuredClone 基于结构化克隆算法(Structured Clone Algorithm),原生保证键值对的完整性和引用关系,且不依赖运行时环境的全局对象。
使用 structuredClone 的注意事项和兼容性兜底
不是所有环境都支持 structuredClone。若目标环境可能较旧(如 Electron 旧版、微信小程序、部分 SSR 场景),需检查并降级:
- 先用
typeof structuredClone === 'function'判断是否可用 - 不可用时,优先考虑
MessageChannel模拟(适用于浏览器主线程/Worker,能处理Date/Map,但不支持function和undefined) - 避免回退到
JSON方案——它会在Date上返回字符串,在Map上返回{},极易引发静默错误
示例兜底逻辑:
function safeClone(obj) {
if (typeof structuredClone === 'function') {
return structuredClone(obj);
}
// MessageChannel fallback(仅限浏览器)
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel();
port1.onmessage = e => resolve(e.data);
port2.postMessage(obj);
});
}
Map 中含 Date 作为 key 时的深拷贝表现
structuredClone 会保留 Map 的键值对结构,包括用 Date 实例作 key 的情况。拷贝后的 Map 中,key 是新创建的 Date 对象,与原 key 时间值相同但引用不同——这符合预期,因为深拷贝本就不该共享引用。
注意:不要试图用 === 比较原 Map 和克隆 Map 的 key,应改用 .getTime() === 或 date1.toString() === date2.toString()。
常见误判场景:
- 原 Map:
map.set(new Date('2023-01-01'), 'a') - 克隆后:
clonedMap.has(new Date('2023-01-01'))返回false(因为是不同实例) - 正确判断:
Array.from(clonedMap.keys()).some(d => d.getTime() === targetDate.getTime())
这点容易被忽略——深拷贝后 Map 的行为逻辑没变,但“相等性”判断方式必须同步调整。











