# Object extension

[JS](../../..) ⟩ [object](../../obj) ⟩ [built-in](..) ⟩ [Object]() ⟩ extension&#x20;

{% tabs %}
{% tab title="🔴 主題" %}
* [merge-with](ext/merge-with "mention") - copy (own enumerable) properties from other sources.
{% endtab %}

{% tab title="💾 程式" %}
* replit ⟩ [Object\_ext.js](https://replit.com/@pegasusroe/play-nodejs#ext/Object_ext.js)

```javascript
// ⭐️ Global Functions

// check if value is object
function isObject(value) { return value === Object(value) }

// check if value is primitive
function isPrimitive(value) { return !isObject(value) }


// ⭐️ extending Object.prototype
// -------------------------------------
//    method/accessor      parameters
// -------------------------------------
// 🔹 .defineProperty     (prop, desc)
// 🔹 .defineProperties   (desc)
// 🔹 .propertyDescriptor (prop)
// 🔹 .assign             (...sources)
// 🔹 .mergeWith          (...sources)
// 🔸 .prototype
// 🔸 .keys
// 🔸 .symbols

Object.defineProperties(Object.prototype, {

    // 🔹 obj.defineProperty(prop, desc)
    defineProperty: {
        value: function(prop, descr) {
            return Object.defineProperty(this, prop, descr);
        },
    },

    // 🔹 obj.defineProperties(desc)
    defineProperties: {
        value: function(descr) {
            return Object.defineProperties(this, descr);
        },
    },

    // 🔹 obj.propertyDescriptor(prop)
    propertyDescriptor: {
        value: function(prop) {
            return Object.getOwnPropertyDescriptor(this, prop);
        },
    },

    // 🔹 obj.assign(...sources)
    assign: {
        value: function(...sources) {
            return Object.assign(this, ...sources);
        },
    },

    // 🔹 obj.mergeWith(...sources)
    mergeWith: {
        value: function(...sources) {

            // prepare to collect descriptors
            const descriptors = {};
            
            // collect all descriptors from sources
            sources.forEach(source => {
                
                // own enumerable (string-keyed) properties -> descriptors
                source.keys.forEach(key => {
                    descriptors[key] = source.propertyDescriptor(key);
                });
        
                // own enumerable (symbol-keyed) properties -> descriptors
                source.symbols.forEach(sym => {
                    const desc = source.propertyDescriptor(sym);
                    if (desc.enumerable) descriptors[sym] = desc;
                });
                
            });

            // merge and return
            return this.defineProperties(descriptors);
        },
    },

    // 🔸 obj.prototype
    prototype: {
        get: function() {
            return Object.getPrototypeOf(this);
        },
    },

    // 🔸 obj.keys (own enumerable)
    keys: {
        get: function() {
            return Object.keys(this);
        },
    },

    // 🔸 obj.symbols (own enumerable/non-enumerable)
    symbols: {
        get: function() {
            return Object.getOwnPropertySymbols(this);
        },
    },

});

// export
module.exports = { 
    isObject, 
    isPrimitive,
};
```
{% endtab %}

{% tab title="⬇️ 應用" %}
* property [attr](../../obj/prop/attr "mention")(s) of an object
{% endtab %}

{% tab title="📘 手冊" %}
* [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
{% endtab %}
{% endtabs %}
