# ⛔️ 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) 會出問題❗️

(💡 答案： [copy-with-getter-setter](https://lochiwei.gitbook.io/web/js/val/obj/extend/object.assign/copy-with-getter-setter "mention"))
{% endhint %}

{% tabs %}
{% tab title="❌ .assign" %}
💊 解藥： [.assigndescriptors](https://lochiwei.gitbook.io/web/js/val/obj/extend/mixin/.assigndescriptors "mention")

* [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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lochiwei.gitbook.io/web/js/val/obj/extend/object.assign/object.assign-causing-typeerror.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
