Почему Array.from не вызывает обратный вызов так же, как Array.prototype.map?

avatar
trincot
22 марта 2021 в 10:15
134
1
2

На момент написания описание MDN функции Array.from гласит:

Array.from() имеет необязательный параметр mapFn, который позволяет выполнять функцию map() для каждого элемента создаваемого массива.

Более четко, Array.from(obj, mapFn, thisArg)
имеет тот же результат, что и Array.from(obj).map(mapFn, thisArg),
за исключением того, что он не создает промежуточный массив.

Однако у него не в точности такое же поведение, как показано здесь:

function mapFn(...args) {
    console.log(...args);
}

let obj = { 0: "value", length: 1 };
let thisArg = { };

Array.from(obj, mapFn, thisArg);
Array.from(obj).map(mapFn, thisArg);

Array.from не передает третий аргумент функции обратного вызова.

Это соответствует спецификации ECMAScript 2020 для Array.from:

.

Звонок ( mapfn , thisarg , " rentsalue , K »).

В то время как для Array.prototype.map спецификация имеет:

Вызов ( callbackfn , thisArg «-значение , к , O »).

Очевидно, приведенная выше цитата из MDN не совсем верна.

Вопрос

Почему такая разница? Разве не было бы разумнее, если бы функция Array.from работала как функция map(), как указано в приведенной выше цитате из MDN?

Источник
VLAZ
22 марта 2021 в 10:39
0

Ммм, thisArg должно быть буквально значением this, переданным обратному вызову. Не третий параметр функции map. Это две разные вещи.

georg
22 марта 2021 в 10:44
0

Другими словами, Array.from не вызывает map, как сбивает с толку MDN, это является map.

VLAZ
22 марта 2021 в 10:45
0

@pilchard, а может и не быть. Массив (как показано) по крайней мере присутствует, но если передается итератор, нет возможности передать полностью материализованное представление этого итератора.

VLAZ
22 марта 2021 в 10:46
0

@georg Я полагаю, что вики имела в виду что-то вроде «позволяет вам выполнять операцию сопоставления», но это как бы потерялось в переводе на «позволяет вам выполнять .map()»

Ответы (1)

avatar
VLAZ
22 марта 2021 в 10:58
1

Цитата из MDN почти точно верна. Но он также неполный.

Вряд ли неверно:

который позволяет вам выполнить функцию map()

Он не выполняет собственный метод .map(). Он выполняет операцию сопоставления, которая почти такая же.

Неполный

Более четко, Array.from(obj, mapFn, thisArg)
имеет тот же результат, что и Array.from(obj).map(mapFn, thisArg),
за исключением того, что он не создает промежуточный массив.

В то время как значение true должно также включать "Пока не используется третий аргумент .map().", чтобы полностью соответствовать тому, как все работает.

Почему такая разница? Разве не было бы разумнее, если бы функция Array.from работала как функция map(), как указано в приведенной выше цитате из MDN?

Интересный вопрос, но ответ на самом деле скучный. Это потому, что вы можете использовать Array.from() для не-массивов.

Почему это важно? Вы показали массив, подобный отображению. Подобный массиву существует целиком и может передаваться как третий аргумент. На самом деле это по реализации .map()

function mapFn(...args) {
    console.log(this, ...args);
//              ^^^^^ also log this for clarity
}

let obj = { 0: "value", length: 1 };
let thisArg = { this: "arg" };

Array.prototype.map.call(obj, mapFn, thisArg);

Однако это не так, если все значение не существует, например, если вы используете итератор:

function* example() {
  yield 1;
  yield 2;
  yield 3;
  if (Math.random() < 0.5) //make the iterator result uncertain
    yield 4;
}

function mapFn(...args) {
    console.log(...args);
}

Array.from(example(), mapFn)

Каждый раз, когда вызывается обратный вызов сопоставления, ему известны только текущие существующие значения. Итератор еще не материализован, поэтому вы не только не можете получить полное представление о нем, но и Array.from() преобразует его в материализованное представление. Это проблема курицы и яйца.

trincot
22 марта 2021 в 11:15
0

Но тем не менее, если бы первый аргумент, указанный для Array.from, был бы передан в качестве третьего аргумента функции обратного вызова — и это относится к итератору — функция обратного вызова все равно могла бы использовать эту ссылку для выполнения других действий, кроме доступа к значениям. Например, он может принять решение о вызове iter.return(), что на самом деле может оказаться удобным в некоторых обстоятельствах.

VLAZ
22 марта 2021 в 11:19
0

@trincot, но это приводит к несоответствиям. Теперь вы не можете создать один mapFn, потому что последний аргумент может быть массивом, может быть похожим на массив, может быть объектом-генератором (хорошо, я немного схитрил в ответе), это может быть итератор. У последнего может даже не быть .return(), поскольку это необязательный метод, поэтому вы даже не сможете его вызвать. И подобный массиву может иметь .return(), который не соответствует протоколу итератора. Это просто беспорядок. Кроме того, ожидается, что отображение не изменит то, что оно отображается.

trincot
22 марта 2021 в 11:35
0

Верно, но этот аргумент уже может быть сделан при выполнении стандартного вызова .map для массива: я мог бы решить иметь свои собственные свойства для этого массива или использовать объект, который является экземпляром класса, расширяющего массив, и тем не менее, я все равно смогу получить этот нестандартный массив в качестве третьего аргумента в своем обратном вызове, что даст мне доступ к нестандартным методам. Более того, если во время разработки мы знаем «тип» того, что мы отображаем, почему было бы непоследовательно использовать это знание в этом конкретном сценарии?

VLAZ
22 марта 2021 в 11:41
0

*пожимает плечами* Вы можете привести этот аргумент для очень многих вещей. «Но что, если <что-то, что выходит за рамки спецификации>». Это так задумано. Судя по всему, вам не совсем подходит. Лично я сожалею об отсутствии оператора привязки. Но этот язык не для вас или меня. Придется идти на уступки, что вводить и как это использовать. Если подавляющее большинство отображений не используют третий аргумент, то отправка Array.from без него кажется разумным компромиссом по сравнению со сложностью.

VLAZ
22 марта 2021 в 11:49
0

@trincot из интереса я проверил предложение для Array.from, чтобы убедиться, что в нем есть основания для исключения третьего аргумента. Он не содержит его сам по себе — он ссылается на ветку списка рассылки, в которой обсуждается (наряду с Array.of), но я не просматривал цепочку электронных писем. Название ветки — «Чистая победа: Array.from и Array.of», поэтому предполагается, что с самого начала не было планов относительно третьего аргумента.