当我尝试运行以下代码时:
function flatten(a) {
return [].slice.call(arguments).reduce(function(acc, val) {
return acc.concat(Array.isArray(val) ? flatten.call(null, val) : val);
}, []);
}
我收到以下错误:
未捕获RangeError:超出最大调用堆栈大小
但如果我使用flatten.apply
代替flatten.call
,那么它的效果非常好。 (深度展平输入数组)。
除了link1和link2之外,我还阅读了其他一些博客和文章,以了解其行为的原因。我要么找不到答案,要么就是忽略了它。 (原谅我的眼睛,如果在后一种情况下)
当然,这两者之间的根本区别在于:
apply - 要求可选参数为数组
call - 要求明确列出可选参数
通常,函数可以采用任何类型的参数 - 基元和非基元。所以,我的问题是:
调用方法的可选参数之一可以是Array
类型还是其他非基本类型?
为什么在使用call
时上述代码超出了调用堆栈?
编辑:由于使用了2种call
方法,因此我的描述含糊不清。我做了修改以澄清。
答案 0 :(得分:7)
你的错误在这里:
[].slice.call(arguments).reduce
^^^^^^^^^
所以,当您通过[x]
时,您在reduce
上呼叫[[x]]
,第一个val
变为[x]
。然后,您使用flatten
再次致电[x]
,故事重演。
另一方面,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]);
&#13;
答案 1 :(得分:2)
当您flatten.call(null, val)
作为数组调用val
时,flatten
函数作为参数val
接收您作为call
的可选参数传递的内容(未选中该类型),因此arguments
为[val]
。
您看到val
数组位于数组中。当你在回调中呼叫reduce
,val
时,仍然是一个数组,你什么都没有,这就是为什么它永远不会停止。
当您致电flatten.apply(null, val)
时val
为数组,flatten
函数会将 val
的元素作为参数接收,因此{ {1}}与arguments
(不是val
)相同:您已经有效地展开了数组。这就是它起作用的原因。