# .assignDescriptors()

[JS](../../../..) ⟩ [objects](../..) ⟩ [extending objects](..) ⟩ [mixin]() ⟩ 💾 Object.assignDescriptors()

{% hint style="danger" %}
☢️ <mark style="color:yellow;">**Alert**</mark>:

<mark style="color:red;">**Don't**</mark> use [Object.assign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) with <mark style="color:yellow;">**sources**</mark> that have <mark style="color:red;">**getters**</mark>, the <mark style="color:orange;">**inner states**</mark> of the sources <mark style="color:red;">**may change**</mark>❗❗❗
{% endhint %}

{% tabs %}
{% tab title="💾 程式" %}
* [replit](https://replit.com/@pegasusroe/ObjectassignDescriptors#index.js), compare with [merge-with](../../../builtin/object/ext/merge-with "mention")

```javascript
/*****************************************************************
 *                 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;
    } 
});
```
{% endtab %}

{% tab title="💈範例" %}
```javascript
// 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))
```
{% endtab %}

{% tab title="📗 參考" %}
* [ ] (2020) JavaScript: The Definitive Guide, 7th Edition (14.1 Property Attributes)
{% endtab %}

{% tab title="👥 相關" %}
* [object.assign](../object.assign "mention") - <mark style="color:red;">**don't use**</mark> with <mark style="color:yellow;">**sources**</mark> that have <mark style="color:red;">**getters**</mark>.
* [object.assign-causing-typeerror](../object.assign/object.assign-causing-typeerror "mention")
{% endtab %}

{% tab title="🗣 討論" %}
* [Object.assign() causes TypeError: this.map is not a function](https://stackoverflow.com/questions/72625697/object-assign-causes-typeerror-this-map-is-not-a-function) (我問的)
{% endtab %}

{% tab title="⬇️ 應用" %}
* [array-and-matrix-methods](../../../builtin/arr/array-and-matrix-methods "mention")
{% endtab %}
{% endtabs %}
