⛔️ Object.assign causing TypeError
Object.assign(target, ...sources) 裡面 source 物件的 methods 似乎不是可以任意寫的,當內部使用物件方法時,JS 引擎(還是 Node 環境❓)竟然會檢查型別❗️
下面的範例用了三個方法來產生一個二維陣列的「最長列長」:
✅ 使用全域函數 (global functions)
✅ 使用 Object.defineProperty()
❌ 使用 Object.assign()
但用最後一種方法 (mixin) 會出問題❗️
💊 解藥: .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
explanation: replit
// ------------------------------------------------------------
// ⭐ `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!
// 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 ✅
// 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 ✅
Last updated