costaricajs / Fundamentos-de-JavaScript

:seedling: Libro de Fundamentos de JavaScript
http://bit.ly/fundamentos-javascript
15 stars 8 forks source link

Agregando un poco al hilo de la historia de Closures #21

Closed josoroma-zz closed 9 years ago

josoroma-zz commented 9 years ago

Closure

Cuando una función utiliza una variable la cual fue declarada fuera de dicha función.

var imprimir = (function(){
  var h = 'hola';         /* Se define una variable */
  function imprimirH(){
    console.log(h);       /* <- Closure: Se conserva el acceso a la variable h */
  }
  return imprimirH;
})();

imprimir(); // =>  'hola'

El acceso a las variables fuera del ámbito léxico inmediato crea un closure/cierre. En otras palabras, se forma un closure cuando una función anidada se define dentro de otra función, permitiendo el acceso a las a las variables de las función externa. El hecho de retornar una función anidada permite mantener el acceso a las variables locales, argumentos y declaraciones de funciones internas de la función externa.

Esta encapsulación nos permite ocultar y preservar el contexto de ejecución de ámbitos externos, al mismo tiempo expone una interfaz pública que pueda ser compartida y reutilizada en el futuro.

function encapsulacion() {
  var _unaPropiedadPrivada = 'una propiedad privada';

  function _metodoPrivado(arg){
    return 'Soy un método privado alterando ' + arg;
  }

  return function metodoPublico() {
    return _metodoPrivado(_unaPropiedadPrivada);
  }
}

var obtenerVariable = encapsulacion();
obtenerVariable() // 'Soy un método privado alterando una propiedad privada'.

Uno de los tipos más populares de closures es el famoso patrón de módulo; el cual permite emular miembros públicos, privados y privilegiados:

El patrón de módulo se definió para proporcionar algo parecido a la encapsulación privada y pública de clases de la ingeniería convencional de sofware.

Relación entre Patrón de Módulo y Cierres

El patrón de módulo encapsula "privacidad", estado y organización utilizando cierres/closures. Proporciona una manera de envolver y mezclar variables y métodos públicos y privados también.

Interfaz

Proteje a ciertas piezas de filtrarse en el ámbito global y de chocar accidentalmente con alguna otra interfaz. Con el patrón de módulo, solamente se devuelve una API pública, manteniendo todo lo demás dentro del closure privado.

var Modulo = (function() {
  var _unaPropiedadPrivada = 'una propiedad privada';

  function _unMetodoPrivado(args) {
    console.log('_unMetodoPrivado => ', args);
  }

  // Retorna un objeto literal.
  return {
    unaPropiedadPubica: 'una propiedad pública',

    unMetodoPublico: function(args) {
      console.log('unMetodoPublicoPuro => ', args);
    },

    unMetodoPrivilegiado: function(args) {
      return _unMetodoPrivado(args);
    }
  };
})();

var config = {
  expandir: true
};

console.log(Modulo.unaPropiedadPubica); // una propiedad pública

Modulo.unMetodoPublico(config);         // unMetodoPublico =>  { expandir: true }

Modulo.unMetodoPrivilegiado(config);    // _unMetodoPrivado =>  { expandir: true }

Reutilización

Como vimos, el patrón de módulo expone una interfaz que otras partes de nuestra aplicación pueden utilizar. Este patrón es muy similar a una expresión funcional inmediatamente-invocada (IIFE), excepto que en nuestro devolvemos un objeto en lugar de una función.

¿Que permite () al final de la función?

El módulo actúa como si fuera un singleton, corre tan pronto como el compilador lo interpreta, por eso, la apertura y cierre de paréntesis al final de la función. Los únicos miembros disponibles fuera del contexto de ejecución del closure son sus métodos y propiedades ubicadas en el objeto público que se retorna. Sin embargo, todas las propiedades y métodos privados vivirán toda la vida de la aplicación mientras el contexto de ejecución se preserve, esto signifca que las variables pueden llegar a ser objeto de interacción via los métodos públicos.

IIFE - Expresión de Función Inmediatamente-Autoinvocada

Otro tipo de closure es IIFE, que no es más que una función anónima auto-invocado que corre en el contexto de window:

(function(window) {

  var _unaPropiedadPrivadaa;

  function _unMetodoPrivado() {
    console.log('_unMetodoPrivado');
  }

  window.Module = {

    public: function() {
      // do something
    }
  };

})(this);

Este tipo de expresión es útil cuando se trata de preservar el espacio de nombres global así como las variables declaradas dentro del cuerpo de la función serán locales al closure y viviran a lo largo de todo su tiempo de ejecución. Este es un tipo bastante popular de encapsulado de código fuente que usan mucho las aplicaciones y los frameworks también, por lo general se usa para exponer una sola interfaz global con la cual se pueda interactuar.

(function(window) {
  var _unaPropiedadPrivadaa;

  function _unMetodoPrivado(args) {
    console.log('_unMetodoPrivado => ', args);
  }

  window.Modulo = {

    unMetodoPublico: function(args) {
      console.log('unMetodoPublico => ', args);
    },

    unMetodoPrivilegiado: function(args) {
      _unMetodoPrivado(args);
    }
  };

  this.Modulo.unMetodoPublico('Inside IIFE');
  window.Modulo.unMetodoPublico('Inside IIFE');

  this.Modulo.unMetodoPrivilegiado('Inside IIFE');
  window.Modulo.unMetodoPrivilegiado('Inside IIFE');
})(this);

this.Modulo.unMetodoPublico('Outside IIFE');
window.Modulo.unMetodoPublico('Outside IIFE');

this.Modulo.unMetodoPrivilegiado('Outside IIFE');
window.Modulo.unMetodoPrivilegiado('Outside IIFE');

// unMetodoPublico  =>  Inside IIFE
// unMetodoPublico  =>  Inside IIFE
// _unMetodoPrivado =>  Inside IIFE
// _unMetodoPrivado =>  Inside IIFE
// unMetodoPublico  =>  Outside IIFE
// unMetodoPublico  =>  Outside IIFE
// _unMetodoPrivado =>  Outside IIFE
// _unMetodoPrivado =>  Outside IIFE
gaboesquivel commented 9 years ago

ver pull requests