Допустимо ли вызывать Array.map на итерабельном, который не является массивом?

gaazkam спросил: 12 мая 2018 в 03:47 в: javascript

Эта статья: Перемещение DOM с помощью функций filter (), map () и стрелок защищает такое использование Array.map, которое кажется мне странным. Более конкретно, автор статьи утверждает, что следующий фрагмент кода действителен и фактически лучше альтернативного Array.from(elements).map(...):

var elements = document.getElementsByClassName("bgflag");
BgFlags = Array.prototype.map.call(elements,
        element =>
        ({
            height: element.offsetTop,
            bgsrc: element.dataset.bgsrc,
            bgcolor: element.dataset.bgcolor,
            size: element.dataset.size,
            name: element.id,
            image: parseInt(element.dataset.image)
        })
    );

Это кажется наиболее подозрительным для моего неподготовленный глаз. Мы вызываем Array.prototype.map на то, что не является Array. Если только где-то прямо не указано, что это разрешено, это пахнет как неопределенное поведение для меня. Я быстро просмотрел соответствующую документацию MDN, но не смог найти, что такое использование разрешено там.

И все же автор статьи подчеркивает:

Even хотя map () является функцией Array.prototype, он работает с любым итерабельным массивом.

Является ли такое использование допустимым, как он утверждает? Если это так, это также относится к другим функциям Array.prototype.*, таким как filter, slice, возможно даже pop, push, другие?

2 ответа

Есть решение
T.J. Crowder ответил: 12 мая 2018 в 03:57

Если это явно не указано, что это разрешено

Это то, что указано в спецификации:

map намеренно является общим; он не требует, чтобы это значение было Array. Поэтому он может быть передан другим типам объектов для использования в качестве метода.

В большинстве методов на Array.prototype есть эта заметка. Поэтому те, которые не могут быть использованы таким образом, не имеют этой заметки.

Поэтому, если вы хотите использовать его, это, в сущности, вопрос стиля. Различные люди придут по-разному, будь то хороший / плохой / безразличный стиль.


Сторона Примечание: если вы собираетесь использовать map таким образом, а не писать это longhand каждый раз, дайте себе ярлык:

const map = Function.prototype.call.bind(Array.prototype.map);

, а затем используйте его следующим образом:

const result = map(arrayLike, e => /*...*/);

Пример Live:

const map = Function.prototype.call.bind(Array.prototype.map);

const arrayLike = {
  0: "zero",
  1: "one",
  2: "two",
  length: 3
};
const result = map(arrayLike, e => e.toUpperCase());
console.log(result);
Jonas W. ответил: 12 мая 2018 в 03:57

Да, как правило, работает. Однако я согласен с тем, что:

Array.from(elements).map(element => /*..*/);
// Or even
Array.from(elements, element => /*...*/);

намного чище.