Você pode fazer isso:
var N = 10;
Array.apply(null, {length: N}).map(Number.call, Number)
resultado: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ou com valores aleatórios:
Array.apply(null, {length: N}).map(Function.call, Math.random)
resultado: [0.7082694901619107, 0.9572225909214467, 0.8586748542729765, 0.865384814329494, 0,008339877473190427, 0.9911756622605026, 0.8133423360995948, 0.8377588465809822, 0.55775759159369
Explicação
Primeiro, observe que Number.call(undefined, N)
é equivalente a Number(N)
, que apenas retorna N
. Nós usaremos esse fato mais tarde.
Array.apply(null, [undefined, undefined, undefined])
é equivalente a Array(undefined, undefined, undefined)
, que produz uma matriz de três elementos e atribui undefined
a cada elemento.
Como você pode generalizar isso para N elementos? Considere como Array()
funciona, que é mais ou menos assim:
function Array() {
if ( arguments.length == 1 &&
'number' === typeof arguments[0] &&
arguments[0] >= 0 && arguments &&
arguments[0] < 1 << 32 ) {
return [ … ]; // array of length arguments[0], generated by native code
}
var a = [];
for (var i = 0; i < arguments.length; i++) {
a.push(arguments[i]);
}
return a;
}
Desde o ECMAScript 5 , Function.prototype.apply(thisArg, argsArray)
também aceita um objeto semelhante a uma matriz do tipo pato como seu segundo parâmetro. Se invocarmos Array.apply(null, { length: N })
, ele executará
function Array() {
var a = [];
for (var i = 0; i < /* arguments.length = */ N; i++) {
a.push(/* arguments[i] = */ undefined);
}
return a;
}
Agora temos uma matriz de elementos N , com cada elemento definido como undefined
. Quando o chamamos .map(callback, thisArg)
, cada elemento será definido como o resultado de callback.call(thisArg, element, index, array)
. Portanto, [undefined, undefined, …, undefined].map(Number.call, Number)
mapearia cada elemento para (Number.call).call(Number, undefined, index, array)
, que é o mesmo Number.call(undefined, index, array)
que, como observamos anteriormente, avalia como index
. Isso completa a matriz cujos elementos são iguais ao seu índice.
Por que enfrentar o problema, em Array.apply(null, {length: N})
vez de apenas Array(N)
? Afinal, ambas as expressões resultariam em uma matriz de elementos indefinidos no elemento N. A diferença é que, na expressão anterior, cada elemento é definido explicitamente como indefinido, enquanto no último, cada elemento nunca foi definido. De acordo com a documentação de .map()
:
callback
é invocado apenas para índices da matriz que atribuíram valores; não é chamado para índices que foram excluídos ou aos quais nunca foram atribuídos valores.
Portanto, Array(N)
é insuficiente; Array(N).map(Number.call, Number)
resultaria em uma matriz não inicializada de comprimento N .
Compatibilidade
Como essa técnica depende do comportamento Function.prototype.apply()
especificado no ECMAScript 5, ela não funcionará em navegadores anteriores ao ECMAScript 5, como o Chrome 14 e o Internet Explorer 9.