¿Cuál es un uso práctico para un cierre en JavaScript?

280

Estoy haciendo todo lo posible para comprender los cierres de JavaScript.

Entiendo que al devolver una función interna, tendrá acceso a cualquier variable definida en su elemento primario inmediato.

¿Dónde me sería útil? Tal vez todavía no lo he entendido. La mayoría de los ejemplos que he visto en línea no proporcionan ningún código del mundo real, solo ejemplos vagos.

¿Alguien puede mostrarme un uso real de un cierre?

¿Es este, por ejemplo?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();
alex
fuente
17
+1 por esforzarte al máximo :-) Para empezar, los cierres pueden parecer realmente desalentadores, sé que fueron para mí. Una vez que los domines, instantáneamente serás un codificador mucho mejor.
Andy E
77
Acabo de escribir una publicación de blog sobre cierres en JavaScript que puede encontrar helfpul.
Skilldrick
@Skilldrick. el enlace está muerto ... y también encontré este ejemplo práctico muy útil. youtube.com/watch?v=w1s9PgtEoJs .
Abhi

Respuestas:

241

He usado cierres para hacer cosas como:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

Como puede ver allí, aahora es un objeto, con un método publicfunction( a.publicfunction()) que llama privatefunction, que solo existe dentro del cierre. NO puede llamar privatefunctiondirectamente (es decir a.privatefunction()), solo publicfunction().

Es un ejemplo mínimo, pero ¿tal vez puedas ver sus usos? Usamos esto para imponer métodos públicos / privados.

Francisco Soto
fuente
26
¡Ah, si esto es un cierre, entonces he usado cierres sin saberlo! A menudo pongo funciones dentro de otra como esa, y luego expongo cualquiera que necesite público devolviendo un objeto literal como en su ejemplo.
alex
1
Sí, como ve, mantiene el contexto de la función porque el objeto que devuelve hace referencia a variables (y funciones) dentro de ella. Entonces los has estado usando, simplemente no lo sabías.
Francisco Soto
9
Técnicamente, cada función que realiza en Javascript en un navegador es un cierre porque el objeto de la ventana está vinculado a él.
Adam Gent
9
Sé que esta es una vieja pregunta, pero para mí esto todavía no proporciona una respuesta adecuada. ¿Por qué no simplemente llamar a la función directamente? ¿Por qué necesitas una función privada?
qodeninja 03 de
55
Porque aunque el ejemplo solo tiene una función, también podría tener variables que no son accesibles desde el exterior. Diga: var obj = (function () {var value = 0; return {get: function () {return value;}, set: function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 etc.
Francisco Soto
212

Supongamos que desea contar la cantidad de veces que el usuario hizo clic en un botón en una página web.
Para esto, está activando una función en onclickcaso de botón para actualizar el recuento de la variable

<button onclick="updateClickCount()">click me</button>  

Ahora podría haber muchos enfoques como:

1) Podría usar una variable global y una función para aumentar el contador :

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

Pero, la trampa es que cualquier script en la página puede cambiar el contador, sin llamarupdateClickCount() .


2) Ahora, puede estar pensando en declarar la variable dentro de la función:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

¡Pero hey! Cada vez updateClickCount()que se llama a la función, el contador se establece nuevamente en 1.


3) ¿Pensando en funciones anidadas ?

Las funciones anidadas tienen acceso al alcance "encima" de ellas.
En este ejemplo, la función interna updateClickCount()tiene acceso a la variable de contador en la función primariacountWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

Esto podría haber resuelto el dilema del contador, si pudiera alcanzar la updateClickCount()función desde el exterior y también necesita encontrar una manera de ejecutar counter = 0solo una vez, no siempre.


4) ¡ Cierre al rescate! (función de auto invocación) :

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

La función de invocación automática solo se ejecuta una vez. Establece el countercero (0) y devuelve una expresión de función.

De esta manera se updateClickCountconvierte en una función. La parte "maravillosa" es que puede acceder al contador en el ámbito principal.

Esto se llama cierre de JavaScript . Permite que una función tenga variables " privadas ".

¡ counterEstá protegido por el alcance de la función anónima y solo se puede cambiar con la función de agregar!

Ejemplo más animado sobre el cierre:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


Referencia: https://www.w3schools.com/js/js_function_closures.asp

JerryGoyal
fuente
50
Esta es la primera respuesta que me hizo decir "Oh, eso es por qué me gustaría utilizar cierres!"
Abogado del Diablo
66
me
alegraste el
15
Acabo de leer la página de w3schools sobre cierres y luego vine aquí para obtener más información. Esto es lo mismo que la página de w3schools
tyelford
1
@JerryGoyal, ¿podría hacer que funcione con 2 botones separados? No puedo entender cómo sin recurrir a 2 variables (copias de la función), lo que parece eliminar algunos de los principales beneficios / conveniencia.
Tyler Collier
2
Buena respuesta. Sin embargo, tenga en cuenta que un cierre no necesita ser una función de invocación automática, pero puede serlo. Cuando un cierre se invoca automáticamente (es decir, se invoca inmediatamente agregando () después de la función), esto significa que el valor de retorno se calcula de inmediato, en lugar de que la función se devuelva y el valor de retorno se calcule más tarde una vez que se invoca la función. Un cierre en realidad puede ser cualquier función dentro de otra función, y su característica clave es que tiene acceso al alcance de la función principal, incluidas sus variables y métodos.
Chris Halcrow
69

El ejemplo que das es excelente. Los cierres son un mecanismo de abstracción que le permite separar las preocupaciones de manera muy limpia. Su ejemplo es un caso de separación de instrumentación (conteo de llamadas) de semántica (una API de informe de errores). Otros usos incluyen:

  1. Pasar el comportamiento parametrizado a un algoritmo (programación clásica de orden superior):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
  2. Simulación de programación orientada a objetos:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
  3. Implementación de control de flujo exótico, como el manejo de eventos de jQuery y las API AJAX.

Marcelo Cantos
fuente
3
( int?) La última vez que lo comprobé, JavaScript era un lenguaje de tipo pato. ¿Quizás estabas pensando en Java?
Hola71 el
1
@ Hello71: Estaba pensando en JavaScript, pero los viejos hábitos son difíciles. Buena atrapada.
Marcelo Cantos
2
@MarceloCantos parece que has olvidado una coma en la implementación del contador. Edité tu publicación para corregirla. Espero que esté bien :)
Natan Streppel
2
@Streppel: ¡Buena captura! Estoy más que feliz de que mejore mi código. :-)
Marcelo Cantos
tratando de entender el n. ° 1 ... ¿Cómo llamarías la proximidad_sort?
Dave2081
26

Sé que llegué muy tarde al responder esta pregunta, pero podría ayudar a cualquiera que todavía esté buscando la respuesta en 2018.

Los cierres de Javascript se pueden usar para implementar la funcionalidad de aceleración y antirrebote en su aplicación.

Estrangulamiento :

La limitación establece un límite como la cantidad máxima de veces que se puede invocar una función a lo largo del tiempo. Como en "ejecutar esta función como máximo una vez cada 100 milisegundos".

Código:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

Rebote :

El rebote pone un límite a una función que no se volverá a llamar hasta que haya transcurrido una cierta cantidad de tiempo sin que se llame. Como en "ejecutar esta función solo si han pasado 100 milisegundos sin que se llame".

Código:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

Como puede ver, los cierres ayudaron a implementar dos hermosas características que cada aplicación web debería tener para proporcionar una funcionalidad de experiencia de interfaz de usuario fluida.

Espero que ayude a alguien.

Mohd Asim Suhail
fuente
18

Sí, ese es un buen ejemplo de un cierre útil. La llamada a warnUser crea la calledCountvariable en su alcance y devuelve una función anónima que se almacena en la warnForTampervariable. Debido a que todavía hay un cierre que hace uso de la variable calledCount, no se elimina al salir de la función, por lo que cada llamada al warnForTamper()aumentará la variable de ámbito y alertará el valor.

El problema más común que veo en StackOverflow es cuando alguien quiere "retrasar" el uso de una variable que se incrementa en cada ciclo, pero debido a que la variable tiene un alcance, cada referencia a la variable sería después de que el ciclo haya terminado, lo que resulta en estado final de la variable:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

Esto daría como resultado que cada alerta muestre el mismo valor de i, el valor al que se incrementó cuando finalizó el ciclo. La solución es crear un nuevo cierre, un alcance separado para la variable. Esto se puede hacer usando una función anónima ejecutada instantáneamente, que recibe la variable y almacena su estado como argumento:

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 
Andy E
fuente
Interesante -1, supongo que esto no es "un uso práctico para un cierre en JavaScript"?
Andy E
1
Encontré algo de utilidad al leerlo, así que otorgué un +1 antes del voto negativo.
alex
1
@alex: gracias, noté el voto a favor. Casi me he acostumbrado a votos negativos anónimos aquí en SO. Solo me molesta porque realmente me gustaría saber si he dicho algo inexacto o incorrecto, y tienden a hacerte pensar que has sido rechazado por otro contestador que quiere una mejor visibilidad para su propia respuesta. Afortunadamente, no soy del tipo vengativo ;-)
Andy E
1
Creo que esto es más una solución para el alcance de bloque roto de JavaScripts. Debería poder agregar var j = i; antes del primer setTimeout y recibe la alerta para usar ese j. Otra solución es usar 'with' así: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert ("Valor de estaba "+ i +" cuando se configuró este temporizador ")}, 100);}}
davidbuttar
1
@AndyE Funny podría no ser la palabra correcta. Acabo de notar que a menudo las personas usan funciones de auto-invocación para explicar los cierres, como muchas respuestas en esta página. Pero la función de devolución de llamada en setTimeout también es un cierre; podría considerarse "un uso práctico" ya que puede acceder a algunas otras variables locales desde la devolución de llamada. Cuando estaba aprendiendo acerca de los cierres, me di cuenta de que esto me era útil: que los cierres están en todas partes, no solo en los patrones JavaScript de arcade.
antoine
14

En el lenguaje JavaScript (o cualquier ECMAScript), en particular, los cierres son útiles para ocultar la implementación de la funcionalidad y al mismo tiempo revelar la interfaz.

Por ejemplo, imagine que está escribiendo una clase de métodos de utilidad de fecha y desea permitir que los usuarios busquen nombres de días de la semana por índice, pero no desea que puedan modificar la matriz de nombres que usa debajo del capó.

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

Tenga en cuenta que la daysmatriz podría simplemente almacenarse como una propiedad del dateUtilobjeto, pero luego sería visible para los usuarios de la secuencia de comandos e incluso podrían cambiarla si quisieran, sin siquiera necesitar su código fuente. Sin embargo, dado que está encerrado por la función anónima que devuelve la función de búsqueda de fecha, solo es accesible por la función de búsqueda, por lo que ahora es a prueba de manipulaciones.

maerics
fuente
2
Esto puede sonar tonto, pero ¿no podrían simplemente abrir el archivo JavaScript y ver su implementación?
itsmichaelwang
1
@Zapurdead: sí, por supuesto, podían ver la implementación, pero no podían cambiar la implementación (accidental o intencionalmente) sin modificar directamente su código fuente. Supongo que podría compararlo con miembros protegidos en Java.
Maerics
6

Hay una sección sobre cierres prácticos en la red de desarrolladores de Mozilla .

alex
fuente
Mirando a través de esto, no veo cómo es "práctico" como si elimino todo return function ()...el código todavía funciona bien. El cierre no es una guarida
una tijereta el
@James_Parsons No podría asignarlos a los controladores de eventos como lo hicieron en el ejemplo entonces.
alex
5

Otro uso común para los cierres es enlazar thisun método a un objeto específico, lo que permite que se llame a otro lugar (como un controlador de eventos).

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

Cada vez que se dispara un evento mousemove, watcher.follow(evt)se llama.

Los cierres también son una parte esencial de las funciones de orden superior, ya que permiten el patrón muy común de reescribir múltiples funciones similares como una sola función de orden superior al parametrizar las porciones diferentes. Como un ejemplo abstracto,

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

se convierte

fooer = function (x) {
    return function (...) {A x B}
}

donde A y B no son unidades sintácticas sino cadenas de código fuente (no literales de cadena).

Consulte " Simplificar mi javascript con una función " para ver un ejemplo concreto.

outis
fuente
5

Aquí, tengo un saludo que quiero decir varias veces. Si creo un cierre, simplemente puedo llamar a esa función para grabar el saludo. Si no creo el cierre, tengo que pasar mi nombre cada vez.

Sin cierre ( https://jsfiddle.net/lukeschlangen/pw61qrow/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

Con un cierre ( https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();
Luke Schlangen
fuente
1
No estoy seguro, pero aún sin un cierre puede llamar como var grretBilly = saludo ("Billy", "Bob"); y llame a grretBilly (); ¿Todavía haría lo mismo? aunque creas un cierre o no, ese es un problema diferente, pero pasar el nombre cada vez no es un problema aquí.
user2906608
4

Si se siente cómodo con el concepto de instanciar una clase en el sentido orientado a objetos (es decir, crear un objeto de esa clase), entonces está cerca de comprender los cierres.

Piénselo de esta manera: cuando crea una instancia de dos objetos Persona, sabe que la variable de miembro de la clase "Nombre" no se comparte entre las instancias; cada objeto tiene su propia 'copia'. De manera similar, cuando crea un cierre, la variable libre ('llamadaCount' en su ejemplo anterior) está vinculada a la 'instancia' de la función.

Creo que su salto conceptual se ve ligeramente obstaculizado por el hecho de que cada función / cierre devuelto por el cierre de la función warnUser (aparte: esa es una función de orden superior ) se une a 'calledCount' con el mismo valor inicial (0), mientras que a menudo al crear cierres es más útil pasar diferentes inicializadores a la función de orden superior, al igual que pasar valores diferentes al constructor de una clase.

Entonces, suponga que cuando 'calledCount' alcanza un cierto valor que desea finalizar la sesión del usuario; es posible que desee valores diferentes para eso, dependiendo de si la solicitud proviene de la red local o del gran Internet malo (sí, es un ejemplo artificial). Para lograr esto, puede pasar diferentes valores iniciales para calledCount a warnUser (es decir, -3 o 0?).

Parte del problema con la literatura es la nomenclatura utilizada para describirlos ("alcance léxico", "variables libres"). No dejes que te engañe, los cierres son más simples de lo que parece ... prima facie ;-)

EdwardGarson
fuente
3

Aquí tengo un ejemplo simple del concepto de cierre que podemos usar en nuestro sitio de comercio electrónico o en muchos otros también. Estoy agregando mi enlace jsfiddle con el ejemplo. contiene una pequeña lista de productos de 3 artículos y un mostrador de carrito.

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>

Abhilash
fuente
2

Uso de cierres:

Los cierres son una de las características más potentes de JavaScript. JavaScript permite el anidamiento de funciones y otorga a la función interna acceso total a todas las variables y funciones definidas dentro de la función externa (y a todas las demás variables y funciones a las que tiene acceso la función externa). Sin embargo, la función externa no tiene acceso a las variables y funciones definidas dentro de la función interna. Esto proporciona una especie de seguridad para las variables de la función interna. Además, dado que la función interna tiene acceso al alcance de la función externa, las variables y funciones definidas en la función externa vivirán más tiempo que la función externa en sí, si la función interna logra sobrevivir más allá de la vida de la función externa.

Ejemplo:

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

En el código anterior, la variable de nombre de la función externa es accesible para las funciones internas, y no hay otra forma de acceder a las variables internas, excepto a través de las funciones internas. Las variables internas de la función interna actúan como almacenes seguros para las funciones internas. Contienen datos "persistentes", pero seguros, para que funcionen las funciones internas. Las funciones ni siquiera tienen que asignarse a una variable, o tener un nombre. lea aquí para más detalles

Sunny SM
fuente
2

Me gusta el ejemplo de fábrica de funciones de Mozilla .

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);
Tom
fuente
11
Este es el tipo de ejemplo que, en mi opinión, no ayuda a las personas a comprender los cierres o para qué sirven. ¿Cuántas veces has escrito un cierre para devolver una función para agregar números, aparte de como ejemplo?
Mohamad el
2

El patrón del módulo de JavaScript utiliza cierres. Su bonito patrón le permite tener algo parecido a vars "públicos" y "privados".

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();
Tomasz Grabowski
fuente
1

Este hilo me ha ayudado enormemente a comprender mejor cómo funcionan los cierres. Desde entonces, he experimentado un poco por mi cuenta y se me ocurrió este código bastante simple que puede ayudar a otras personas a ver cómo los cierres se pueden usar de manera práctica y cómo usar el cierre en diferentes niveles para mantener variables similares a la estática y / o variables globales sin riesgo de sobrescribirlas o confundirlas con variables globales. Lo que esto hace es realizar un seguimiento de los clics de los botones, tanto a nivel local para cada botón individual como a nivel global, contando cada clic de botón, contribuyendo a una sola figura. Tenga en cuenta que no he usado ninguna variable global para hacer esto, lo cual es una especie de objetivo del ejercicio: tener un controlador que se pueda aplicar a cualquier botón que también contribuya a algo globalmente.

Por favor, expertos, ¡háganme saber si he cometido alguna mala práctica aquí! Todavía estoy aprendiendo esto por mí mismo.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>
Darren Crabb
fuente
0

Referencia: uso práctico de cierres

En la práctica, los cierres pueden crear diseños elegantes, lo que permite la personalización de varios cálculos, llamadas diferidas, devoluciones de llamada, creación de alcance encapsulado, etc.

Un ejemplo del método de clasificación de matrices que acepta como argumento la función de condición de clasificación:

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

Mapeo funcional como el método de mapeo de matrices que mapea una nueva matriz por la condición del argumento funcional:

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

A menudo es conveniente implementar funciones de búsqueda utilizando argumentos funcionales que definen condiciones casi ilimitadas para la búsqueda:

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

Además, podemos observar la aplicación de funciones como, por ejemplo, un método forEach que aplica una función a una matriz de elementos:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

Se aplica una función a los argumentos (a una lista de argumentos - en apply, y a argumentos posicionados - en llamada):

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

Llamadas diferidas:

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

Funciones de devolución de llamada:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

Creación de un alcance encapsulado con el propósito de ocultar objetos auxiliares:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10
Damodaran
fuente
0

Gran parte del código que escribimos en el front-end JavaScript está basado en eventos: definimos algunos comportamientos y luego los adjuntamos a un evento que es activado por el usuario (como un clic o una pulsación de tecla). Nuestro código generalmente se adjunta como una devolución de llamada: una única función que se ejecuta en respuesta al evento. size12, size14 y size16 ahora son funciones que redimensionarán el texto del cuerpo a 12, 14 y 16 píxeles, respectivamente. Podemos adjuntarlos a botones (en este caso enlaces) de la siguiente manera:

function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

Violín

Muhammad Usman
fuente
Si bien este código puede responder la pregunta, proporcionar un contexto adicional con respecto a cómo y / o por qué resuelve el problema mejoraría el valor a largo plazo de la respuesta.
Donald Duck
1
Este ejemplo, me parece, podría implementarse sin cierre a través de una función estándar. Estoy tratando de encontrar un ejemplo de algo que NO PODRÍA implementarse sin cierre
Zach Smith
0

Los cierres son una forma útil de crear , una secuencia incrementada bajo demanda:

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

Las diferencias se resumen de la siguiente manera:

Funciones anónimas Funciones definidas

No se puede usar como método Se puede usar como método de un objeto

Existe solo en el ámbito en el que se define Existe dentro del objeto en el que se define

Solo se puede invocar en el ámbito en el que se define Se puede invocar en cualquier punto del código

Se puede reasignar un nuevo valor o eliminar No se puede eliminar o cambiar

Referencias

Paul Sweatte
fuente
0

En la muestra dada, el valor de la variable adjunta 'contador' está protegido y solo puede modificarse utilizando las funciones dadas (incremento, decremento). porque está en un cierre,

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2

ShAkKiR
fuente
0

Explicando el uso práctico de un Cierre en JavaScript ---

Cuando creamos una función dentro de otra función, estamos creando un cierre. Los cierres son poderosos porque son capaces de leer y manipular los datos de sus funciones externas. Cada vez que se invoca una función, se crea un nuevo ámbito para esa llamada. La variable local declarada dentro de la función pertenece a ese ámbito y solo se puede acceder desde esa función. Cuando la función ha finalizado la ejecución, el alcance generalmente se destruye.

Un ejemplo simple de tal función es este:

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

En el ejemplo anterior, la función buildName () declara un saludo de variable local y lo devuelve. Cada llamada a una función crea un nuevo ámbito con una nueva variable local. Una vez que la función termina de ejecutarse, no tenemos forma de volver a referirnos a ese alcance, por lo que se recolecta basura.

Pero, ¿qué tal cuando tenemos un enlace a ese alcance?

Veamos la siguiente función:

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

La función sayName () de este ejemplo es un cierre. La función sayName () tiene su propio ámbito local (con variable de bienvenida) y también tiene acceso al ámbito de la función externa (que lo encierra). En este caso, el saludo variable de buildName ().

Una vez realizada la ejecución de buildName, el alcance no se destruye en este caso. La función sayMyName () todavía tiene acceso a ella, por lo que no se recolectará basura. Sin embargo, no hay otra forma de acceder a los datos desde el ámbito externo, excepto el cierre. El cierre sirve como la puerta de entrada entre el contexto global y el alcance externo.

Mandeep Kaur
fuente
0

Estoy tratando de aprender clousures y creo que el ejemplo que he creado es un caso de uso práctico. Puede ejecutar el fragmento y ver el resultado en la consola. Tenemos dos usuarios separados que tienen datos separados. Cada uno de ellos puede ver el estado real y actualizarlo.

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

Krystian Wójcicki
fuente