JS中如何克隆对象(深克隆浅克隆递归克隆)
作者:大家的林语冰 人猫神话
JS 中如何克隆对象?
此问题看似简单,实际十分复杂。
假设我们需要获取下述对象的拷贝。
const cat = { name: '薛定谔', girlFriends: { name: '龙猫' } }
我们应该简单地创建一个新变量吗?(不)
如果我们将对象赋值给一个新变量,那几乎毫无卵用。
const newCar = cat newCat.name = '柴郡猫' console.log(newCat.name) //=> 柴郡猫 console.log(cat.name) //=> 柴郡猫
两个对象都会变化。
原因很简单 —— 我们并没有创建一个新对象。我们只是创建了一个新的引用。
现在有两个变量:cat
和 newCat
,它们都引用同一个对象。
浅克隆
何时浅克隆:当我们的对象属性有且仅有一层深度,或者我们不关心嵌套引用时。
当且仅当源对象不包含对其他对象的任何引用时,浅克隆才有效。
const newCat = { ...cat } newCar.name = '柴郡猫' console.log(newCat.name) //=> 柴郡猫 console.log(cat.name) //=> 薛定谔 newCat.girlFriends.name = '加菲猫' console.log(newCat.girlFriends.name) //=> 加菲猫 console.log(cat.girlFriends.name) //=> 加菲猫
更新女友名字也会导致原始名字变化,这是因为 newCat.girlFriends
和 car.girlFriends
仍然引用内存中的同一对象。
这就是为什么它被称为“浅”克隆:它能且仅能拷贝顶层属性。
在我们的示例中,我们使用展开运算符 ...
来执行浅克隆,但我们也可以使用 Object.assign
。
const newCat = Object.assign({}, cat)
通过 JSON 深克隆
何时使用此方案:当且仅当我们需要深克隆,且我们的对象有且仅有数组、原始值和其他普通对象时。
克隆对象的一种流行方案是,将其转换为 JSON
字符串,然后解析它。
const newCat = JSON.parse(JSON.stringify(cat)) newCat.name = '柴郡猫' console.log(newCat.name) //=> 柴郡猫 console.log(cat.name) //=> 薛定谔 newCat.girlFriends.name = '加菲猫' console.log(newCat.girlFriends.name) //=> 加菲猫 console.log(cat.girlFriends.name) //=> 龙猫
虽然此方案适用于大多数情况,但它仍然有其局限性。它能且仅能转换普通对象。
// Date 对象会变成字符串 JSON.parse(JSON.stringify({ now: new Date() })) // {now: '2022-07-14T13:21:36.761Z'} // undefined 对应属性人间蒸发 JSON.parse(JSON.stringify({ girlFriends: undefined })) //=> {} // 无法克隆 Set JSON.parse(JSON.stringify({ set: new Set([9]) })) //=> { set: {} } // 无法克隆 Symbol JSON.parse(JSON.stringify({ symbol: Symbol('996') })) //=> {} // 无法克隆正则 JSON.parse(JSON.stringify({ regex: /996/i })) //=> {} // 无法克隆 BigInt JSON.parse(JSON.stringify({ bigint: 996n })) //=> 未捕获类型错误:无法序列化 BigInt
递归克隆
何时使用此方案:当且仅当我们的对象包含复杂的结构(比如 Map/RegExp
等)时。
structuredClone
可以搞定大部分问题。
如你所愿,它通过递归遍历对象来工作。它足够机智,可以使用循环引用拷贝对象,并且知道如何克隆不同类型的数据(包括但不限于 Map/Set/Date
等)。
structuredClone({ regex: /996/i, set: new Set([9]) }) //=> { regex: /996/i, set: Set(1) {9}}
它仍然有其局限性:举个栗子,它无法克隆函数或 DOM 节点。
令人鸡冻的是,它已经被所有主流浏览器支持。
粉丝请注意,它不是 ECMAScript 标准的一部分,而是浏览器 API 的一部分,并且不同浏览器的实现可能一龙一猪。Node 根本没有此 API。
或者,我们始终可以使用 lodash
的 cloneDeep
,它实现了类似的算法。
完结撒花
根据情况选择特定的克隆机制。虽然我相信,在大多数情况下,JSON.parse(JSON.stringify())
应该游刃有余,但有时我们可能想使用 structuredClone
函数来拷贝复杂的对象。在选择克隆机制之前,请了解您的数据。
How to clone an object in JavaScript?: https://www.strictmode.io/articles/clone
以上就是JS中如何克隆对象(深克隆浅克隆递归克隆)的详细内容,更多关于JS克隆对象的资料请关注脚本之家其它相关文章!