Расширить базовый класс Array в JavaScript

у меня есть пользовательский класс массива, который расширяет базовый класс массива. У меня есть пользовательский метод для удобства использования

export class ExampleArray extends Array {
    includesThing(thing) {
        ...

        return false
    }
}

однако существующие способы filter, map etc возвращает экземпляр массива. Я хотел бы вернуть экземпляр ExampleArray С помощью этих методов.

Я могу найти интерфейс для этих методов, но не их реализации. Как вызвать родительский метод и вернуть пользовательский EampleArray вместо этого? Что-то вроде после

export class ExampleArray extends Array {
    filter() {

    result = Array.filter()
    array = new ExampleArray()
    array.push(...result)

    return array
}

или это даже правильный способ расширить массив, чтобы сделать пользовательский массив?

4 ответов


вам не нужно переписывать или переопределять любой из методов массива. Просто убедитесь, что у вас есть правильный конструктор.

причина, по которой это работает, находится в спецификации ES6 здесь (Курсив мой):

9.4.2.3 ArraySpeciesCreate (originalArray, длина)

...

пусть C будет Get (originalArray, "конструктор"). ReturnIfAbrupt (C). Если IsConstructor (C) истинно, тогда

...

Это Array.filter используется для создания нового массива-он получает конструктор исходного объекта и использует его для создания фильтрованного массива.

вот код из другого ответа с переопределенными фильтрами и методами карты, и он работает одинаково без них. Этот код (с помощью консоли Chrome):

class ExampleArray extends Array {
  constructor(...args) {
    super(...args);
  }
  hasMoreThanTwoItems() {
    // example custom method
    return this.length > 2;
  }
  isExampleArray() {
    return true;
  }
}

const exampleArray = new ExampleArray(3, 4, 5, 6, 7);

// true, filtering will result in 3 items
console.log(
  exampleArray
    .filter(e => e > 4)
    .hasMoreThanTwoItems()
);

// false, filtering will result in zero items
console.log(
  exampleArray
    .filter(e => e > 10)
    .hasMoreThanTwoItems()
);

// true, is an ExampleArray
console.log(
  exampleArray
    .map(e => e * 2)
    .isExampleArray()
);

производит этот выход:

true
false
true

Я должен добавить, что это не хорошая модель для расширения javascript классы в целом, но массив, по-видимому, является расширяемым по дизайну.


вам понадобится тени существующей .filter и .map Так что, когда вызывается экземпляр ExampleArray ваш новая функции будут вызываться, а не Array.prototype функции. Внутри ExampleArray, вы можете получить доступ к super.map и super.filter для того, чтобы добраться до Array.prototype методы. Например:

class ExampleArray extends Array {
  constructor(...args) {
    super(...args);
  }
  hasMoreThanTwoItems() {
    // example custom method
    return this.length > 2;
  }
  isExampleArray() {
    return true;
  }
  
  // Shadow Array.prototype methods:
  filter(...args) {
    return new ExampleArray(
      // Spread the result of the native .filter into a new ExampleArray instance:
      ...super.filter.apply(this, args)
    );
  }
  map(...args) {
    return new ExampleArray(
      ...super.map.apply(this, args)
    );
  }
}

const exampleArray = new ExampleArray(3, 4, 5, 6, 7);

// true, filtering will result in 3 items
console.log(
  exampleArray
    .filter(e => e > 4)
    .hasMoreThanTwoItems()
);

// false, filtering will result in zero items
console.log(
  exampleArray
    .filter(e => e > 10)
    .hasMoreThanTwoItems()
);

// true, is an ExampleArray
console.log(
  exampleArray
    .map(e => e * 2)
    .isExampleArray()
);

обратите внимание, что есть и другие методы массива, которые возвращают массивы, включая splice, slice, и (экспериментально)flat и flatMap. Если вы хотите, чтобы они возвращали экземпляр пользовательского класса, а не по умолчанию Array экземпляр, следуйте тому же шаблону: тень Array.prototype имя функции и возвращает new ExampleArray заполнено результатом applying Array.prototype способ:

<fnName>(...args) {
  return new ExampleArray(
    ...super.<fnName>.apply(this, args)
  );
}

создайте новый фильтр имен методов и используйте внутри ключевое слово "super", например: super.filter (func)


гораздо проще-создать статические функции, которые принимают любой массив в качестве первого аргумента.

таким образом, массив по-прежнему расширен (функция wise). Также расширение массива сбивает с толку и не масштабируется в больших коллективах. Конструктор также будет потерян всякий раз, когда массив передается (например, JSON в HTTP-запросе) без дополнительной заботы.