Funciones que devuelven funciones

Si no sabes lo que es el ámbito de una variable, no sigas leyendo.

¿Porqué son necesarias?

Un caso típico es cuando una función espera como parámetro otra función (callback), por ejemplo para gestionar un evento, pero tu quieres que se ejecute un método de un objeto (de una instancia en concreto).

Esto es lo que NO puedes hacer:


var oFool = {}
oFool.value = 5;
oFool.show = function () { alert(this.value) };
setTimeout(oFool.show, 100);

En este caso la función setTimeout está recibiendo como función callback el código de la función que actúa como método en el objeto, pero fuera del contexto del objeto, por eso el alert mostrará undefined.

Para este caso, la solución puede ser una función anónima:


var oFool = {};
oFool.value = 5;
oFool.show = function () { alert(this.value) };
setTimeout(function () {
oFool.show();
}, 100);

Esta función anónima está asociada al mismo contexto que el código en el cual está la función setTimeout (que no es necesariamente el contexto global) y por lo tanto puede acceder a la instancia oFool.

¿Cuándo hace falta una función que devuelve otra función o cuándo no es suficiente una función anónima?

La función anónima no es suficiente cuando ese contexto al que queda asociada es cambiante, por ejemplo dentro de un bucle, porque en ese caso se quedará con el último valor del iterador.


var aLayers = document.getElementsByName("div");
for(k in aLayers) {
var oLayer = aLayers[k];
oLayer.onclick = function () {
alert("div #"+k);
};
}

En este caso, todas la función asignada a cada capa queda asociada al mismo contexto, por lo tanto la variable k tendrá el mismo valor para todas.

Una solución, que será válida para casos más complejos sería:


var aLayers = document.getElementsByName("div");
for(k in aLayers) {
var oLayer = aLayers[k];
oLayer.num = k;
oLayer.onclick = function () {
alert("div #"+this.num);
};
}

Porque en este caso, el valor de la variable k queda almacenado en el contexto de cada capa y la función asignada hace uso de ese contexto (mediante this).

Pero aquí hemos venido a mostrar la utilidad de una función que devuelve otra función, así que veamos una solución que hace uso de esta técnica:


var aLayers = document.getElementsByName("div");
for(k in aLayers) {
var oLayer = aLayers[k];
var fClick = function (p) {
return function () {
alert(p);
};
};
oLayer.onclick = fClick(k);
}

Lo que sucede aquí es que la función fClick recibe un parámetro p que pasa a formar parte de su ámbito y devuelve una función anónima que hace uso de este parámetro. Cuando se asocia fClick(k) al evento onclick, el valor de k queda asociado al ámbito de la función anónima que se genera en ese momento y no varía en la siguiente interacción, en la cual se creará una nueva función anónima con un nuevo ámbito que tendrá un valor diferente de k en su variable p.