为什么在 javascript 中过滤扩展数组的 class 的对象调用它的构造函数?

Why in javascript filtering an object of a class that extends an Array calls it's constructor?

我有这个示例代码:

class TestClass extends Array {
 constructor() {
  console.log( 'constructor' );
  
  let ar = [];
  ar.push( { id: 1, name: 'a' } );
  ar.push( { id: 2, name: 'b' } );
  ar.push( { id: 3, name: 'c' } );
  ar.push( { id: 4, name: 'd' } );
  
  // finalizing object
  super( ...ar );
 }
 
 Foo() {
  console.log( 'foo' );
  return this.filter( item => item.id > 2 );
 }
}

let t = new TestClass();
console.log( t.Foo() );

这是我已经写过的更简单的版本。我的应用程序一直运行到现在,但在我需要过滤扩展数组中的数据时停止了。 我发现,问题是在我的 class 的对象上调用过滤器函数会在内部调用构造函数。上面的代码显示了该示例。 有没有办法绕过这个问题,因为此时我不能再次调用构造函数。此外,我发现(使用这个简单的 TestClass)实际输出不是我所期望的——我得到一个包含 4 个项目的数组,id 为 3、4、3、4。谁能解释这里发生了什么?

Symbol.species 提供了一种方法 return 不是派生的 class 的另一个实例,而是用于.e.g.在这种情况下,又是一个 Array 实例。

class TestClass extends Array {
  constructor() {
    console.log( 'constructor' );

    let ar = [];
    ar.push( { id: 1, name: 'a' } );
    ar.push( { id: 2, name: 'b' } );
    ar.push( { id: 3, name: 'c' } );
    ar.push( { id: 4, name: 'd' } );

    // finalizing object
    super( ...ar );
  }
  static get [Symbol.species]() { return Array; }

  Foo() {
    console.log( 'foo' );
    return this.filter( item => item.id > 2 );
  }
}

let t = new TestClass();
let a = t.Foo();

console.log('a : ', a);
console.log('(a instanceof Array) ? ', (a instanceof Array));
console.log('(a instanceof TestClass) ? ', (a instanceof TestClass));
.as-console-wrapper { max-height: 100%!important; top: 0; }

根据规范,

Array.filter

  1. Let A be ? ArraySpeciesCreate(O, 0).

(这里,O是原数组,A是结果)

ArraySpeciesCreate

  1. Let C be ? Get(originalArray, "constructor").

通俗地说,X.filter 创建一个新对象并将 X 的构造函数应用于它。这就是你的构造函数被再次调用的原因。

总的来说,这个设计需要修复。您的 TestClass 扩展了 Array,现在,如果您在整个应用程序中将 Array 替换为 TestClass,它的行为是否相同?显然不是,这意味着您的 TestClass 违反了基本的 OOP 原则,即所谓的 LSP,应该重新设计(例如,通过聚合数组而不是扩展数组)。