Answer
You can achieve this by using a Proxy
Object, either through a function, or directly.
Using a Function during Destruct:
The Proxy
object takes the target
object, and a handler
.
A handler
allows you to set certain conditions like get
and set
that can alter the way that data is returned to the user. We'll be using get
for this.
In the below handler, we alter the get
functionality. We check if the target[prop]
returns an Array
or Object
. If it does, we create a copy in memory and return that instead of the reference. If it is not an Array
or Object
we simply return the primitive value (string, number, etc)
let copyHandler = {
get: function( target, prop, receiver ) {
let value = target[ prop ];
if ( Array.isArray( value ) ) return value.slice( 0 );
if ( typeof value === "object" && value.constructor.name === "Object" ) return Object.assign( {}, value );
return value;
}
}, getCopy = obj => new Proxy(obj, copyHandler);
Utilizing the getCopy
function as our destructuring middle-man, we can be sure that all our values return new references:
const {
name,
testArray,
object
} = getCopy(a);
object.array = [...object.array, 0];
console.log(a.object.array); // [4,5,6]
console.log(object.array); // [4,5,6,0]
Example:
let copyHandler = {
get: function( target, prop, receiver ) {
let value = target[ prop ];
if ( Array.isArray( value ) ) return value.slice( 0 );
if ( typeof value === "object" && value.constructor.name === "Object" ) return Object.assign( {}, value );
return value;
}
}, getCopy = obj => new Proxy(obj, copyHandler);
let a = {
name: 'lala',
testArray: [ 1, 2, 3 ],
object: {
name: 'object',
array: [ 4, 5, 6 ]
}
};
const {
name,
testArray,
object
} = getCopy(a);
object.array = [...object.array, 0];
console.log(a.object.array); // [4,5,6]
console.log(object.array); // [4,5,6,0]
Alternatively, we can do it directly on Declaration/Initialization/Reception:
Directly in this sense means that we can setup the Object to return copies during destructured declaration only.
We do this similarly to above by utilizing a Proxy
, and a middle-man function.
Note: The middle-man functions aren't necessary, but it helps keep things organized.
let destructHandler = {
get: function( target, prop, receiver ) {
if(!this.received) this.received = new Set();
let value = target[ prop ];
if(this.received.has(prop)) return value;
this.received.add(prop);
if ( Array.isArray( value ) ) return value.slice( 0 );
if ( typeof value === "object" && value.constructor.name === "Object" ) return Object.assign( {}, value );
return value;
}, destructable = obj => new Proxy(obj, destructHandler);
The difference here is that our get
handler uses a Set
to determine whether or not a property has already been grabbed once.
It will return copies upon the first request for a referential property (an Array
or Object
). It will still return any primitive value as normal.
This means that upon declaring/initialization/reception of the object, you can apply the destructable
proxy and immediately afterwards pull out copies from that object using a destruct.
Initialization example code:
let a = destructable({
name: 'lala',
testArray: [ 1, 2, 3 ],
object: {
name: 'object',
array: [ 4, 5, 6 ]
}
});
Destructure example:
const {
name,
testArray,
object
} = a;
object.array = [...object.array, 0];
console.log(a.object.array); // [4,5,6]
console.log(object.array); // [4,5,6,0]
Example:
let destructHandler = {
get: function( target, prop, receiver ) {
if(!this.received) this.received = new Set();
let value = target[ prop ];
if(this.received.has(prop)) return value;
this.received.add(prop);
if ( Array.isArray( value ) ) return value.slice( 0 );
if ( typeof value === "object" && value.constructor.name === "Object" ) return Object.assign( {}, value );
return value;
}
}, destructable = obj => new Proxy(obj, destructHandler);
let a = destructable({
name: 'lala',
testArray: [ 1, 2, 3 ],
object: {
name: 'object',
array: [ 4, 5, 6 ]
}
});
const {
name,
testArray,
object
} = a;
object.array = [...object.array, 0];
console.log(object.array); // [4,5,6,0]
console.log(a.object.array); // [4,5,6]
Hope this helps! Happy Coding!