💾.assignDescriptors()
JS ⟩ objects ⟩ extending objects ⟩ mixin ⟩ 💾 Object.assignDescriptors()
☢️ Alert:
Don't use Object.assign() with sources that have getters, the inner states of the sources may change❗❗❗
replit, compare with obj.mergeWith()
/*****************************************************************
* Object.assignDescriptors() *
*****************************************************************
*
* • copies property descriptors from sources into the target
* instead of just copying property values.
*
* • copies all own properties (both enumerable and non-enumerable).
* • copies getters from sources and overwrites setters in the target
* rather than invoking those getters/setters.
*
* • propagates any TypeErrors thrown by `Object.defineProperty()`:
* • if the target is sealed or frozen or
* • if any of the source properties try to change an existing
* non-configurable property on the target.
*/
Object.defineProperty(Object, "assignDescriptors", {
// match the attributes of `Object.assign()`
writable : true,
enumerable : false,
configurable: true,
// value of the `assignDescriptors` property.
value: function(target, ...sources) {
for(let source of sources) {
// copy properties with string key
for(let name of Object.getOwnPropertyNames(source)) {
let desc = Object.getOwnPropertyDescriptor(source, name);
Object.defineProperty(target, name, desc);
}
// copy propertyes with symbol key
for(let symbol of Object.getOwnPropertySymbols(source)) {
let desc = Object.getOwnPropertyDescriptor(source, symbol);
Object.defineProperty(target, symbol, desc);
}
}
return target;
}
});
// a counter
let counter = {
_c: 0,
get count() { return ++this._c; } // ⭐ getter
};
// ----------------------------------------------------------
// ☢️ Alert:
// Don't use Object.assign with sources that have getters,
// the inner states of the sources may change❗❗❗
// ----------------------------------------------------------
// copy the property values (with getter)
let iDontCount = Object.assign({}, counter);
// ☢️ `counter.count` gets INVOKED❗❗❗ counter._c === 1 (polluted)❗❗❗
// copy the property descriptors
let iCanCount = Object.assignDescriptors({}, counter);
[
counter._c, // 1 (☢️ polluted by Object.assign❗)
// ⭐ `iDontCount.count` is a "data" property
Object.getOwnPropertyDescriptor(iDontCount, 'count'),
// {
// value: 1, <---- ⭐ data property
// writable: true, enumerable: true, configurable: true
// }
iDontCount.count, // 1: just a data property,
iDontCount.count, // 1: it won't count.
// ⭐ `iCanCount.count` is an "accessor" property (getter)
Object.getOwnPropertyDescriptor(iCanCount, 'count'),
// {
// get: [Function: get count], <---- ⭐ accessor property
// set: undefined,
// enumerable: true,
// configurable: true
// }
// ☢️ although it can count, it doesn't count from 1❗
iCanCount.count, // 2: it's a getter method alright, but its "count"
iCanCount.count, // 3: has been polluted by Object.assgin() ☢️
].forEach(x => console.log(x))
Object.assign() - don't use with sources that have getters.
Last updated