Почему Array.apply(null, [args]) действует непоследовательно при работе с разреженными массивами?

Недавно я обнаружил следующий фрагмент кода на SO, чтобы помочь быстро заполнить массив значениями по умолчанию:

Array.apply(null, new Array(3)).map(function() {return 0;});

Учитывая поведение конструктора Array и метода apply, приведенный выше фрагмент можно также переписать как таковой:

Array.apply(null, [undefined, undefined, undefined]).map(function() {return 0;});

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

var sparseArr = [3,,,4,1,,],
    denseArr = Array.apply(null, sparseArr).map(function(e) {
      return e === undefined ? 0 : e;
    });

// denseArr = [3,0,0,4,1,0]

Однако в этом случае возникают две странности:

  • Если конечный член sparseArr равен undefined, этот термин не отображается в denseArr
  • Если sparseArr содержит только один член (например, sparseArr = [1]) или один термин, за которым следует один конечный член undefined (например, sparseArr = [1,]), итоговый denseArr равен [undefined x 1]

Может ли кто-нибудь объяснить это поведение?

Ответ 1

new Array(3) [...] также можно переписать как [undefined, undefined, undefined]

Нет - как вы только что видели, конструктор массива создает разреженные массивы, поэтому его следует переписать как [,,,].

Если конечный член sparseArr равен undefined

Неа. Вы забываете о конечных комматиках, которые являются необязательными с EcmaScript 5. Фактически [1] просто эквивалентен [1,] (обе имеют длину 1).

Чтобы получить разреженные "слоты", вам нужно будет добавить дополнительные запятые:

[] // empty array
[,] // empty array
[,,] // [undefined x 1]
[,,,] // [undefined x 2]

Если sparseArr содержит только один член, результирующий denseArr равен [undefined x N]

Подумайте, что значит называть метод apply:

Array.apply(null, [3,,4,1]) ≡ Array(3, undefined, 4, 1)
Array.apply(null, [3,4]) ≡ Array(3, 4)
Array.apply(null, [1]) ≡ Array(1)

И вы знаете, что делает Array конструктор при вызове с помощью одного числового аргумента - он создает разреженный массив этой длины...

Ответ 2

С ECMA 262 вы можете иметь конечную запятую в массивах. Ее присутствие никак не изменяет содержимое массива.

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

Примечание: поскольку undefined в массивах имеет нестандартное поведение в IE и 9, я бы не использовал его там. Вместо этого используйте null.

Тот факт, что у вас есть удивительные результаты, когда sparseArr содержит один элемент, состоит в том, что у Array есть два разных конструктора: если вы передаете ему несколько аргументов, он создает массив с этой последовательностью, если вы передаете ему одиночное число, он создает массив длины "число", заполненное undefined.

new Array(1, 2, 3)
=> [1, 2, 3]

new Array(2)
=> [undefined, undefined]