如何正确实现继承自Array的JavaScript扩展类的单元素实例化?
嘿,我太懂你现在的困扰了——继承Array的时候,那个构造器的歧义行为真的让人头大!原生Array的构造器在传单个数字时,是创建指定长度的空数组;传多个参数才会把参数当作元素。但你想要的是让单参数直接变成数组里的元素,同时还不想破坏fill这类原生方法的正常使用对吧?
先说说你之前写的构造器的问题:你在处理单参数时用了super()创建空数组再push元素,这确实能得到[1]这样的结果,但也完全改变了原生Array单参数场景的行为——原来的new Array(2)是长度为2的空数组,fill(0)会变成[0,0],但你的构造器里new FooArray(2)是[2],fill(0)只能得到[0],这就是你觉得fill用不了的核心原因,因为你改变了单参数场景下数组的初始状态。
那怎么解决这个问题呢?分两种思路,看你更倾向哪种行为:
思路一:让构造器完全以“元素列表”模式工作(和Array.of行为一致)
如果你希望不管传几个参数,都直接把参数当作数组的元素,完全抛弃原生的“单数字设长度”的行为,那可以这么写构造器:
class FooArray extends Array { constructor(...items) { // 先创建一个空数组 super(0); // 把所有参数都push进去,不管数量多少 this.push(...items); } // 额外加个静态方法,用来创建指定长度的空数组,弥补失去的原生能力 static ofLength(length) { const arr = new FooArray(); arr.length = length; return arr; } }
这样用起来就完全符合你的需求了:
new FooArray()→[]new FooArray(1)→[1]new FooArray(1, 2)→[1, 2]- 想要创建长度为2的数组再填充?用
FooArray.ofLength(2).fill(0)→[0, 0]
思路二:保留原生Array的双模式行为,但支持单元素实例化
如果你不想完全抛弃原生的行为,有时候还是需要用单数字设置长度,那可以直接用原生的Array.of来创建元素数组,同时让构造器保持原生逻辑:
class FooArray extends Array { // 这里不需要重写构造器,直接继承父类的就行 } // 用的时候,单元素数组这么创建: const singleItemArr = FooArray.of(1); // → [1] // 指定长度的数组还是用构造器: const lengthArr = new FooArray(2).fill(0); // → [0, 0]
这种方式的好处是完全保留了原生Array的所有行为,不会破坏fill、map这些方法的预期效果,只是创建单元素数组的时候需要用of方法而不是直接传单参数给构造器。
为什么你原来的构造器有问题?
你之前的写法里,当处理单参数时调用了super()(创建空数组)再push元素,这会导致数组的constructor属性可能出现不一致,而且直接改变了原生构造器的行为逻辑,使得依赖原生长度设置的方法无法按预期工作。而上面的思路一用统一的方式处理所有参数,同时通过静态方法补全了长度创建的能力,就不会有这个问题了。
备注:内容来源于stack exchange,提问作者msampaio

