> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/web/js/val/obj/extend/mixin.md).

# mixin

{% hint style="success" %}
a [mixin](https://en.wikipedia.org/wiki/Mixin) is a <mark style="color:orange;">**object**</mark> containing <mark style="color:yellow;">**methods**</mark> that can be <mark style="color:yellow;">**used by other objects**</mark> <mark style="color:orange;">**without**</mark> a need to <mark style="color:orange;">**inherit**</mark> from it.
{% endhint %}

{% tabs %}
{% tab title="🔴 主題" %}

* [mixin inheritance](/web/js/val/obj/extend/mixin/mixin-inheritance.md)
* [Object.assign()](/web/js/val/obj/extend/object.assign.md)
  * [⛔️ Object.assign causing TypeError](/web/js/val/obj/extend/object.assign/object.assign-causing-typeerror.md)
  * [❗️Object.assign copies with getter/setter](/web/js/val/obj/extend/object.assign/copy-with-getter-setter.md)
* [mergeWithoutOverride()](/web/js/val/obj/extend/mixin/mergewithoutoverride.md)
* [.assignDescriptors()](/web/js/val/obj/extend/mixin/.assigndescriptors.md)
* 💈範例：
  * [obj.prop(path)](/web/js/val/obj/prop/access/optional-chaining/obj.prop-path.md) - pollyfill for [optional chaining (?., ?.\[\])](/web/js/val/obj/prop/access/optional-chaining.md)
  * [mixin: HandleEvents](/web/js/val/obj/extend/mixin/mixin-handleevents.md)
    {% endtab %}

{% tab title="💾 程式" %}

* replit ⟩ [mixin](https://replit.com/@pegasusroe/JS-object-mixin#index.js)

```javascript
const { log } = console;

// -------------- Object.assign --------------

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);

// -------------- Mixins --------------

// ⭐ mixin (similar to Swift protocols)
let canSayHi = {
    sayHi() { log(`Hello ${this.name}!`) },
    sayBye() { log(`Bye ${this.name}!`) },
};

// User
class User {
    constructor(name) {
        this.name = name;
    }
}

// -------------------------------------------------
// ⭐ copy all enumerable own members from mixin(s)
//    to `User.prototype`, NOT `User` itself.
//
//             ╭ ⭐️ target ─╮  ╭source╮
Object.assign( User.prototype, canSayHi );
//
// -------------------------------------------------

// now User can say hi
new User("Dude").sayHi();          // Hello Dude!

[
    // same object    
    target,                        // { a: 1, b: 4, c: 5 }
    returnedTarget,                // { a: 1, b: 4, c: 5 }
    target === returnedTarget,     // true
    
    // same function
    User.prototype.sayHi === canSayHi.sayHi,    // true

].forEach(x => log(x));
```

{% endtab %}

{% tab title="⭐️ 重點" %}
{% hint style="danger" %}
in [Object.assign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign),&#x20;

* [primitive](/web/js/val/prim.md) (both **target** and **sources**) will be [wrapped](/web/js/val/prim/wrapper.md) to objects.
* <mark style="color:purple;">**null**</mark> or <mark style="color:purple;">**undefined**</mark> <mark style="color:orange;">**target**</mark> will trigger <mark style="color:red;">**TypeError**</mark>.
* <mark style="color:purple;">**null**</mark> or <mark style="color:purple;">**undefined**</mark> <mark style="color:green;">**sources**</mark> will be <mark style="color:yellow;">**ignored**</mark>.
  {% endhint %}

```javascript
const str  = 'abc';
const bool = true;
const num  = 10;
const sym  = Symbol('foo');

// • primitives wrapped
// • null and undefined (sources) ignored
const obj = Object.assign({}, str, null, bool, undefined, num, sym);

// only "string wrappers" have own enumerable properties.
console.log(obj);     // { "0": "a", "1": "b", "2": "c" }
```

* replit ⟩ [mixin object into "number"?](https://replit.com/@pegasusroe/JS-mixin-mix-object-into-number#index.js)

```javascript
const { log } = console;

// ----------------------------------------------
// ❌ assign object to "null" (Error)
// ⛔ TypeError: 
//    Cannot convert undefined or null to object
//
//    let o1 = Object.assign(null, {a: 1});
//                           ^^^^  <----
// ----------------------------------------------

// ✅ assign object to "number" (it's OK)
//    "number" is wrapped to object.
let o2 = Object.assign(3, {a: 1});

let n = 5;

// --------------------------------------------
// ⛔ TypeError:
//    Cannot create property 'b' on number '5'
//
//    n.b = 3;
//    ^  <----
// --------------------------------------------

[
    o2,                      // [Number: 3] { a: 1 }
    typeof o2,               // 'object'    (⭐ NOT 'number'❗)
    o2 instanceof Number,    // true        (⭐ still a `Number`❗)
    o2.a,                    // 1
    
    o2 ==  3,    // true
    o2 === 3,    // false        (⭐ NOT the same thing❗)
    3  === 3,    // true
    o2 + 4,      // 7
    
    n + n.b,     // NaN          ( 5 + undefined )
    typeof n,    // 'number'
    
].forEach(x => log(x));
```

{% endtab %}

{% tab title="📗 參考" %}

* [ ] JS.info ⟩ [Mixins](https://javascript.info/mixins)
* [ ] LogRocket ⟩ [How to copy objects in JavaScript: A complete guide](https://blog.logrocket.com/copy-objects-in-javascript-complete-guide/)
  {% endtab %}

{% tab title="📘 手冊" %}

* [Object.assign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)\
  copies all [enumerable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable) [own properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) from one or more *source objects* to a *target object,* and returns the modified target object.
* Classes ⟩ [Mix-ins](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#mix-ins)\
  MDN 官方的寫法與 JS.info ⟩ [Mixins](https://javascript.info/mixins) ​完全不一樣❗️
* [JSDoc](/web/appendix/jsdoc.md) ⟩ [mixin](https://jsdoc.app/tags-mixin.html)
  {% endtab %}

{% tab title="👥 相關" %}

* similar to [clone(obj)](/web/appendix/custom/custom-functions/clone-obj.md).
* [Object.assign()](/web/js/val/obj/extend/object.assign.md) uses [getter/setter](/web/js/val/class/member/getter-setter.md) to achieve its goal.
* here are [Broken mention](broken://pages/oMVrW0wBZ5od8eDA8LV6) for [Google Apps Script](/web/appendix/gas.md) objects.
  {% endtab %}

{% tab title="⬇️ 應用" %}

* [obj.prop(path)](/web/js/val/obj/prop/access/optional-chaining/obj.prop-path.md) - access object's property by its path.
* [SheetMethods](/web/appendix/gas/app/prototypes/app.sheet.prototype/sheetmethods.md) - mixin for [Google Apps Script](/web/appendix/gas.md) Sheet objects.
  {% endtab %}
  {% endtabs %}
