协慌网

登录 贡献 社区

创建一个包含 1 ... N 的 JavaScript 数组

我正在寻找下面的任何替代方法来创建一个包含 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.7082694901619107,0.95702225909214467,0.8586748542729765,0.8653848143294454,0.008339877473190427,0.991756622605026,0.8133423360995948,0.8377588465809822,0.5577575915958732,0.163636514581783035]

说明

首先,请注意Number.call(undefined, N)等于Number(N)Number.call(undefined, N)返回N我们稍后会使用这个事实。

Array.apply(null, [undefined, undefined, undefined])等效于Array(undefined, undefined, undefined) ,它生成一个三元素数组并为每个元素指定undefined

你如何将它推广到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 5Function.prototype.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) 。因此, [undefined, undefined, …, undefined].map(Number.call, Number)会将每个元素映射到(Number.call).call(Number, undefined, index, array) ,这与Number.call(undefined, index, array)相同Number.call(undefined, index, array) ,正如我们之前观察到的那样,它是对index评估。这样就完成了元素与索引相同的数组。

为什么要经历Array.apply(null, {length: N})而不仅仅是Array(N)的麻烦?毕竟,两个表达式都会产生一个未定义元素的N元素数组。不同之处在于,在前一个表达式中,每个元素都显式设置为 undefined,而在后者中,每个元素都从未设置过。根据.map()的文档:

仅为已分配值的数组的索引调用callback ; 对于已删除的索引或从未分配过值的索引,不会调用它。

因此, Array(N)不足; Array(N).map(Number.call, Number)将导致长度为N的未初始化数组。

兼容性

由于此技术依赖于 ECMAScript 5 中指定的Function.prototype.apply()行为,因此它不适用于 ECMAS 和 Internet Explorer 9 之类的 ECMAScript 前 5 浏览器。

在 ES6 中使用 Array from()keys()方法。

Array.from(Array(10).keys())
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

使用扩展运算符的较短版本。

[...Array(10).keys()]
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

ES6简单明了的方法

Array.from({length: 5}, (v, k) => k+1); 
// [1,2,3,4,5]

因此:

Array.from({length: N}, (v, k) => k+1);  
   // [1,2,3,...,N]

const range = (N) => Array.from({length: N}, (v, k) => k+1) ;

console.log(
  range(5)
)