又一个Call vs Apply查询

时间:2015-05-30 16:49:05

标签: javascript

当我尝试运行以下代码时:

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,那么它的效果非常好。 (深度展平输入数组)。

除了link1link2之外,我还阅读了其他一些博客和文章,以了解其行为的原因。我要么找不到答案,要么就是忽略了它。 (原谅我的眼睛,如果在后一种情况下)

当然,这两者之间的根本区别在于:

  

apply - 要求可选参数为数组

     

call - 要求明确列出可选参数

通常,函数可以采用任何类型的参数 - 基元和非基元。所以,我的问题是:

  1. 调用方法的可选参数之一可以是Array类型还是其他非基本类型?

  2. 为什么在使用call时上述代码超出了调用堆栈?

  3. 编辑:由于使用了2种call方法,因此我的描述含糊不清。我做了修改以澄清。

2 个答案:

答案 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);

以下是callapply的简短说明:



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;
&#13;
&#13;

答案 1 :(得分:2)

当您flatten.call(null, val)作为数组调用val时,flatten函数作为参数val接收您作为call的可选参数传递的内容(未选中该类型),因此arguments[val]

您看到val数组位于数组中。当你在回调中呼叫reduceval时,仍然是一个数组,你什么都没有,这就是为什么它永远不会停止。

当您致电flatten.apply(null, val)val为数组,flatten函数会将 val的元素作为参数接收,因此{ {1}}与arguments(不是val)相同:您已经有效地展开了数组。这就是它起作用的原因。