在 JavaScript 开发中,对象复制是一个常见的操作。很多开发者都会遇到一种情况:只复制了对象的浅层属性而没有复制其深层属性。在 ES8 中,有一些对象函数可以帮助我们完成对象深复制的操作,本文将会介绍这些函数。
浅复制和深复制
在介绍深复制过程中,先来回顾一下什么是浅复制。所谓浅复制,就是只复制对象的一层属性,而不复制对象里面的子对象。比如:
-- -------------------- ---- ------- --- ---- - ------ -------- ------ ----------- ---------- --- ---- - ----------------- ------ ------------------ -- ------ -------- ------ --------- -------------------------- ------------------ -- ------ -------- ------ --------- ------------------ -- ------ -------- ------ ---------
在上面的例子中,我们使用了 Object.assign()
函数来进行对象的浅复制。虽然 obj2
和 obj1
看起来内容一样,但其实 obj2
中的 hobby
数组只是一个指向 obj1
中该数组的指针,它们是同一个数组对象。所以当我们修改 obj2
中的 hobby
数组的值时,实际上也修改了 obj1
中的 hobby
数组的值。
相对于浅复制,深复制则是对对象和其所有子对象进行复制,使复制品与原版互不影响。比如:
-- -------------------- ---- ------- --- ---- - ------ -------- ------ ----------- ---------- --- ---- - --------------------------------- ------------------ -- ------ -------- ------ --------- -------------------------- ------------------ -- ------ -------- ------ --------- ------------------ -- ------ -------- ------ ---------
在上面的例子中,我们使用了 JSON.stringify()
和 JSON.parse()
函数来进行对象的深复制。虽然这种方法可以深复制对象,但它不支持复制函数,而且会忽略对象中的非枚举属性。
ES8 中的对象函数
在 ES8 中,新增了一些对象函数可以辅助我们完成对象的深复制。其中,最常用的函数是 Object.assign()
、Object.getOwnPropertyDescriptors()
和 Object.create()
。
Object.assign()
在前面的例子中,我们已经介绍了 Object.assign()
函数可以用于对象的浅复制。但实际上,它也可以用于对象的深复制。比如:
-- -------------------- ---- ------- --- ---- - ------ -------- ------ ----------- ---------- --- ---- - ----------------- ----- ------- ------------------ ------------------ -- ------ -------- ------ --------- -------------------------- ------------------ -- ------ -------- ------ --------- ------------------ -- ------ -------- ------ ---------
在上面的例子中,我们使用了 [...obj1.hobby]
来复制 obj1
中的 hobby
数组,从而实现了对象的深复制。
不过,当复制的对象中存在子对象时,Object.assign()
仍然只能进行浅复制。比如:
-- -------------------- ---- ------- --- ---- - ------ -------- -------- ------ ---------- -------- ---------- --- ---- - ----------------- ------ ------------------ -- ------ -------- -------- ---- ----------------- - ----------- ------------------ -- ------ -------- -------- ------ ----------- -------- --------- ------------------ -- ------ -------- -------- ------ ----------- -------- ---------
在上面的例子中,我们复制了 obj1
对象,但是由于 obj1
中的 address
属性是一个对象,所以复制后的 obj2
中也只是将 address
属性复制了一份引用,没有真正完成深复制,因此在修改 obj2
中的 address.city
属性时,obj1
中的 address.city
属性也被修改了。
Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors()
函数可以获取指定对象的所有自身属性的描述符,包括 configurable、enumerable、writable 和 value 属性。利用这个函数,我们可以精确地对对象进行深复制。比如:
-- -------------------- ---- ------- --- ---- - ------ -------- -------- ------ ---------- -------- ---------- --- ---- - --- ----------------------------- ---------------------------------------- ------------------ -- ------ -------- -------- ---- ----------------- - ----------- ------------------ -- ------ -------- -------- ------ ---------- -------- --------- ------------------ -- ------ -------- -------- ------ ----------- -------- ---------
在上面的例子中,我们使用了 Object.defineProperties()
函数定义了 obj2
的属性以及它们的描述符,这些描述符是通过 Object.getOwnPropertyDescriptors()
函数从 obj1
获取的。这样,复制过程中便包含了它们的配置信息,也就实现了完整的深复制。
Object.create()
Object.create()
函数可以创建一个新对象,该对象的原型继承自指定的原型对象,并拥有指定的属性。通过这个函数,可以实现对象的深复制。比如:
-- -------------------- ---- ------- --- ---- - ------ -------- -------- ------ ---------- -------- ---------- --- ---- - ------------------------------------------ ---------------------------------------- ------------------ -- ------ -------- -------- ---- ----------------- - ----------- ------------------ -- ------ -------- -------- ------ ---------- -------- --------- ------------------ -- ------ -------- -------- ------ ----------- -------- ---------
在上面的例子中,我们使用了 Object.getPrototypeOf()
函数获取了 obj1
的原型对象,并将其传递给了 Object.create()
函数,从而创建了一个新对象,该对象的原型继承自 obj1
的原型对象。然后,我们又使用了 Object.getOwnPropertyDescriptors()
函数获取了 obj1
的所有描述符,并将其传递给了 Object.create()
函数,从而创建了一个新对象,该对象拥有了 obj1
的所有属性,从而实现了深复制。
总结
通过上述分析,我们可以看到,ES8 中的三个对象函数都可以用于实现对象的深复制,而每个函数都有其优点和不足。如果对象只有单层属性,则可以使用 Object.assign()
函数进行深复制;如果对象包含嵌套的子对象,则可以使用 Object.defineProperties()
函数进行深复制;如果需要深复制对象的所有属性,包括继承自原型对象的属性,则可以使用 Object.create()
函数进行深复制。当我们在实际开发中需要对对象进行深复制时,可以根据实际情况选择合适的函数来使用。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/647eddfa48841e9894e8ae95