我正在寻找以下方法的任何替代方法,以创建包含1到N的JavaScript数组,其中N仅在运行时已知。
var foo = [];
for (var i = 1; i <= N; i++) {
foo.push(i);
}
对我来说,我觉得应该有一种不用循环的方法。
我正在寻找以下方法的任何替代方法,以创建包含1到N的JavaScript数组,其中N仅在运行时已知。
var foo = [];
for (var i = 1; i <= N; i++) {
foo.push(i);
}
对我来说,我觉得应该有一种不用循环的方法。
当前回答
您可以这样做:
var N = 10;
Array.apply(null, {length: N}).map(Number.call, Number)
结果:[0,1,2,3,4,5,6,7,8,9]
或具有随机值:
Array.apply(null, {length: N}).map(Function.call, Math.random)
结果:[0.782694901619107,0.9572225909214467,0.8586748542729765,0.8653848143294454, 0.008339877473190427, 0.9911756622605026, 0.8133423360995948, 0.8377588465809822, 0.5577575915958732, 0.16363654541783035]
解释
首先,注意Number.call(undefined,N)等同于Number(N),它只返回N。我们稍后将使用这个事实。
Array.apply(null,[undefined,undefineed,undefinded])等同于Array(undefine,undefine,undefide),它生成一个三元素数组,并为每个元素分配undefine。
如何将其推广到N个元素?考虑一下Array()的工作原理,大致如下:
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;
}
自ECMAScript 5以来,Function.pr原型.apply(thisArg,argsArray)也接受鸭子类型的类似数组的对象作为其第二个参数。如果我们调用Array.apply(null,{length:N}),那么它将执行
function Array() {
var a = [];
for (var i = 0; i < /* arguments.length = */ N; i++) {
a.push(/* arguments[i] = */ undefined);
}
return a;
}
现在我们有一个N元素数组,每个元素都设置为undefined。当我们对其调用.map(callback,thisArg)时,每个元素都将被设置为callback.call(thisArg,element,index,array)的结果。因此,[未定义,未定义,…,未定义].map(Number.call,Number)会将每个元素映射到(Number.call).call(Number,undefined,index,array),这与Number.ccall(undefineed,index,array)相同,正如我们之前所观察到的,它的计算结果为index。这就完成了元素与其索引相同的数组。
为什么要解决Array.apply(null,{length:N})而不是Array(N)的问题?毕竟,这两个表达式都会产生一个未定义元素的N元素数组。不同之处在于,在前一个表达式中,每个元素都被显式设置为undefined,而在后一个表达式,每个元素从未被设置。根据.map()的文档:
回调仅对已赋值数组的索引调用;对于已删除或从未赋值的索引,不会调用它。
因此,Array(N)不足;数组(N).map(Number.call,Number)将导致长度为N的未初始化数组。
兼容性
由于该技术依赖于ECMAScript 5中指定的Function.protype.apply()的行为,因此它在之前的ECMAScript5浏览器(如Chrome 14和Internet Explorer 9)中不起作用。
其他回答
以下是摘要(在控制台中运行):
// setup:
var n = 10000000;
function* rangeIter(a, b) {
for (let i = a; i <= b; ++i) yield i;
}
function range(n) {
let a = []
for (; n--; a[n] = n);
return a;
}
function sequence(max, step = 1) {
return {
[Symbol.iterator]: function* () {
for (let i = 1; i <= max; i += step) yield i
}
}
}
var t0, t1, arr;
// tests
t0 = performance.now();
arr = Array.from({ length: n }, (a, i) => 1)
t1 = performance.now();
console.log("Array.from({ length: n }, (a, i) => 1) Took " + (t1 - t0) + " milliseconds.");
t0 = performance.now();
arr = range(n);
t1 = performance.now();
console.log("range(n) Took " + (t1 - t0) + " milliseconds.");
t0 = performance.now();
arr = Array.from(rangeIter(0, n));
t1 = performance.now();
console.log("Array.from(rangeIter(0, n)) Took " + (t1 - t0) + " milliseconds.");
t0 = performance.now();
arr = [...rangeIter(0, n)];
t1 = performance.now();
console.log("[...rangeIter(0, n)] Took " + (t1 - t0) + " milliseconds.");
t0 = performance.now();
arr = Array.from(sequence(n));
t1 = performance.now();
console.log("Array.from(sequence(n)) Took " + (t1 - t0) + " milliseconds.");
t0 = performance.now();
arr = [...sequence(n)];
t1 = performance.now();
console.log("[...sequence(n)] Took " + (t1 - t0) + " milliseconds.");
t0 = performance.now();
arr = Array(n).fill(0).map(Number.call, Number);
t1 = performance.now();
console.log("Array(n).fill(0).map(Number.call, Number) Took " + (t1 - t0) + " milliseconds.");
t0 = performance.now();
arr = Array.from(Array(n).keys());
t1 = performance.now();
console.log("Array.from(Array(n).keys()) Took " + (t1 - t0) + " milliseconds.");
t0 = performance.now();
arr = [...Array(n).keys()];
t1 = performance.now();
console.log("[...Array(n).keys()] Took " + (t1 - t0) + " milliseconds.");
最快的是Array(n).fill(0).map(Number.call,Number),第二个是[…Array(n).keys()]
但是。。。rangeIter的方式非常方便(可以内联),速度快,功能更强大
填充Array的新方法是:
const array=[…array(5).keys()]console.log(数组)
结果将为:[0,1,2,3,4]
对我来说,这是更有用的实用程序:
/**
* create an array filled with integer numbers from base to length
* @param {number} from
* @param {number} to
* @param {number} increment
* @param {Array} exclude
* @return {Array}
*/
export const count = (from = 0, to = 1, increment = 1, exclude = []) => {
const array = [];
for (let i = from; i <= to; i += increment) !exclude.includes(i) && array.push(i);
return array;
};
使用递归的ES6解决方案。不同于所有其他解决方案
const range = (n, A = []) => (n === 1) ? [n, ...A] : range(n - 1, [n, ...A]);
console.log(range(5));
Array.prototype.fill()
a = Object.keys( [].fill.call({length:7}, '' ) ).map(Number)
a.pop();
console.debug(a)
[0, 1, 2, 3, 4, 5, 6]