Рекурсивный вызов функции javascript

я могу создать рекурсивную функцию в переменной, вот так:

/* Count down to 0 recursively.
 */
var functionHolder = function (counter) {
    output(counter);
    if (counter > 0) {
        functionHolder(counter-1);
    }
}

С этим functionHolder(3); выводит 3 2 1 0. Допустим, я сделал следующее:

var copyFunction = functionHolder;

copyFunction(3); выводит 3 2 1 0 как выше. Если бы я тогда изменился functionHolder следующим образом:

functionHolder = function(whatever) {
    output("Stop counting!");

затем functionHolder(3); даст Stop counting!, как ожидалось.

copyFunction(3); теперь 3 Stop counting! как это относится к functionHolder, не функция (на которую она сама указывает). Это может быть желательно в некоторых обстоятельствах, но есть ли способ написать функцию так, чтобы она вызывала себя, а не переменную, которая ее содержит?

то есть, можно ли изменить только строку functionHolder(counter-1); так что прохождение всех этих шагов все равно дает 3 2 1 0 когда мы называем copyFunction(3);? Я пытался!--26--> но это дает мне ошибку this is not a function.

5 ответов


Использование Именованных Выражений Функция:

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

var factorial = function myself (n) {
    if (n <= 1) {
        return 1;
    }
    return n * myself(n-1);
}
typeof myself === 'undefined'

здесь myself и виден только внутри функции.

вы можете использовать это частное имя для рекурсивного вызова функции.

посмотреть 13. Function Definition спецификации ECMAScript 5:

в Идентификатор в FunctionExpression можно ссылаться изнутри FunctionBody в FunctionExpression разрешить функция может вызывать саму себя рекурсивно. Однако, в отличие от FunctionDeclaration, идентификатор в FunctionExpression не может ссылаться и не влияет на область, заключающую FunctionExpression.

обратите внимание, что Internet Explorer до версии 8 ведет себя неправильно, поскольку имя фактически отображается в окружающей среде переменных, и он ссылается на дубликат фактической функции (см. Патрик ДГкомментарий ниже).

используя аргументы.callee:

в качестве альтернативы вы можете использовать arguments.callee для ссылки на текущую функцию:

var factorial = function (n) {
    if (n <= 1) {
        return 1;
    }
    return n * arguments.callee(n-1);
}

5-е издание ECMAScript запрещает использование аргументов.callee () in строгого режима однако:

(от MDN): в обычных аргументах кода.callee ссылается на заключительная функция. Этот вариант использования слаб: просто назовите заключительную функцию! Более того, аргументы.вызываемый объект существенно препятствует оптимизации, такой как встроенные функции, поскольку необходимо обеспечить возможность ссылки на неинлайнированную функцию, если аргументы.вызываемый доступен. аргументы.вызываемый объект для функций строгого режима-это свойство, которое не удаляется при установке или извлечении.


Вы можете получить доступ к самой функции, используя arguments.callee [MDN]:

if (counter>0) {
    arguments.callee(counter-1);
}

это сломается в строгом режиме, однако.


Я знаю, что это старый вопрос, но я подумал, что представлю еще одно решение, которое можно использовать, если вы хотите избежать использования именованных выражений функций. (Не говоря, что вы должны или не должны избегать их, просто представляя другое решение)

  var fn = (function() {
    var innerFn = function(counter) {
      console.log(counter);

      if(counter > 0) {
        innerFn(counter-1);
      }
    };

    return innerFn;
  })();

  console.log("running fn");
  fn(3);

  var copyFn = fn;

  console.log("running copyFn");
  copyFn(3);

  fn = function() { console.log("done"); };

  console.log("fn after reassignment");
  fn(3);

  console.log("copyFn after reassignment of fn");
  copyFn(3);

Вы можете использовать Y-комбинатор: (Википедия)

// ES5 syntax
var Y = function Y(a) {
  return (function (a) {
    return a(a);
  })(function (b) {
    return a(function (a) {
      return b(b)(a);
    });
  });
};

// ES6 syntax
const Y = a=>(a=>a(a))(b=>a(a=>b(b)(a)));

// If the function accepts more than one parameter:
const Y = a=>(a=>a(a))(b=>a((...a)=>b(b)(...a)));

и вы можете использовать его как это:

// ES5
var fn = Y(function(fn) {
  return function(counter) {
    console.log(counter);
    if (counter > 0) {
      fn(counter - 1);
    }
  }
});

// ES6
const fn = Y(fn => counter => {
  console.log(counter);
  if (counter > 0) {
    fn(counter - 1);
  }
});

вот один очень простой пример:

var counter = 0;

function getSlug(tokens) {
    var slug = '';

    if (!!tokens.length) {
        slug = tokens.shift();
        slug = slug.toLowerCase();
        slug += getSlug(tokens);

        counter += 1;
        console.log('THE SLUG ELEMENT IS: %s, counter is: %s', slug, counter);
    }

    return slug;
}

var mySlug = getSlug(['This', 'Is', 'My', 'Slug']);
console.log('THE SLUG IS: %s', mySlug);

заметил, что counter считает "назад" в отношении того, что slug ' s значение. Это из-за позиции, в которой мы регистрируем эти значения, как функция рецидивирует перед регистрацией -- таким образом, мы по существу продолжаем гнездиться все глубже и глубже в стека вызовов до ведение журнала происходит.

как только рекурсия встречает окончательный элемент стека вызовов, она батуты " out " функции вызывает, в то время как первое приращение counter происходит внутри последнего вложенного вызова.

Я знаю, что это не "исправление" кода спрашивающего, но, учитывая название, я думал, что буду обобщенно иллюстрировать рекурсия для лучшего понимания рекурсии, откровенная.