Я могу создать рекурсивную функцию в переменной так:
/* 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
.
Ответы:
Использование выражений именованных функций:
Вы можете дать функциональному выражению имя, которое на самом деле является частным и видно только изнутри функции 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:Обратите внимание, что Internet Explorer до версии 8 ведет себя некорректно, так как имя фактически отображается в окружении переменных, включающих в себя, и ссылается на дубликат фактической функции (см. Комментарий Патрика DW ниже).
Используя arguments.callee:
В качестве альтернативы вы можете использовать
arguments.callee
для ссылки на текущую функцию:var factorial = function (n) { if (n <= 1) { return 1; } return n * arguments.callee(n-1); }
Пятая редакция ECMAScript запрещает использование arguments.callee () в строгом режиме , однако:
источник
myself
но на самом деле это видно в окружении включающих переменных и ссылается на дубликат фактическойmyself
функции. Вы должны иметь возможность установить внешнюю ссылку наnull
.return n * myself(n-1);
?Вы можете получить доступ к самой функции, используя
arguments.callee
[MDN] :if (counter>0) { arguments.callee(counter-1); }
Однако в строгом режиме это не работает.
источник
TypeError
, но я не нашел ничего, что официально заявляло бы, чтоarguments.callee
(или любое нарушение строгого режима) устарело за пределами «строгого режима».Вы можете использовать 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 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);
источник
Вот один очень простой пример:
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
значение. Это из-за позиции, в которой мы регистрируем эти значения, поскольку функция повторяется перед записью - поэтому мы, по сути, продолжаем все глубже и глубже встраиваться в стек вызовов до того, как произойдет регистрация.После того , как рекурсия соответствует конечному пункту вызова стека, это батуты «из» вызовов функций, в то время как первое приращение
counter
происходит внутри последний вложенного вызов.Я знаю , что это не «исправление» на коде спрашивающего, но получил титул я думал , что я в общем служить примером рекурсии для лучшего понимания рекурсии, вчистую.
источник