Array子类Matrix无法继承父类map/slice方法的问题排查
错误原因
当在Matrix实例上调用slice、map等Array原生方法时,这些方法默认会使用当前实例的构造函数(即Matrix)创建新的返回实例。但你的Matrix构造函数要求必须传入nrows和ncols两个整数参数,而slice等方法调用构造函数时只会传入一个参数(新数组的长度),导致构造函数的参数校验逻辑触发TypeError,最终表现为RangeError: Invalid array length。
举个例子:调用this.slice(0, 2)时,内部会执行new Matrix(2),此时构造函数的ncols参数为undefined,不满足Number.isInteger(ncols)的校验规则,直接抛出错误,导致slice无法完成新实例的创建。
解决方法
方法1:设置Symbol.species返回Array(推荐)
通过定义Matrix[Symbol.species]为Array,让Array原生方法创建新实例时使用普通Array而非Matrix,无需逐个重写方法:
class Matrix extends Array { static get [Symbol.species]() { return Array; } // 原构造函数及其他方法保持不变 }
修改后可以直接调用this.slice()、this.map()等方法,它们会返回普通Array,不会触发Matrix构造函数的参数校验错误。简化后的rows方法如下:
rows() { const rowArray = []; for (let k = 0; k < this.nrows; k++) { const start = k * this.ncols; const stop = start + this.ncols; rowArray.push(this.slice(start, stop)); } return rowArray; }
方法2:重写Array原生方法
逐个重写slice、map等方法,让它们调用Array原型上的方法并返回普通Array:
class Matrix extends Array { slice(...args) { return Array.prototype.slice.call(this, ...args); } map(...args) { return Array.prototype.map.call(this, ...args); } // 其他需要重写的方法如filter、concat等同理 }
方法3:兼容Array构造函数签名
修改Matrix构造函数,支持单参数的情况(即只传入一个参数时,默认视为1行N列的矩阵):
constructor(nrows, ncols, elem = (x, y) => undefined) { // 兼容Array构造函数的单参数形式 if (ncols === undefined) { ncols = nrows; nrows = 1; } // 原参数校验逻辑 if (!Number.isInteger(nrows) || !Number.isInteger(ncols) || nrows < 0 || ncols < 0) { throw new TypeError("Invalid nrows or ncols argument"); } const size = nrows * ncols; super(size); this.nrows = nrows; this.ncols = ncols; this.shape = [nrows, ncols]; // 原填充逻辑不变 }
这种方式下,slice等方法会返回1行N列的Matrix实例,适合需要保持返回值为Matrix类型的场景,但需确保这种单维度Matrix符合业务逻辑。
内容的提问来源于stack exchange,提问作者invader.zimm

