una - recibir valor en funcion javascript
Llamar a una funciĆ³n de javascript recursivamente (5)
Puedo crear una función recursiva en una variable como esta:
/* Count down to 0 recursively.
*/
var functionHolder = function (counter) {
output(counter);
if (counter > 0) {
functionHolder(counter-1);
}
}
Con esto, functionHolder(3);
produciría 3
2
1
0
. Digamos que hice lo siguiente:
var copyFunction = functionHolder;
copyFunction(3);
produciría 3
2
1
0
como arriba. Si luego cambié functionHolder
siguiente manera:
functionHolder = function(whatever) {
output("Stop counting!");
Luego functionHolder(3);
daría Stop counting!
, como se esperaba.
copyFunction(3);
ahora da 3
Stop counting!
ya que se refiere a functionHolder
, no a la función (que a su vez apunta). Esto podría ser deseable en algunas circunstancias, pero ¿hay alguna forma de escribir la función para que se llame a sí misma y no a la variable que la contiene?
Es decir, ¿es posible cambiar solo la línea functionHolder(counter-1);
de modo que seguir todos estos pasos todavía da 3
2
1
0
cuando llamamos a copyFunction(3);
? Intenté this(counter-1);
pero eso me da el error de que this is not a function
.
Aquí hay un ejemplo muy simple:
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);
Tenga en cuenta que el counter
cuenta "hacia atrás" en relación con el valor de slug
. Esto se debe a la posición en la que estamos registrando estos valores, ya que la función se repite antes de iniciar sesión, por lo que, esencialmente, seguimos anidando más y más en la pila de llamadas antes de que se inicie el registro.
Una vez que la recursión se encuentra con el elemento final de la pila de llamadas, trampolines "fuera" de las llamadas a funciones, mientras que, el primer incremento del counter
produce dentro de la última llamada anidada.
Sé que esto no es una "solución" para el código del Interlocutor, pero dado el título, pensé que genéricamente ejemplificaría la Recursividad para una mejor comprensión de la recursión, directamente.
Uso de expresiones de funciones con nombre:
Puede darle a una expresión de función un nombre que sea realmente privado y solo sea visible desde el interior de la función ifself:
var factorial = function myself (n) {
if (n <= 1) {
return 1;
}
return n * myself(n-1);
}
typeof myself === ''undefined''
Aquí myself
solo myself
visible solo dentro de la función misma.
Puede usar este nombre privado para llamar a la función recursivamente.
Consulte 13. Function Definition
de la 13. Function Definition
de la especificación ECMAScript 5:
El identificador en una FunctionExpression se puede referenciar desde el FunctionBody de FunctionExpression para permitir que la función se llame recursivamente. Sin embargo, a diferencia de en una declaración de función, el identificador en una expresión de función no se puede referenciar y no afecta el alcance que encierra la expresión de función.
Tenga en cuenta que Internet Explorer hasta la versión 8 no se comporta correctamente ya que el nombre es realmente visible en el entorno de variables adjuntas, y hace referencia a un duplicado de la función real (consulte el comentario de patrick dw a continuación).
Usando arguments.callee:
Alternativamente, puede usar arguments.callee
para referirse a la función actual:
var factorial = function (n) {
if (n <= 1) {
return 1;
}
return n * arguments.callee(n-1);
}
La quinta edición de ECMAScript prohíbe el uso de arguments.callee () en modo estricto , sin embargo:
(Desde MDN ): en el código normal arguments.callee se refiere a la función adjunta. Este caso de uso es débil: ¡simplemente nombra la función adjunta! Además, arguments.callee dificulta considerablemente las optimizaciones, como las funciones en línea, porque debe ser posible proporcionar una referencia a la función no en línea si se accede a arguments.callee. arguments.callee para funciones de modo estrictas es una propiedad no eliminable que se lanza cuando se establece o recupera.
Puede acceder a la función usando arguments.callee :
if (counter>0) {
arguments.callee(counter-1);
}
Sin embargo, esto se romperá en modo estricto.
Puedes usar el Y-combinator: ( Wikipedia )
// 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)));
Y puedes usarlo así:
// 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);
}
});
Sé que esta es una vieja pregunta, pero pensé que presentaría una solución más que podría usarse si desea evitar el uso de expresiones de función nombradas. (No dice que debe o no debe evitarlos, simplemente presentando otra solución)
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);