如何克隆ES6导入的数据以创建独立可变对象?
Cypress测试中深拷贝数据集的最优方案?
我在Cypress测试中需要两组略有差异的数据集。由于数据集较长且与原数据的差异极小,原本打算复制对象并按需修改,但ES6导入的变量是绑定关系,修改副本会同时改动原对象。
问题演示
测试数据文件(test.json)
{ "foo": { "bar": "XXX" } }
浅拷贝导致的问题
import test from './test.json' const firstTest = {...test} const secondTest = {...test} secondTest.foo.bar = '@@@' console.log(firstTest.foo.bar) // 输出: @@@
扩展运算符(...)和Object.assign()都只能实现浅拷贝,对于嵌套对象,内部的引用还是共享的,所以修改嵌套属性会影响所有副本。
当前可行的深拷贝方法
import test from './test.json' const firstTest = JSON.parse(JSON.stringify(test)) const secondTest = JSON.parse(JSON.stringify(test)) secondTest.foo.bar = '@@@' console.log(firstTest.foo.bar) // 输出: XXX
更优的深拷贝方案
1. 使用原生structuredClone() API
现代浏览器原生支持的深拷贝方法,比JSON序列化更可靠,能处理Date、RegExp等特殊类型,且无需额外依赖:
import test from './test.json' const firstTest = structuredClone(test) const secondTest = structuredClone(test) secondTest.foo.bar = '@@@' console.log(firstTest.foo.bar) // 输出: XXX
Cypress运行在浏览器环境,完全兼容该API。
2. 自定义递归深拷贝函数
如果需要自定义拷贝逻辑(比如跳过特定属性、处理特殊类型),可以手动实现递归拷贝:
function deepClone(obj) { if (obj === null || typeof obj !== 'object') return obj // 处理Date对象 if (obj instanceof Date) return new Date(obj.getTime()) // 处理RegExp对象 if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags) // 处理数组 if (Array.isArray(obj)) return obj.map(item => deepClone(item)) // 处理普通对象 const clonedObj = {} for (const key in obj) { if (obj.hasOwnProperty(key)) { clonedObj[key] = deepClone(obj[key]) } } return clonedObj } // 使用示例 import test from './test.json' const firstTest = deepClone(test) const secondTest = deepClone(test)
3. 利用Cypress Fixture特性
如果数据集是作为fixture使用,每次调用cy.fixture()都会返回独立的深拷贝对象,天然避免引用共享问题:
cy.fixture('test.json').then(test => { const firstTest = test firstTest.foo.bar = 'YYY' // 此处修改不会影响后续的fixture调用 }) cy.fixture('test.json').then(test => { console.log(test.foo.bar) // 输出: XXX })
为什么JSON序列化不是最优解?
JSON.parse(JSON.stringify())存在明显局限性:
- 会忽略
undefined、function、Symbol类型的属性 - 丢失
Date对象的类型,转成字符串后无法自动还原 - 遇到循环引用的对象会直接抛出错误
内容的提问来源于stack exchange,提问作者Hexodus
相关产品推荐
相关产品推荐

