Javascript 中的映射数组与序号

Mapping Array in Javascript with sequential numbers

以下代码:

let myArray = Array.apply(null, {length: 10}).map(Number.call, Number);

创建以下数组:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

我只是不明白为什么。我在互联网上找不到任何可以解释这种行为的内容。有谁知道为什么会这样工作?也许 link 某些文档?

Array.apply(null, {length: 10})

创建一个长度为 10 的数组,所有元素均为 undefined

.map(Number.call, Number)

将为每个元素调用 Number.call,参数为 (element, index, array),并将 this 设置为 Number。 call 的第一个参数将被视为 this (此处不相关),所有其他参数按原样传递,第一个参数是索引。 Number 现在会将其第一个参数 index 转换为数字(此处:将 return 索引,因为它是一个数字),这就是 map 将写入其 return 的内容数组。

让我们将表达式分解为两部分:

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 属性 和编号索引的 object。例如,以下是数组的有效表示形式:

var arr = {length: 3, 0: 1, 1: 2, 2: 3} // this represents the array [1, 2, 3]

在 JavaScript 中,我们将此 object 称为 "array-like object"。您可以使用常规 for 循环迭代此 object:

for (var i=0; i<arr.length; i++) {
  console.log(arr[i]) // logs 1 .. 2 .. 3
}

但是这个 object 不是 Array 构造函数的实例:

arr instanceof Array // false

幸运的是,任何array-like object都可以转换为数组:

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-likeobjects,我们可以看到等价于:

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.callFunction.prototype.call的shorthand:

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是调用Numberthis值,也就是说我们丢弃它。第二个参数是索引,也就是我们关心的值,第三个参数不需要,因为Number只取一个参数,所以这个也舍弃了。

在这种情况下,Number 函数用作恒等函数:

function id(x) {
  return x
}

它只是一个函数,只有一个参数,returns参数传入它。这就是我们所关心的。因为 index 已经是一个数字,所以我们得到:

Number(index) === id(index)

希望有助于进一步理解。

编辑: 扩展 Array(10) 无法使用迭代方法(例如 mapArray.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
Array.apply(null, {length: 10}) 

//> 输出的是undefined数组,长度为10,为什么: 它需要 null 来执行,因为它不需要 this 来执行,此外这个参数必须是 tepeof 对象,并且我们知道:typeof null === 'object',如果你将放置任何其他对象而不是 null,它会做同样的事情,只关心第二个参数的长度(它测试第二个参数的长度),

.map(Number.call, 数字);

decompose(我们知道Number的构造函数是Function.prototype(Number.proto),所以:

.map(function(){
Function.prototype.call.apply(this, arguments)
}, Number)// the second arg is the this value that map function takes during each iteration

我们也知道这是Number,因为每次调用Number都是这样:

Function.prototype.call.apply(Number, arguments)

});//这里我们不需要第二个参数

现在我们重新组合一切: Function.prototype => 数字,

call.apply(Number, arguments); => call(arguments[0], arguments[1])

原因:arguments是类数组对象,我们仍然可以将它传递给apply方法,但是我们不能调用,因为它接受参数分隔 comma.You 不能将arguments定义为参数,所以你需要表明它正在寻找什么:索引,也就是参数[1],因为这个值它必须接受任何对象,所以它接受:null,或任何东西,但它必须存在:

return Number.call(null, arguments[1]);

这里的数字是恒等函数:

function example(x){
 return x;
}

所以数字(e)==例子(e); 所以第一次迭代:

Number(0) //it takes 0 index:

return 0..

然后:

Number(1)//cause it takes the index of the second element:

return 1..

感谢阅读...

es6 简化版

let k = Array.from({ length: 5 }).map((currentElement, i) => i)

console.log(k)

// Output -[0, 1, 2, 3, 4]