> 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/object.assign/object.assign-causing-typeerror.md).

# ⛔️ Object.assign causing TypeError

{% hint style="danger" %}
[Object.assign(target, ...sources)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 裡面 source 物件的 <mark style="color:yellow;">**methods**</mark> 似乎<mark style="color:red;">**不是可以任意寫的**</mark>，當內部使用物件方法時，JS 引擎(還是 Node 環境❓)竟然會<mark style="color:red;">**檢查型別**</mark>❗️
{% endhint %}

{% hint style="warning" %}
下面的範例用了三個方法來產生一個二維陣列的「最長列長」：

* ✅ 使用全域函數 (global functions)
* ✅ 使用 Object.defineProperty()
* ❌ 使用 Object.assign()

但用最後一種方法 (mixin) 會出問題❗️

(💡 答案： [❗️Object.assign copies with getter/setter](/web/js/val/obj/extend/object.assign/copy-with-getter-setter.md))
{% endhint %}

{% tabs %}
{% tab title="❌ .assign" %}
💊 解藥： [.assignDescriptors()](/web/js/val/obj/extend/mixin/.assigndescriptors.md)

* [replit](https://replit.com/@pegasusroe/js-max-row-length-by-Objectassign#index.js)

```javascript
// Array.prototype + .max(), .maxRowLength (by Object.assign)
Object.assign(Array.prototype, {

    // max something in the array
    max(mapf = (x) => x) {
        // -----------------------------------------
        return Math.max(...this.map(x => mapf(x)));
        //                 ^^^^^^^^
        // ⛔ TypeError: this.map is not a function
        // -----------------------------------------
    },

    // max row length of the matrix
    get maxRowLength() {
        return this.max(row => row.length);
    },
});

// matrix (2D array)
let m = [[1, 2, 3, 4], [1, 2], [1, 2, 3]];

m.max(row => row.length)    
m.maxRowLength
```

* explanation: [replit](https://replit.com/@pegasusroe/why-Objectassign-causes-TypeError#index.js)

```javascript
// ------------------------------------------------------------
// ⭐ `Object.assign()` copies with "get/set" operations, so if 
// • source object has a getter method, or 
// • target object has a setter method
// ⭐ they will be INVOKED❗, NOT COPIED❗.
// ------------------------------------------------------------

// the "source" object
let s = {

    // ⭐ function copied ✅
    max(mapf = (x) => x) {
        return Math.max(...this.map(x => mapf(x)));
        //                 ^^^^^^^^
        // ⛔ TypeError: this.map is not a function
    },

    // ⭐ getter invoked❗, NOT COPIED❗(during the assign❗)
    get maxRowLength() {
        return this.max(row => row.length);
    },
};

// ⭐ during `Object.assign`:
// ---------------------------------------------
// (function copied ✅)
// • t.max = t.max 
//   
// (source getter invoked❗, NOT COPIED❗)
// • t.maxRowLength =   s.maxRowLength
//                   ╰⭐ getter invoked ╯
//
//   => return this.max(...)                  // ⭐ this === s
//       => return Math.max(...this.map(...)) // ⛔ `s.map` is not a function
//                             ^^^^^^^^
// ⛔ TypeError: this.map is not a function
let t = Object.assign(Array.prototype, s);    // the "target" object

// matrix
let m = [[1, 2, 3, 4], [1, 2], [1, 2, 3]];    // can't even reach here!
```

{% endtab %}

{% tab title="✅ 全域函數" %}

* [replit](https://replit.com/@pegasusroe/js-max-row-length-glabal-functions#index.js)

```javascript
// max something in the array
function arrayMax(arr, mapf = x => x){
    return Math.max(...arr.map(x => mapf(x)));
}

// max row length of the 2D array
// assuming an element in the 2D array is called a "row".
function maxRowLength(matrix){
    return arrayMax(matrix, row => row.length);
}

// 2D array
let m = [[1, 2, 3, 4], [1, 2], [1, 2, 3]];

arrayMax(m, row => row.length)     // 4 ✅
maxRowLength(m)                    // 4 ✅
```

{% endtab %}

{% tab title="✅ .defineProperty" %}

* [replit](https://replit.com/@pegasusroe/js-max-row-length-ObjectdefineProperty#index.js)

```javascript
// Array.prototype + .max()
Array.prototype.max = function(f = x => x){
    return Math.max(...this.map(x => f(x)));
};

// Array.prototype + .maxRowLength (by Object.defineProperty)
Object.defineProperty(Array.prototype, 'maxRowLength', {
    get: function(){ return this.max(row => row.length) }
});

// matrix (2D array)
let m = [[1, 2, 3, 4], [1, 2], [1, 2, 3]];

m.max(row => row.length)    // 4 ✅
m.maxRowLength              // 4 ✅
```

{% endtab %}

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

* [Object.defineProperty()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)
* [Object.assign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
  {% 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 %}
  {% endtabs %}
