[英]JavaScript: Is there a way to deep copy an object's non-writable property?
Let's say we have an object obj
with its foo
property being non-writable假设我们有一个对象
obj
,它的foo
属性是不可写的
const obj = {foo: {}}
Object.defineProperty(obj, 'foo', {value: {bar: 'baz'}, writable: false, enumerable: true})
And I want to deep copy this object with its original property descriptors preserved.我想深度复制这个对象并保留其原始属性描述符。 So for the copied object, its
foo
property should still be non-writable.所以对于复制的对象,它的
foo
属性应该仍然是不可写的。
const propertyDescriptors = Object.getOwnPropertyDescriptors(obj)
const cloned = Object.create(
Object.getPrototypeOf(obj),
propertyDescriptors
)
But the problem occurs when I want to deep copy the foo
property's value.但是当我想深度复制
foo
属性的值时,就会出现问题。 Note that I wrote a recursive algorithm to deep copy but I just used the spread operator here for brevity.请注意,我为深度复制编写了一个递归算法,但为了简洁起见,我只是在这里使用了扩展运算符。
cloned.foo = {...obj.foo}
Now there is an error because cloned.foo
has writable: false
because we preserved the property descriptor from the original obj
现在有一个错误,因为
cloned.foo
有writable: false
因为我们保留了原始obj
的属性描述符
I am thinking if there is a way to get around this so that I can deep copy the value of the property and also preserve its original property descriptors?我在想是否有办法解决这个问题,以便我可以深度复制属性的值并保留其原始属性描述符?
The below code go through every property deepCopyWithPropertyDescriptors
and copy the value and descriptors with dynamic programming
.下面的代码遍历每个属性
deepCopyWithPropertyDescriptors
并使用dynamic programming
复制值和描述符。 Be aware of the max depth of object to avoid stack overflow.请注意对象的最大深度以避免堆栈溢出。
Run the code to see the result before and after clone compared side by side.运行代码并排比较克隆前后的结果。
function isObject(value) { var type = typeof value return value != null && (type == 'object' || type == 'function') } function deepCopyWithPropertyDescriptors(o) { const resultObj = {} const desc = Object.getOwnPropertyDescriptors(o) delete desc.value for(const key in o) { const value = o[key] if(isObject(value)) { resultObj[key] = deepCopyWithPropertyDescriptors(value) } else { resultObj[key] = value } } Object.defineProperties(resultObj, desc) return resultObj } // Examples 1 const obj = {foo: {}} Object.defineProperty(obj, 'foo', {value: {bar: 'baz'}, writable: false, enumerable: true}) const cloned = deepCopyWithPropertyDescriptors(obj) console.log("obj", Object.getOwnPropertyDescriptors(obj), Object.getOwnPropertyDescriptors(cloned)) // Examples 2 const obj2 = {foo: {}} const obj2xfoo = {bar: 'xx'} Object.defineProperty(obj2xfoo, 'bar', {value: 'baz', writable: true, enumerable: true, configurable: false}) Object.defineProperty(obj2, 'foo', {value: obj2xfoo, writable: false, enumerable: true, configurable: false}) const cloned2 = deepCopyWithPropertyDescriptors(obj2) console.log("obj2.foo:", Object.getOwnPropertyDescriptors(obj2.foo), Object.getOwnPropertyDescriptors(cloned2.foo)) console.log("obj2:", Object.getOwnPropertyDescriptors(obj2), Object.getOwnPropertyDescriptors(cloned2))
structuredClone
You can use the latest deep clone browser api structuredClone
, as you do not care the compatibilities of these browsers: Internet Explorer, Opera Android, Samsung Internet.您可以使用最新的深度克隆浏览器 structuredClone
,因为您不关心这些浏览器的兼容性:Internet Explorer、Opera Android、Samsung Internet。
const obj = {foo: {}} Object.defineProperty(obj, 'foo', {value: {bar: 'baz'}, writable: false, enumerable: true}) const propertyDescriptors = Object.getOwnPropertyDescriptors(obj) const cloned0 = structuredClone(obj) console.log(cloned0)
JSON.parse
and JSON.stringify
JSON.parse
和JSON.stringify
You can use the JSON.parse
and JSON.stringify
functions to bypass the writable
read only check.您可以使用 JSON.parse
和JSON.stringify
函数绕过writable
只读检查。
const obj = {foo: {}} Object.defineProperty(obj, 'foo', {value: {bar: 'baz'}, writable: false, enumerable: true}) const propertyDescriptors = Object.getOwnPropertyDescriptors(obj) const cloned1 = JSON.parse(JSON.stringify(obj)) console.log(cloned1)
You can use Object.defineProperty()
a couple of times will give the expected result.您可以多次使用
Object.defineProperty()
将给出预期的结果。
With first defineProperty()
call you can create property with value undefined
and property descriptor configurable: true
.通过第一次
defineProperty()
调用,您可以创建值为undefined
且属性描述符configurable: true
。 The next call sets the value to the added property and then updates the property descriptor as per the original object into a cloned object.下一次调用将值设置为添加的属性,然后根据原始对象将属性描述符更新为克隆对象。 Calling
defineProperty()
with the same key, allows modifying value and descriptor if configurable is true.如果可配置为真,则使用相同的键调用
defineProperty()
允许修改值和描述符。
'use strict'; const obj = {foo: {}} const foo = {bar: 'baz'}; Object.defineProperty(obj, 'foo', {value: {bar: 'baz'}, writable: false, enumerable: true}) const clonedObj = cloneObject(obj); //comparing with original object property console.log(obj.foo === clonedObj.foo); //comparing with different object with same properties console.log(foo === clonedObj.foo); console.log('\nOriginal Object'); objWithDiscriptors(obj); console.log('\nClonned Object'); objWithDiscriptors(clonedObj); function cloneObject(sourceObj){ const __clone = {}; Object.keys(sourceObj).map(k => { const propertyDescriptor = Object.getOwnPropertyDescriptor(sourceObj, k); Object.defineProperty(__clone, k, {configurable: true}); if(typeof sourceObj[k] === 'object' && sourceObj[k] !== null && sourceObj[k] !== undefined && !Array.isArray(sourceObj[k])){ Object.defineProperty(__clone, k, {value: cloneObject(sourceObj[k])}); }else{ Object.defineProperty(__clone, k, {value: sourceObj[k]}); } Object.defineProperty(__clone, k, { writable: propertyDescriptor.writable, enumerable: propertyDescriptor.enumerable, configurable: propertyDescriptor.configurable }); }) return __clone; } function objWithDiscriptors(obj){ Object.keys(obj).map(k => { console.group(k); const descriptor = Object.getOwnPropertyDescriptor(obj, k); console.log(`configurable: ${descriptor.configurable}`); console.log(`enumerable: ${descriptor.enumerable}`); console.log(`writable: ${descriptor.writable}`); if(typeof obj[k] == 'object' && obj[k] !== null && obj[k] !== undefined){ objWithDiscriptors(obj[k]); } console.groupEnd() }) }
Additional check and logic can be added for array of objects.可以为对象数组添加额外的检查和逻辑。
If I understand your requirement correctly, You want to deep copy the source object into a target object by preserving source object properties ( writable
, enumerable
).如果我正确理解您的要求,您希望通过保留源对象属性(
writable
、 enumerable
)将源对象深度复制到目标对象中。 If Yes, You can use Object.assign() which copies all enumerable own properties from source object to a target object and returns the modified target object.如果是,您可以使用Object.assign()将所有可枚举的自身属性从源对象复制到目标对象并返回修改后的目标对象。
const obj = {}; Object.defineProperty(obj, 'foo', { value: {bar: 'baz'}, writable: false, enumerable: true }); const objDescriptor = Object.getOwnPropertyDescriptor(obj, 'foo'); const structuredObj = Object.assign(obj); const structuredObjDescriptor = Object.getOwnPropertyDescriptor(structuredObj, 'foo'); console.log(objDescriptor); console.log(structuredObjDescriptor);
One limitation : Above solution is performing shallow copy
not a deep copy
.一个限制:上述解决方案是执行
shallow copy
而不是deep copy
。
Just curious, As writable
property is false
and user can not modify the foo
property value, Then why we need to perform deep copy of the source object ?只是好奇,由于
writable
属性是false
的,用户不能修改foo
属性值,那么为什么我们需要对源对象执行深拷贝呢?
Update :更新 :
To get both enumerable as well as non-enumerable properties from the cloned object.从克隆对象中获取可枚举和不可枚举的属性。 You can use Object.getOwnPropertyNames() method and then iterate over the object keys.
您可以使用Object.getOwnPropertyNames()方法,然后遍历对象键。
Demo :演示:
const obj = {}; Object.defineProperty(obj, 'foo', { value: {bar: 'baz'}, writable: false, enumerable: false }); const structuredObj = Object.assign(obj); console.log(JSON.stringify(structuredObj)); // An empty object as property is non enumerable. // This iteration will return both enumerable as well as non-enumerable properties. Object.getOwnPropertyNames(structuredObj).forEach(function(property) { console.log(property, structuredObj[property]); });
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.