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


Estoy tratando de envolver mi cabeza alrededor de los cierres de JavaScript.

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

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

¿Puede alguien mostrarme un uso real de un cierre?

Es éste, 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();
Author: Ram, 2010-04-28

20 answers

He usado cierres para hacer cosas como:

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

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

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

Es un ejemplo mínimo, pero tal vez usted puede ver usos a ella? Utilizamos esto para hacer cumplir los métodos públicos/privados.

 194
Author: Francisco Soto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-11 20:01:42

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

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

Ahora bien, podría haber muchos enfoques como:

1) Se 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 llamar updateClickCount().


2) Ahora, Usted podría estar pensando en declarar la variable dentro de la función:

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

Pero, ¡Oye! Cada vez que se llama a la función updateClickCount(), el contador se vuelve a poner en 1.


3) Pensando en Funciones anidadas?

Las funciones anidadas tienen acceso al ámbito "arriba" de ellas.
En este ejemplo, la función interna updateClickCount() tiene acceso a la variable contador en la función padre countWrapper()

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 llegar a la función updateClickCount() desde el exterior y también necesita encontrar una manera de ejecutar counter = 0 solo una vez, no cada vez.


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 auto invocación solo se ejecuta una vez. Establece el counter a cero (0), y devuelve una expresión de función.

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

Esto se llama Cierre de JavaScript. Hace posible que una función tenga variables" privadas".

El counter está protegido por el alcance de la función anónima, y solo se puede cambiar usando la función add!

Un ejemplo más vivo 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>
 90
Author: JerryGoyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-19 17:59:53

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

  1. Pasando 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 exotic flow control, como el manejo de eventos de jQuery y las API de AJAX.

 61
Author: Marcelo Cantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-11 21:13:42

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

El problema más común que veo en StackOverflow es cuando alguien quiere "retrasar" el uso de una variable esto se incrementa en cada bucle, pero debido a que la variable es de alcance, entonces cada referencia a la variable sería después de que el bucle haya terminado, lo que resulta en el 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 lugar a que cada alerta mostrara el mismo valor de i, el valor al que se aumentaba cuando terminaba el bucle. La solución es crear un nuevo cierre, un ámbito separado para la variable. Esto se puede hacer utilizando una función anónima ejecutada instantáneamente, que recibe la variable y almacena su estado como un 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); 
 18
Author: Andy E,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-28 09:51:01

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 a los usuarios buscar 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 matriz days podría simplemente almacenarse como una propiedad de la dateUtil pero luego sería visible para los usuarios del script e incluso podrían cambiarlo si lo desearan, 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.

 13
Author: maerics,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-15 15:36:27
 6
Author: alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-09 12:30:10

Otro uso común para closures es enlazar this en un método a un objeto específico, permitiendo 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);

Cuando se dispara un evento mousemove, se llama watcher.follow(evt).

Los cierres también son una parte esencial de las funciones de orden superior, lo que permite el patrón muy común de reescribir múltiples funciones similares como una sola función de orden superior mediante la parametrización de las porciones diferentes. Como un resumen ejemplo,

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

Se convierte en

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 cadenas).

Vea " Optimizando mi javascript con una función" para un ejemplo concreto.

 5
Author: outis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:07

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();
 5
Author: Luke Schlangen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-07 21:48:40

Si te sientes 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ás cerca de entender los cierres.

Piénsalo de esta manera: cuando instancias dos objetos Person, sabes que la variable miembro de la clase "Name" no se comparte entre las instancias; cada objeto tiene su propia 'copia'. Del mismo modo, cuando crea un cierre, la variable libre ('calledCount' 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 la función warnUser (aparte: eso es una función de orden superior) closure enlaza '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 diferentes valores al constructor de una clase.

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

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

 4
Author: EdwardGarson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-28 12:09:18

Sé que estoy súper tarde en responder a esta pregunta, pero podría ayudar a cualquiera que aún busque la respuesta en 2018.

Los cierres de Javascript se pueden utilizar para implementar throttley debounce funcionalidad en su aplicación.

Throttling :

La limitación pone un límite en el número máximo de veces que se puede llamar a 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)
    }
  }
}

Debouncing :

Debouncing pone un límite a que una función no sea llamada de nuevo hasta que haya pasado una cierta cantidad de tiempo sin que sea llamada. 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 características hermosas que cada aplicación web debería tener para proporcionar una funcionalidad de experiencia de interfaz de usuario fluida.

Espero que ayude a alguien.

 2
Author: Mohd Asim Suhail,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-02 09:06:58

Hace un tiempo escribí un artículo sobre cómo se pueden usar los cierres para simplificar el código de manejo de eventos. Se compara ASP.NET gestión de eventos en jQuery del lado del cliente.

Http://www.hackification.com/2009/02/20/closures-simplify-event-handling-code /

 1
Author: stusmith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-28 11:11:22

Me gusta la función factory de Mozilla ejemplo.

function makeAdder(x) {

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

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);
 1
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-10 14:35:04

El patrón del módulo JavaScript utiliza cierres. Su patrón agradable le permite tener algo similar vars" público "y" privado".

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 );

    }
  };

})();
 1
Author: Tomasz Grabowski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-12 11:30:15

Aquí tengo un ejemplo simple de concepto de cierre que podemos utilizar para nuestro sitio de comercio electrónico o 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 contador 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>
 1
Author: Abhilash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-01 15:00:12

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 completo a todas las variables y funciones definidas dentro de la función externa (y todas las demás variables y funciones a las que la función externa tiene acceso). 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. Un cierre se crea cuando la función interna de alguna manera se pone a disposición de cualquier ámbito fuera 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 name de la la función externa es accesible a 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 las funciones internas funcionen. Las funciones ni siquiera tienen que ser asignadas a una variable, o tener un nombre. lea aquí para más detalles

 0
Author: Sunny S.M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-10 07:49:27

Referencia: uso Práctico de los 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 el método sort de matrices que acepta como argumento la función sort-condition:

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

Mapeando funcionales como el método map de arrays que mapea un nuevo array por la condición del argumento funcional:

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

A Menudo es conveniente para implementar funciones de búsqueda con el uso de argumentos funcionales que definen condiciones casi ilimitadas para la búsqueda:

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

También, podemos notar la aplicación de funcionales 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

Una función se aplica a los argumentos (a una lista de argumentos - in apply, y a argumentos posicionados - in call):

(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 fin 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
 0
Author: Damodaran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-29 20:11:38

Gran parte del código que escribimos en front-end JavaScript está basado en eventos-definimos algún comportamiento, luego lo 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 sola 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 los botones (en este caso enlaces) como sigue:

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;

Fiddle

 0
Author: Usman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-03 12:17:33

Los cierres son una forma útil de crear generadores , 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:

Anonymous functions                                    Defined functions

Cannot be used as a method                             Can be used as a method of an object

Exists only in the scope in which it is defined        Exists within the object it is defined in

Can only be called in the scope in which it is defined Can be called at any point in the code

Can be reassigned a new value or deleted               Cannot be deleted or changed

Referencias

 0
Author: Paul Sweatte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-20 16:35:49

Este hilo me ha ayudado enormemente a obtener una mejor comprensión de cómo funcionan los cierres. Desde entonces, he hecho algunos experimentos 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 una manera práctica y cómo usar el cierre en diferentes niveles para mantener variables similares a variables estáticas y/o globales sin riesgo de que se sobrescriban o confundan con variables globales. Lo que esto hace es mantener un registro de botón clics, tanto a nivel local para cada botón individual como a nivel global, contando cada clic de botón, contribuyendo a una sola cifra. Tenga en cuenta que no he utilizado ninguna variable global para hacer esto, que es el punto del ejercicio - tener un controlador que se puede aplicar a cualquier botón que también contribuye a algo a nivel mundial.

Por favor, expertos, háganme saber si he cometido alguna mala práctica aquí! Todavía estoy aprendiendo estas cosas yo 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>
 0
Author: Darren Crabb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-24 14:16:45

En la muestra dada, el valor de la variable incluida 'counter' está protegido y solo se puede alterar usando 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
 0
Author: ShAkKiR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-21 07:57:50