如何创建一个包含1…N的数组。
如何创建一个包含1…N的数组。
我正在寻找创建JavaScript数组的任何替代方案,该数组包含从1到N的数字,其中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.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, 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 5开始,Function.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)
相同。正如我们之前所观察到的那样,其结果是index
。这完成了一个元素与其索引相同的数组。
为什么要使用Array.apply(null, {length: N})
,而不是直接使用Array(N)
?毕竟,这两个表达式都会得到N个未定义元素的数组。区别在于,在前一种表达式中,每个元素都被明确设置为未定义,而在后一种表达式中,每个元素都没有被设置。根据.map()
的文档:
callback
仅为具有已分配值的数组索引调用;它不为已删除或未分配值的索引调用。
因此,Array(N)
是不够的;Array(N).map(Number.call, Number)
将得到一个未初始化的长度为N的数组。
兼容性
由于这种技术依赖于在ECMAScript 5中指定的Function.prototype.apply()
的行为,因此在先ECMAScript 5的浏览器中(如Chrome 14和Internet Explorer 9)将无法工作。