另一个 Call 与 Apply 查询
当我尝试运行以下代码片段时:
function flatten(a) {
return [].slice.call(arguments).reduce(function(acc, val) {
return acc.concat(Array.isArray(val) ? flatten.call(null, val) : val);
}, []);
}
我收到以下错误:
Uncaught RangeError: Maximum call stack size exceeded
但是,如果我使用
flatten.apply
而不是
flatten.call
,它就可以完美运行。(深度展平输入数组)。
除了这个 link1 和 link2 之外,我还阅读了其他一些博客和文章,以了解它为什么会如此表现。我要么找不到答案,要么一定是我忽略了它。(如果是后一种情况,请原谅我的眼睛)
当然,这两者之间的根本区别是:
apply - requires the optional parameter be an array
call - requires the optional parameters be listed explicitly
通常,函数可以采用任何类型的参数 - 原始和非原始。所以,我的问题是:
-
call 方法的可选参数之一可以是
Array
类型或其他非原始类型吗? -
当使用
call
时,为什么上述代码超出了调用堆栈?
编辑:由于使用了 2 个
call
方法,我的描述含糊不清。我做了一些更改以澄清。
您的错误在这里:
[].slice.call(arguments).reduce
^^^^^^^^^
因此,当您传递
[x]
时,您会在
[[x]]
上调用
reduce
,并且第一个
val
变成
[x]
。然后,您再次使用
[x]
调用
flatten
,故事又重复了一遍。
另一方面,
apply(..., val)
只会将
x
传递给
flatten
,从而将嵌套级别减少一级。
如果您对如何使用
apply
展平深层嵌套数组感兴趣,则无需递归即可实现:
while(ary.some(Array.isArray))
ary = [].concat.apply([], ary);
以下是
call
与
apply
的一个小例子:
function fun() {
var len = arguments.length;
document.write("I've got "
+ len
+ " "
+ (len > 1 ? " arguments" : "argument")
+ ": "
+ JSON.stringify(arguments)
+ "<br>");
}
fun.call(null, 123);
// fun.apply(null, 123); <-- error, won't work
fun.call(null, [1,2,3]);
fun.apply(null, [1,2,3]);
当您调用
flatten.call(null, val)
时,由于
val
是一个数组,
flatten
函数会接收
val
作为参数,而您将其作为可选参数传递给
call
(未检查类型),因此
arguments
为
[val]
。
您会看到
val
数组位于数组内部。当您调用
reduce
时,回调中的
val
将
仍然
是一个数组,您没有展平任何内容,这就是它永不停止的原因。
当您调用
flatten.apply(null, val)
时,由于
val
是一个数组,
flatten
函数会接收
元素
val
作为参数,因此
arguments
与
val
相同(而不是
[val]
):您已经有效地解开了数组。这就是它起作用的原因。