💾.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
Was this helpful?