History API 的 state 参数通过结构化克隆实现深拷贝,支持循环引用和多种内置类型,但不支持函数、Symbol 和 DOM 节点;可封装为降级方案,兼容 Chrome 4+、Firefox 4+、Safari 10.1+ 等。

History API 的 state 参数本身不是为深拷贝设计的,但它在调用 pushState() 或 replaceState() 时,会自动对传入对象执行结构化克隆(structured clone)——这是一种浏览器原生支持的、比 JSON.parse(JSON.stringify()) 更健壮的深拷贝机制。因此,你可以把它当作一个“副作用可用”的深拷贝工具,尤其适合不能使用 structuredClone()(兼容性不足)或需要处理循环引用的场景。
为什么 state 能间接实现深拷贝
当你调用:
history.pushState(obj, '', '/')history.replaceState(obj, '', '/')
浏览器会对 obj 执行结构化克隆,生成一份独立副本存入历史栈。这个过程:
- 支持对象、数组、Map、Set、Date、RegExp、ArrayBuffer、TypedArray 等内置类型
- 能正确处理循环引用(
JSON.stringify会报错) - 是同步、无副作用的(不触发页面跳转或重绘)
- 但不支持函数、Symbol、DOM 节点、Error 实例等不可序列化值(传入会直接报错)
安全封装一个 structuralClone 函数
利用 replaceState 临时覆盖当前 state,再立即恢复,即可提取克隆体:
function structuralClone(obj) {
const oldState = history.state;
history.replaceState(obj, document.title);
const cloned = history.state;
history.replaceState(oldState, document.title);
return cloned;
}
注意:
- 必须确保当前页面已通过
pushState或replaceState设置过初始 state(否则oldState为null,恢复时会清空) - Safari 限制 30ms 内最多调用 100 次,高频使用需节流
- 该方法在 iframe 中可能受限(取决于同源策略)
和原生 structuredClone 的对比
现代浏览器已支持 structuredClone(),但兼容性仍有限(如 IE 完全不支持,旧版 Safari 需加前缀)。用 History API 方式作为降级方案更稳妥:
- ✅ 支持所有主流现代浏览器(Chrome 4、Firefox 4、Safari 10.1+、Edge 13+)
- ✅ 不依赖全局 API 可用性,只要 History API 存在即可
- ❌ 无法拷贝函数、Symbol;Error 对象会丢失堆栈信息
- ❌ 有轻微性能开销(涉及历史栈操作),不适合超大数据量(建议单次 ≤640KB)
实际使用建议
这不是首选的深拷贝方式,但在特定场景下很实用:
- 你需要兼容老版本 Safari(
- 你已在 SPA 中大量使用 History API,顺手复用已有机制
- 你正在调试或临时验证某个对象是否可被结构化克隆(直接传给
pushState看是否报错)
日常开发中,优先用 structuredClone();不支持时再 fallback 到 History 方案,并做好类型校验与错误捕获。

















