domingo, 4 de diciembre de 2011

Namespaces en javascript (2 de 2)

Siguiendo con la creación de objetos y los namespaces en javascript, ahora vamos otra forma de inicializar objetos. Está vez vamos a declarar un entorno y lo vamos a pasar como argumento de una función autoejecutable. El siguiente código nos muestra como hacerlo
var myContext = {};
(function (context) {
    var foo = 'Foo';
    var bar = 'Bar';

    // Private methods
    function getFoo () {        
        return foo;
    }

    function getBar () {
        return bar;
    }

    // Public methods
    context.getMessage = function () {
        return getFoo() + ' ' + getBar();        
    }

})(myContext);

console.log(myContext.getMessage());
console.log(myContext.getFoo()); // Uncaught TypeError: Object #<Object> has no method 'getFoo'
console.log(myContext.getBar()); // Uncaught TypeError: Object #<Object> has no method 'getBar'
Vemos como las variables 'foo' y 'bar' así como los método 'getFoo' y 'getBar' permanecen privados, por lo que solo tienen visibilidad dentro de su contexto. Lo métodos asignados al objeto pasado como parámetro (context), se podrán acceder de manera pública. Como nota, cabe destacar que este es el patrón que sigue jQuery.

This Namespace Proxy

Al leer sobre patrones y como declarar objetos en javascript unos de los patrones que más fama tiene es el patrón This Namespace Proxy (mejor no hacer traducciones) del desarrollador James Edwards. El patrón utiliza los comando apply y call. Para muchos este el patrón más elegante que hay en javascript. Veamos un ejemplo
var myContext = {};
(function () {
    var foo = 'Foo';
    var bar = 'Bar';

    // Private methods
    function getFoo() {        
        return foo;
    }

    function getBar() {
        return bar;
    }

    // Public Methods
    this.getMessage = function () {
        return getFoo() + ' ' + getBar();
    };

}).apply(myContext);

console.log(myContext.getMessage()); // Foo Bar
console.log(myContext.getFoo()); // Uncaught TypeError: Object #<Object> has no method 'getFoo'
console.log(myContext.getBar()); // Uncaught TypeError: Object #<Object> has no method 'getBar'

Siguiendo este patrón, podemos incluso asignar un mismo módulo a dos contextos direfentes, consiguiendo implementaciones paralela como podemos ver en el siguiente ejemplo
// Declarations
var myContext1 = {}, myContext2 = {};

// Method for any context
var increment = function (pSeed) {
    var lValue = pSeed || 0;
    this.increment = function () {
        return lValue++;
    }
};

increment.call(myContext1);
increment.call(myContext2, 100);

console.log(myContext1.increment()); // 0
console.log(myContext1.increment()); // 1
console.log(myContext1.increment()); // 2
console.log(myContext2.increment()); // 100
console.log(myContext2.increment()); // 101
console.log(myContext2.increment()); // 102

Conclusiones

En la programación Javascript, hay que evitar la creación de variables y métodos globales, si pretendemos integrar librerías de terceros o escribir un código que pueda ser reutilizado. Para ello tenemos que estructurarlo correctamente para evitar duplicidades que hagan que la ejecución de nuestro código no sea la esperada.

Para ello, recurrimos a la creación de objetos que actúen como contenedores de código que compartan funcionalidad. A lo largo de este artículo y del anterior hemos visto diferentes aproximaciones al mismo problem.

No hay comentarios:

Publicar un comentario