Почему при фильтрации javascript объект класса, который расширяет массив, вызывает его конструктор? - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть этот пример кода:

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() );

Это более простая версия того, что я уже написал.Мое приложение работало до сих пор, но остановилось в тот момент, когда мне нужно было отфильтровать данные в моем расширенном массиве.Я обнаружил, что проблема заключается в том, что вызов функции фильтра для объекта моего класса внутренне вызывает конструктор.Код выше показывает этот пример.Есть ли способ обойти эту проблему, потому что я не могу снова вызвать конструктор в этой точке.Кроме того, я обнаружил (используя этот простой TestClass), что фактический вывод не тот, который я ожидал - я получил массив из 4 элементов с идентификаторами 3, 4, 3, 4. Может кто-нибудь объяснить, что здесь происходит?

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Согласно спецификации,

Array.filter говорит

Пусть будет?ArraySpeciesCreate (O, 0).

(здесь O - исходный массив и A результат)

и ArraySpeciesCreate говорит

Пусть C будет?Get (originalArray, "constructor").

С точки зрения непрофессионала, X.filter создает новый объект и применяет к нему конструктор X.Вот почему ваш конструктор вызывается снова.

В общем, этот дизайн нужно исправить.Ваш TestClass расширяет Array, теперь, если вы замените Array на TestClass во всем приложении, будет ли он вести себя так же?Очевидно, нет, это означает, что ваш TestClass нарушает фундаментальный принцип ООП, так называемый LSP, и должен быть переработан (например, путем объединения массива вместо его расширения).

0 голосов
/ 03 декабря 2018

Symbol.species предоставляет способ вернуть не другой экземпляр производного класса, а для .eg в этом случае экземпляр 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; }
...