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


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

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

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

var copyFunction = functionHolder;

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

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

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

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

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

5 75

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, чтобы позволить функции вызывать себя рекурсивно. Однако, в отличие от объявления функции, на идентификатор в выражении функции нельзя ссылаться и он не влияет на область, заключающую выражение функции.

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

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

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

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

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

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

Вы можете получить доступ к самой функции, используя 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 значение. Это происходит из-за позиции, в которой мы регистрируем эти значения, как функция рецидивирует перед регистрацией -- Итак, мы по существу продолжаем вкладывать все глубже и глубже в call-stackдо происходит регистрация.

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

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