javascript Array.apply 实际在做什么
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25512771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is Array.apply actually doing
提问by Neil
After reading this SO Question, I'm still a little confused as to what Array.apply is actually doing. Consider the following snippet:
在阅读了这个 SO Question 之后,我仍然对 Array.apply 实际在做什么感到有些困惑。考虑以下片段:
new Array(5).map(function(){
return new Array(5);
});
I expect this to init an array with 5 undefined entries, then map over them creating a two dimensional array of 5x5);
我希望这会初始化一个包含 5 个未定义条目的数组,然后映射它们创建一个 5x5 的二维数组);
Instead I just get the array as if it was never mapped over:
相反,我只是获取数组,就好像它从未被映射过一样:
[undefined, undefined, undefined, undefined, undefined]
When I wrap the constructor call to array in an Array.apply call, then map over that, it works as expected:
当我将构造函数调用包装在 Array.apply 调用中,然后映射到数组时,它按预期工作:
Array.apply(null, new Array(5)).map(function(){
return new Array(5);
});
resulting in;
导致;
[[undefined, undefined, undefined, undefined, undefined],
[undefined, undefined, undefined, undefined, undefined],
[undefined, undefined, undefined, undefined, undefined],
[undefined, undefined, undefined, undefined, undefined],
[undefined, undefined, undefined, undefined, undefined]];
What's the deal? Is Array.apply just another way of calling new Array(), or Array.prototype.constructor? Are there any other situations where this would be advantageous? Also, why didn't my first approach pick up on the map I was sending it through?
这是怎么回事?Array.apply 只是调用 new Array() 或 Array.prototype.constructor 的另一种方式吗?是否有任何其他情况这将是有利的?另外,为什么我的第一个方法没有在我发送它的地图上找到?
Thanks! -Neil
谢谢!-尼尔
回答by Dan Tao
Good question!
好问题!
The Array
constructor function (with can be used without new
), when passed more than 1 argument, creates an array containing the arguments passed in as its elements. So you can do this:
该Array
构造函数(具有可在不使用new
)时通过多于1个参数,生成包含在通过作为其元素的参数的阵列。所以你可以这样做:
Array(1, 2, 3); // => [1, 2, 3]
As you probably know, Function.prototype.apply
allows you to provide arguments to a function in the form of an array. So calling Array.apply
(which just inherits its .apply()
method from Function's prototype; any function that is an instance of Function would allows you to do this), which will be functionality equivalent to the code above:
您可能知道,Function.prototype.apply
允许您以数组的形式为函数提供参数。所以调用Array.apply
(它只是.apply()
从 Function 的原型中继承了它的方法;作为 Function 实例的任何函数都允许你这样做),这将是与上面代码等效的功能:
Array.apply(null, [1, 2, 3]); // => [1, 2, 3]
Now, here's why things are a bit confusing. The Array.prototype.map
method is spec'd in such a way that it deals with sparse arraysspecially. A sparse array is one that has a "length" that is greater than the number of elements that have actually been inserted. For example:
现在,这就是事情有点混乱的原因。该Array.prototype.map
方法的规范方式使其专门处理稀疏数组。稀疏数组的“长度”大于实际插入的元素数。例如:
var arr = [];
arr[0] = 'foo';
arr[5] = 'bar';
The array constructed above will have a length
property of 6, because it has an element at index 0 and one at index 5. However, since no elements were ever inserted between those indices, if you call map
on it you'll see that the mapping function does not get applied to the missing elements:
上面构造的数组的length
属性为 6,因为它在索引 0 处有一个元素,在索引 5 处有一个元素。但是,由于从未在这些索引之间插入任何元素,如果您调用map
它,您将看到映射函数不会应用于缺失的元素:
// This should throw, right? Since elements 1 through 4 are undefined?
var lengths = arr.map(function(s) { return s.length; });
// Nope!
lengths; // => [3, , , , , 3]
And why are we seeing this behavior in your example with new Array(5)
? You guessed it: because the array constructor, when given a single argument, creates a sparsearray with the specified length.
为什么我们会在您的示例中看到这种行为new Array(5)
?您猜对了:因为数组构造函数在给定单个参数时会创建一个具有指定长度的稀疏数组。
So the issue here is that while map
(and other methods on Array.prototype
, such as forEach
) behaves specially with respect to sparse arrays by skipping over the missing values, the Function.prototype.apply
method does not have any such special behavior.
所以这里的问题是,虽然map
(以及 上的其他方法Array.prototype
,例如forEach
)通过跳过缺失值对稀疏数组Function.prototype.apply
有特殊行为,但该方法没有任何此类特殊行为。
回答by AndreyGS
It is very intresting example. And pretty good answer by Dan Tao. But I think that I can give a little additional explanation.
这是一个非常有趣的例子。丹涛的回答很好。但我想我可以做一些额外的解释。
In the first case
在第一种情况下
new Array(5)
新数组(5)
creates an empty object, then it passes throw function and gived length of 5. Because of missing any other arguments this object will get no allocated entries.
创建一个空对象,然后它传递 throw 函数并给定长度为 5。由于缺少任何其他参数,该对象将不会获得分配的条目。
// Array(5) [ <5 empty slots> ]
And when you trying to "map" those entries nothing actualy happened because of missing real entries.
当您尝试“映射”这些条目时,由于缺少真实条目,实际上什么也没发生。
However, if you try after this step "array[0]" for example, it returns "undefined"...
但是,如果您在此步骤之后尝试“array[0]”,例如,它会返回“undefined”...
In the next case you are using "Call" method of Array() function after first "new Array(5)" (but actualy it has no differense "Call" or "Construct" method of call is used with Array function).
在下一种情况下,您将在第一个“new Array(5)”之后使用 Array() 函数的“Call”方法(但实际上它没有区别“Call”或“Construct”调用方法与 Array 函数一起使用)。
Array.apply(null, new Array(5))
Array.apply(null, new Array(5))
So "new Array(5)" already gived as result Array(5) [ <5 empty slots> ] and "Function.prototype.apply()" decomposes this array to the five parameters that Array() function gets in. And in the current step we get:
所以“new Array(5)”已经作为结果给出 Array(5) [ <5 empty slots> ] 和“Function.prototype.apply()”将这个数组分解为 Array() 函数传入的五个参数。在我们得到的当前步骤:
// Array(5) [ undefined, undefined, undefined, undefined, undefined ]
These are five real entries. And we can do "map()" with them. But there is a litle mistake in your result, because we currently get after
这是五个真实的条目。我们可以用它们做“map()”。但是你的结果有一个小错误,因为我们目前得到
Array.apply(null, new Array(5)).map(function() { return new Array(5); });
Array.apply(null, new Array(5)).map(function() { return new Array(5); });
a little bit different result
有点不同的结果
/*
[…]
0: Array(5) [ <5 empty slots> ]
1: Array(5) [ <5 empty slots> ] ?
2: Array(5) [ <5 empty slots> ] ?
3: Array(5) [ <5 empty slots> ] ?
4: Array(5) [ <5 empty slots> ]
*/
and to get more precise, to get "five on five, undefined" result we need to little upgrade your code
为了更精确,要获得“五对五,未定义”的结果,我们几乎不需要升级您的代码
Array.apply(null, new Array(5)).map(function(){ return Array.apply(null,new Array(5)); });
this will return "five on five, undefined" array.
这将返回“五对五,未定义”数组。
/*
[…]
0: Array(5) [ undefined, undefined, undefined, … ]
1: Array(5) [ undefined, undefined, undefined, … ]
2: Array(5) [ undefined, undefined, undefined, … ]
3: Array(5) [ undefined, undefined, undefined, … ]
4: Array(5) [ undefined, undefined, undefined, … ]
*/
But what I'm talking about is that not only "Function.prototype.apply()" has current behavior to decompose array without real entries. I'll give you an example:
但我要说的是,不仅“Function.prototype.apply()”具有分解没有实际条目的数组的当前行为。我给你举个例子:
Array(...new Array(5)).map(() => Array(...new Array(5)));
this will actualy gived to us exactly the same result - five on five undefined.
这实际上会给我们完全相同的结果 - 五对五未定义。
If we took a closer look:
如果我们仔细观察:
- In the first action
new Array(5)
Array() function returns an empty array but with length property value of '5' because it runs in "Construct" mode, and has one parameter (5). - The second and third action
Array.apply() | Array(...)
first of all spreads Array without elements to 5 parameters and then pass them to an Array().
- 在第一个操作中,
new Array(5)
Array() 函数返回一个空数组,但长度属性值为“5”,因为它在“构造”模式下运行,并且有一个参数 (5)。 - 第二个和第三个动作
Array.apply() | Array(...)
首先将没有元素的 Array 传播到 5 个参数,然后将它们传递给一个 Array()。
It's because of "apply()" or "..." behavior of decomposing arrays. When it gets length of an array it auto transform "empty slots" into undefined values.
这是因为分解数组的“apply()”或“...”行为。当它获得数组的长度时,它会自动将“空槽”转换为未定义的值。
Referense from Ecma-262/6.0
来自 Ecma-262/6.0 的参考
(http://www.ecma-international.org/ecma-262/6.0/#sec-function.prototype.apply)
( http://www.ecma-international.org/ecma-262/6.0/#sec-function.prototype.apply)
19.2.3.1 Function.prototype.apply
1. If IsCallable(func) is false, throw a TypeError exception.
2. If argArray is null or undefined, then Return Call(func, thisArg).
3. Let argList be CreateListFromArrayLike(argArray).
7.3.17 CreateListFromArrayLike (obj [, elementTypes] )
1. ReturnIfAbrupt(obj).
2. If elementTypes was not passed, let elementTypes be (Undefined, Null, Boolean, String, Symbol, Number, Object).
3. If Type(obj) is not Object, throw a TypeError exception.
4. Let len be ToLength(Get(obj, "length")).
5. ReturnIfAbrupt(len).
6. Let list be an empty List.
7. Let index be 0.
8. Repeat while index < len
a. Let indexName be ToString(index).
b. Let next be Get(obj, indexName).
c. ReturnIfAbrupt(next).
d. If Type(next) is not an element of elementTypes, throw a TypeError exception.
e. Append next as the last element of list.
f. Set index to index + 1.
9. Return list.
- Here in '8a' clause we get "0", "1"... indexNames to pass them in '8b' as argument names of arraylike object (in our case just array, without any 'like') - array["0"], array["1"]...
- Each of requested element values returns with "undefined" and then in 8'e' they consecutively appends to the arguments list.
- "CreateListFromArrayLike" returns with five arguments of undefined in list representation in '9'clause which is passed to Function.prototype.apply() and will be applied to an Array() (it will be looking like
- 在'8a'子句中,我们得到“0”、“1”... indexNames 将它们作为数组对象的参数名称传递到 '8b'(在我们的例子中只是数组,没有任何 'like') - array["0 "], 数组["1"]...
- 每个请求的元素值都返回“未定义”,然后在 8'e' 中它们连续附加到参数列表中。
- “CreateListFromArrayLike”在“9”子句中的列表表示中返回五个未定义的参数,该参数被传递给 Function.prototype.apply() 并将应用于 Array() (它看起来像
new Array(undefined,undefined,undefined,undefined,undefined)
).
new Array(undefined,undefined,undefined,undefined,undefined)
)。
Spread operator uses iteration protokol instead, but in practice they act simulary on this case.
Spread 运算符使用迭代协议代替,但实际上它们在这种情况下的行为是相似的。
回答by Varun Bhalla
The Array constructor , when passed with a single numeric value (Let's say n) , creates a sparse Array of length n and has zero elements in it... so .map() wont work on it as explained by Dan Tao...
Array 构造函数,当传递单个数值(假设为 n)时,会创建一个长度为 n 的稀疏数组,其中包含零个元素......所以 .map() 不会像 Dan Tao 解释的那样处理它......
let ar = Array(5); // Array(5) [ <5 empty slots> ]
So , you can use .fill() method , that changes all elements in an array to given value, from a start index (default 0) to an end index (default array.length). It returns the modified array... So we can fill the empty (sparse) array with "undefined" value... It won't be sparse anymore...And finally, you can use .map() on returned array...
因此,您可以使用 .fill() 方法,将数组中的所有元素更改为给定值,从开始索引(默认为 0)到结束索引(默认为 array.length)。它返回修改后的数组......所以我们可以用“未定义”值填充空(稀疏)数组......它不再是稀疏的......最后,你可以在返回的数组上使用 .map() 。 ..
let ar = Array(5).fill(undefined) ; // [undefined,undefined,undefined,undefined,undefined]
let resultArray = ar.map( el => Array(5).fill(undefined) );
this resultArray will be a 5*5 array with every value of undefined...
这个 resultArray 将是一个 5*5 的数组,每个值都是 undefined...
/*
[ [ undefined, undefined, undefined, undefined, undefined ],
[ undefined, undefined, undefined, undefined, undefined ],
[ undefined, undefined, undefined, undefined, undefined ],
[ undefined, undefined, undefined, undefined, undefined ],
[ undefined, undefined, undefined, undefined, undefined ] ]
*/
:)
:)