JS 中如何克隆对象?你学会了吗?
创始人
2025-07-10 11:21:33
0

大家好,这里是大家的林语冰。

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 函数来拷贝复杂的对象。在选择克隆机制之前,请了解您的数据。

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
P2P的自白|我不生产内容,我... 现在一提起P2P,人们就会联想到正在被有关部门“围剿”的互联网理财服务。×租宝事件使得劳...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...