⛔️ Object.assign causing TypeError

Object.assign(target, ...sources) 裡面 source 物件的 methods 似乎不是可以任意寫的,當內部使用物件方法時,JS 引擎(還是 Node 環境❓)竟然會檢查型別❗️

下面的範例用了三個方法來產生一個二維陣列的「最長列長」:

  • ✅ 使用全域函數 (global functions)

  • ✅ 使用 Object.defineProperty()

  • ❌ 使用 Object.assign()

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

(💡 答案: ❗️Object.assign copies with getter/setter)

💊 解藥: .assignDescriptors()

// 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
// ------------------------------------------------------------
// ⭐ `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!

Last updated