递归函数是否有特殊的作用域规则?

时间:2016-06-21 01:47:26

标签: javascript recursion scope

我正在研究一个需要递归函数的问题,并注意到嵌套执行似乎正在修改父代的参数:

var foo = function(ar) {
  console.log('Calling function - Array: ' + ar.toString());
  if(ar.length > 1){
    var ele = ar.pop();
    foo(ar);
    console.log('Function recursion ends - Array: ' + ar.toString() + ' Popped: ' + ele);
    return;
  } else {
    console.log('Function ends - Array: ' + ar.toString());
    return;
  }
}

foo([1,2,3]);

输出(缩进矿):

/*
Calling function - Array: 1,2,3
  Calling function - Array: 1,2
    Calling function - Array: 1
    Function ends - Array: 1
  Function recursion ends - Array: 1 Popped: 2
Function recursion ends - Array: 1 Popped: 3 <-- What happened to 2?
*/

这看起来很奇怪 - 因为我用[1,2,3]调用了函数,我希望函数的第一次迭代仍然保持在ar和{{}之间提供给它的所有元素。 {1}} - 但是当函数结束时,只有ele保留在提供的数组中 - 1发生了什么?嵌套执行2是否超出了第一个执行的变量?

我对JavaScript中函数作用域的理解会说传递给函数的变量只能在本地修改它们,而不能将它们导出回全局/父作用域,如下所示:

pop

但递归函数的输出似乎挑战了这种理解。

如何防止这些嵌套执行修改父参数以恢复丢失的var bar = 'bar'; function doBar(bar){ bar = 'foo'; } doBar(bar); console.log(bar); //Outputs 'bar'; ?我对JavaScript中作用域的理解是错误的吗?

在打开这个问题之前,我在抓住吸管的可怜尝试中尝试在闭包中执行该功能:

2

但是我得到了与没有使用闭包相同的结果 - 我怀疑是因为我明确地传递了var foo = function(ar) { console.log('Calling function - Array: ' + ar.toString()); if(ar.length > 1){ var ele = ar.pop(); (function(foo, ar){ foo(ar); })(foo, ar) console.log('Function recursion ends - Array: ' + ar.toString() + ' Popped: ' + ele); return; } else { console.log('Function ends - Array: ' + ar.toString()); return; } } ar,这与没有闭包没什么不同。

2 个答案:

答案 0 :(得分:1)

Array.prototype.pop()是一个可变操作。当您以递归方式调用foo时,该可变操作在调用范围内仍然有效。范围没有任何奇怪的事情,只是你可能期望foo内的操作不能修改你给它的参数的内部。数组通过引用传递,这意味着参数将引用调用范围所执行的相同数组。

相反,您可以致电arr.slice(0, -1)。这将返回数组的浅表副本,而不是修改现有数组。它将改变你获取数组最后一个索引的方式。

var foo = function(ar) {
  console.log('Calling function - Array: ' + ar.toString());
  if(ar.length > 1){
    var ar2 = ar.slice(0, -1);
    foo(ar2);
    console.log('Function recursion ends - Array: ' + ar2.toString() + ' Popped: ' + ar.slice(-1));
    return;
  } else {
    console.log('Function ends - Array: ' + ar.toString());
    return;
  }
}

foo([1,2,3]);

答案 1 :(得分:0)

正如@ 4castle建议的那样,如果你想在每次迭代时拥有更持久的数组视图,你可以用slice()克隆ar。或者,您也可以重新设计函数foo(),以便有更多空间使用&#34; current&#34; ar和ele。

&#13;
&#13;
var foo = function(ar) {
  console.log('Given Array: ' + ar.toString());
  
  if(ar.length === 0){
    console.log('recursion ended');
    return;
  }
  
  var ele = ar.pop();
  console.log('Array Now: ' + ar.toString() + ' After Popping: ' + ele);
  console.log(" ");

  foo(ar);
}

foo([1,2,3]);
&#13;
&#13;
&#13;