子类继承Array后map调用自身构造器,如何改为调用Array构造器?
问题与解决方案
问题详情
当对继承自Array的ConsoleText类实例text调用map()方法时,会触发ConsoleText(text.length)构造器调用(示例中最后一行构造器输入为2)。如何修改才能让text.map()调用Array构造器?这是否属于继承的特性?
示例代码
'use strict' let initializingText = true; class ConsoleText extends Array { constructor(strings) { console.log('构造器输入:', strings); if (initializingText) { super(...strings); } else { super('item1', 'item2', 'item3', 'item4', 'item5'); } } } let text = new ConsoleText(['a', 'b']); // 构造器输入:(2) ['a', 'b'] console.log('text:', text); // text: ConsoleText(2) ['a', 'b'] initializingText = false; // 切换以避免解构数字时出错 console.log(text.map(() => 'q')); // 构造器输入:2 // ConsoleText(5) ['q', 'q', 'item3', 'item4', 'item5']
解答
这是继承的特性吗?
是的,这属于JavaScript数组子类的固有特性。数组的迭代方法(如map、filter、slice等)在创建新数组时,默认会使用当前实例的constructor构造新对象,也就是示例中的ConsoleText构造器。所以调用map时,会传入新数组的长度作为参数,触发ConsoleText的构造函数。
修改方法
通过重写类的Symbol.species getter,指定迭代方法创建新数组时使用Array构造器而非子类ConsoleText。Symbol.species是内置符号,用于定义派生对象的构造函数。
修改后的代码如下:
'use strict' let initializingText = true; class ConsoleText extends Array { constructor(strings) { console.log('构造器输入:', strings); if (initializingText) { super(...strings); } else { super('item1', 'item2', 'item3', 'item4', 'item5'); } } // 重写Symbol.species,指定使用Array构造器 static get [Symbol.species]() { return Array; } } let text = new ConsoleText(['a', 'b']); // 构造器输入:(2) ['a', 'b'] console.log('text:', text); // text: ConsoleText(2) ['a', 'b'] initializingText = false; // 切换以避免解构数字时出错 console.log(text.map(() => 'q')); // 不再触发ConsoleText构造器 // 输出:Array(2) ['q', 'q']
修改后,map方法会用Array构造器创建新数组,不会再触发ConsoleText的构造函数调用。
内容的提问来源于stack exchange,提问作者Pootis Spencer
相关产品推荐
相关产品推荐

