以下代码:
let myArray = Array.apply(null, {length: 10}).map(Number.call, Number);
创建以下数组:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
我只是不明白为什么。我在互联网上找不到任何可以解释这种行为的内容。有谁知道为什么会这样工作?也许是一些文档的链接?
最佳答案
让我们将表达式分解为两部分:
1) 让我们讨论第一个表达式:
Array.apply(null, {length: 10})
在 JavaScript 中,Array
构造函数可以使用一个参数来创建一定长度的数组,例如:
Array(10) // makes an array of length 10
这个数组是一个稀疏数组(一个包含没有元素的索引的数组)。您可以想象我们生成了以下数组:
[,,,,,,,,] // Array with 10 indexes, but no elements
您可以将 JavaScript 中的数组视为具有 length
属性和编号索引的对象。例如,以下是数组的有效表示形式:
var arr = {length: 3, 0: 1, 1: 2, 2: 3} // this represents the array [1, 2, 3]
在 JavaScript 中,我们称这个对象为“类数组对象”。您可以使用常规 for
循环迭代此对象:
for (var i=0; i<arr.length; i++) {
console.log(arr[i]) // logs 1 .. 2 .. 3
}
但是这个对象不是Array
构造函数的实例:
arr instanceof Array // false
幸运的是,任何类数组对象都可以转换为数组:
Array.prototype.slice.call({length: 3, 0: 1, 1: 2, 2: 3}) // [1, 2, 3]
所有数组方法都是有意通用的以允许这种行为,因此您可以轻松地使用 forEach
进行循环,例如:
Array.prototype.forEach.call({length: 3, 0: 1, 1: 2, 2: 3}, function(x) {
console.log(x)
})
现在,回到第一个表达式:
Array.apply(null, {length: 10})
分解上面关于类数组对象的表达式,我们可以看到它等价于:
Array(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
换句话说,我们正在创建一个包含 10 个元素的数组,其值为 undefined
(注意它不再是稀疏的)
2) 进入第二个表达式:
.map(Number.call, Number);
第一个参数是应用于数组中每个元素的回调函数,第二个参数是回调中的 this
值。
让我们分解这个表达式。首先我们可以把回调函数写成匿名函数:
Array.apply(null, {length: 10}).map(function() {
return Number.call.apply(this, arguments)
}, Number)
然后我们意识到Number.call
是Function.prototype.call
的简写:
Array.apply(null, {length: 10}).map(function() {
return Function.prototype.call.apply(this, arguments)
}, Number)
接下来,我们内联this
值:
Array.apply(null, {length: 10}).map(function() {
return Function.prototype.call.apply(Number, arguments)
})
最后我们分解函数的应用:
Array.apply(null, {length: 10}).map(function() {
return Number.call(arguments[0], arguments[1], arguments[2]) // map provides 3 arguments
})
如您所见,第一个参数,即 undefined
的元素,是调用 Number
的 this
值,也就是说我们丢弃它。第二个参数是索引,这是我们关心的值,第三个参数不需要,因为 Number
只接受一个参数,所以这个也被丢弃。
在这种情况下,Number
函数用作标识函数:
function id(x) {
return x
}
它只是一个带有一个参数的函数,该参数返回传递给它的参数。这就是我们所关心的。因为 index
已经是一个数字,所以我们得到:
Number(index) === id(index)
希望这有助于进一步理解。
编辑:扩展Array(10)
无法使用迭代方法(例如map
与)的原因Array.apply(null, {length: 10})
我们要看implementation of map (滚动到“Polyfill”标题)。
原因是因为正如我之前指出的,Array(10)
是一个稀疏数组,它没有任何值,只有一个长度。通过查看实现,我们可以看到发生了什么:
// 8. Repeat, while k < len
while (k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
可以看到在k in O
中,in
运算符先检查是否存在,结果不存在;它不是 undefined
,它只是不存在。这与像 O[k]
这样的属性访问不同,如果属性不存在,它会给你一个 undefined
的值。
var o = {}
'p' in o // false
o.p // undefined
关于javascript - 使用序列号在 Javascript 中映射数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37888470/