Рекурсивный вызов функции 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);
? Я пытался this(counter-1);
но это дает мне ошибку 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, чтобы позволить функции вызывать себя рекурсивно. Однако, в отличие от объявления функции, на идентификатор в выражении функции нельзя ссылаться и он не влияет на область, заключающую выражение функции.
обратите внимание, что 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
происходит внутри последнего вложенного вызова.Я знаю, что это не "исправление" на коде вопрошающего, но с учетом названия я думал, что в целом приведу пример рекурсия для лучшего понимания рекурсии, откровенная.