2010-04-12 15 views
18

estaba escuchando la charla de Crockford en JavaScript y cierres estoy convencido de los beneficios de la ocultación de información, pero no tienen un firme entendimiento de cuándo utilizar las funciones de devolución de llamada.¿Cuáles son los casos de uso para las funciones de cierre/devolución de llamada en JavaScript?

Es sobre todo una verdadera declaración de que una persona puede lograr la misma funcionalidad con o sin devoluciones de llamada.

Como alguien que está escribiendo código, lo heurística o señales debo tener en cuenta al determinar cuándo utilizar devoluciones de llamada/cierres?

No estoy buscando para la declaración general de 'cierres hacer el código más seguro', en lugar de una lista de ejemplos o reglas generales prácticos para cuando las devoluciones de llamada son la idea correcta.

Presentación de Crockford: http://www.yuiblog.com/blog/2010/04/08/video-crockonjs-5/

Respuesta

29

En primer lugar:

  • Devolución de llamada: una función que se pasa como argumento a otra función, generalmente llamada como resultado de un evento.
  • Cierre: Alcance retenido. Es decir. el concepto de que cuando se declara una función dentro de otra función, el alcance de la función externa es accesible dentro de la función interna.

Las devoluciones de llamada también pueden ser cierres, pero no siempre.

Esta es una devolución de llamada:

someProcess(myCallback); 

function myCallback() { 
    alert('Done...'); 
} 

function someProcess(callback) { 
    // does stuff... 
    // ... 
    callback(); 
} 

Un cierre:

function foo(msg) { 

    function bar() { 
     // I can access foo's scope 
     // (i.e. bar can access everything that foo can access) 
     alert(msg); 
    } 

    return bar; 

} 

foo('hello')(); // alerts "hello" 

Un uso común de los cierres es proporcionar información a la clandestinidad, lo cual es útil en traer algún tipo de encapsulación para el idioma . Eche un vistazo al the module pattern para ver esto en acción.

Otro uso común es cuando la unión controladores de eventos a los elementos. P.ej.

var myElements = [ /* DOM Collection */ ]; 

for (var i = 0; i < 100; ++i) { 
    myElements[i].onclick = function() { 
     alert('You clicked on: ' + i); 
    }; 
} 

Eso no funcionaría. Para cuando se hace clic en el elemento, la variable i es 99.Para que esto funcione correctamente nos frío utilizar un cierre para capturar el valor de i:

function getHandler(n) { 
    return function() { 
     alert('You clicked on: ' + n); 
    }; 
} 

for (var i = 0; i < 100; ++i) { 
    myElements[i].onclick = getHandler(i); 
} 
+0

contador ejemplo es clásico :). – Anshul

5

This writeup from Mozilla may answer why use closures and when

También, see this set of examples (especially "What can be done with Closures?" section that has the following exmples):

  • Ejemplo 1: setTimeout con función de Referencias
  • Ejemplo 2: Asociar funciones con objeto Métodos de instancia
  • Ejemplo 3: rel encapsulante Funcionalidad ado

tengo e sensación de que este se puede remontar a Crockford, pero el uso clásico de cierres es emular instancia privada o variables estáticas (que carece de JavaScipt)

12

Digamos que usted desea una función que se puede utilizar para devolver un valor único "id" para usar al crear nuevos elementos DOM . Ahora, en algo como Java, podrías crear una clase con un contador privado interno, y luego tener un método que agregue el contador a alguna cadena de prefijo. Bueno, en Javascript:

var getId = (function() { 
    var counter = 0; 
    return function() { 
    return "prefix" + counter++; 
    }; 
})(); 

Ahora la variable "getId" está destinada a una función que es creado por otra función, y creó de tal manera que tiene una variable persistente para utilizar entre invocaciones. Del mismo modo, si quería tener una familia de funciones "getId" (digamos, una para cada tipo de elemento DOM podría añadir), que podría hacer esto:

var getIdFunc = function(prefix) { 
    var counter = 0; 
    return function() { 
    return prefix + counter++; 
    }; 
}; 
var getId = { 
    'div': getIdFunc('div'), 
    'span': getIdFunc('span'), 
    'dl': getIdFunc('dl'), 
    // ... 
}; 

Ahora puede llamar getId.div() para obtener una nueva valor "id" para un nuevo <div>. La función se creó llamando a una función que proporciona dos valores escondidos en un cierre: la cadena de prefijo (pasada como un argumento) y el contador (un var declarado en el ámbito de cierre).

Una vez que se acostumbre, la instalación es tan flexible y útil que sentirá dolor al regresar a un entorno sin ella.

Ah, y he aquí un consejo para ayudarle a mantenerse fuera Stackoverflow debe probar esto: se trata de un problema que aparece todo el tiempo:

for (var i = 0; i < 10; ++i) { 
    var id = "foo" + i; 
    var element = document.getElementById(id); 
    element.onclick = function() { 
    alert("hello from element " + i); 
    }; 
} 

Cuál es el problema aquí? Bueno, esa variable "i" a la que hace referencia esa función es la "i" del alcance en el que se ejecuta ese bucle. Esa variable, notarás, se incrementa a través del ciclo (duhh, ¿verdad?). Bueno, cada una de esas pequeñas funciones creadas y asignadas como manejadores de eventos será que comparta con la misma variable única "i" en el ámbito de cierre. Oops! La solución es hacer algo como esto:

for (var i = 0; i < 10; ++i) { 
    var id = "foo" + i; 
    var element = document.getElementById(id); 
    element.onclick = (function(iCopy) { 
    return function() { 
     alert("hello from element " + iCopy); 
    }; 
    })(i); 
} 

Hacemos una copia del exterior "i" en un ámbito de cierre de su cuenta, por lo que ahora cada controlador de eventos tiene su propio!

En resumen: la técnica de aprovechar cierres surge todo el tiempo maldito una vez que te acostumbras. No es un boleto gratis a un nuevo país de las maravillas de la programación libre de errores; no me malinterpretes Sin embargo, es un paradigma muy útil y flexible.

Cuestiones relacionadas