2016-06-21 2 views
1

Я работаю над проблемой, которая требует рекурсивной функции и заметил, что вложенные казни, как представляется, изменение параметров материнской компании:Имеют ли рекурсивные функции специальные правила определения области видимости?

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 и ele - но вместо этого, когда функция завершается, только 1 остается в предоставленном массиве - что случилось с 2? Выполнено ли вложенное выполнение pop из переменной первого выполнения?

Мое понимание области видимости функции в JavaScript бы сказать, что переменные, передаваемые в функцию могут изменять их только локально и не экспортировать их обратно в рамки глобального/родителя, как показано здесь:

var bar = 'bar'; 

function doBar(bar){ 
bar = 'foo'; 
} 

doBar(bar); 
console.log(bar); //Outputs 'bar'; 

Но выход от рекурсивной функции, кажется, бросает вызов этому пониманию.

Как я могу предотвратить использование этих вложенных исполнений от родительского параметра, чтобы вернуть отсутствующий 2? Является ли мое понимание ошибок в JavaScript неправильным?


В моей жалкой попытке ухватиться за соломинку, прежде чем открыть этот вопрос, я пытался выполнение функции в замыкании:

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 и foo, делая его ничем иным, как без закрытия.

+0

Уплотненный Foo() выполняется на массиве, прежде чем войти что-нибудь. Попробуйте выполнить регистрацию до вложенного 'foo()' – charlietfl

+3

Нет, нет ничего особенного в рекурсии, и это не имеет ничего общего с областью видимости. Каждый вызов создает свою собственную переменную 'ar'. Просто они ссылаются на один и тот же объект массива, а 'pop' мутирует его. – Bergi

+0

К тому времени, когда вы начинаете регистрировать «Рекурсия функции заканчивается ...», что «есть», что вам подсказывает консоль? – JonSG

ответ

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]);

+2

Вы не обратились к ожиданию OP о том, что «переменные, переданные функции, могут только модифицировать их локально». В ответе следует явно упомянуть, что аргумент 'ar' каждого вызова ссылается * на тот же * массив. Как было прокомментировано непосредственно под вопросом, это действительно не рекурсия или область, а ссылки на массивы, не создающие копии массива. – nnnnnn

+0

«вы, возможно, ожидаете, что операции внутри' foo' не могут изменять параметры, которые вы ему даете ». Boom. Прямо там. Тем не менее удаление последнего элемента массива представляло собой часть того, как ведет себя эта функция. Поэтому копирование массива, поскольку вопрос был отмечен как обман, было лучшим решением. – HPierce

+0

@nnnnnn Я упомянул об этом, но теперь я прямо сказал это в ответе. Я рад, что он все равно ответил на вопрос. – 4castle

0

Как @ 4castle предлагает, если вы хотите иметь более долговечный вид массива на каждой итерации, можно клонировать ар с ломтиком(). Кроме того, вы также можете переделать свою функцию foo(), чтобы позволить вам больше места для работы с «текущими» ar и ele.

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]);

Смежные вопросы