viernes, 15 de febrero de 2013

Más allá del misterio: Asignando valores con el Revealing Module Pattern

Un patrón bastante extendido cuando desarrollamos código en JavaScript es el Revealing Module Pattern para poder establecer ámbito sobre ciertas partes de nuestro módulo. Quien más quien menos que desarrolle en JavaScript (y no esté en una cueva) ha oído hablar de él así que no vamos a hacer un análisis en detalle sobre el mismo.
Por otro lado, es bastante común exponer un objeto con los nombre de las propiedades que coincidentes con los métodos y propiedades que exponemos y aquí viene el punto propenso a errores que quiero mitigar con este post.
Imaginemos este código:
   1:  var Person = function () {
   2:      var name,
   3:          sayHello = function () {
   4:              alert('Hi, my name is ' + name);
   5:          };
   6:      return {
   7:          name: name,
   8:          sayHello: sayHello,
   9:      }
  10:  }
  11:   
  12:  var andoni = new Person();
  13:  andoni.name = 'andoni';
  14:  andoni.sayHello();



Si yo os pregunto que mostrará la función sayHello por pantalla, que me diríais?
Pues aunque no os lo creáis un bonito mensaje: "Hi, my name is undefined"
Ahora es cuando viene lo de:

“¿Pero que ha pasado? Si yo he asignado el name a "Andoni"!!!

Esto seguro que es por lo del this, o lo del lexical scope … JavaScript es un mierda y que porque no nos habremos quedado en la era de COBOL...”

Que no cunda el pánico, vamos a entender que ha pasado antes de pegarle fuego a V8, Chakra y a todo lo que se mueva...

Tened en cuenta que si aplicamos el patrón, la función nos devuelve otro objeto con dos propiedades (name y sayHello). Cada una de ellas apunta a los objetos reales que está revelando. El código que hemos visto arriba recibe ese objeto y lo que está haciendo es cambiar a que apunta la propiedad name del objeto revelado, que obviamente, no es lo que esperábamos... (recordad los punteros)

Revisemos nuestro código problemático con una modificación:

   1:  var Person = function () { 
   2:      var name, 
   3:          setName = function(nameToSet){ 
   4:              name = nameToSet; 
   5:          }, 
   6:          sayHello = function () { 
   7:              alert('Hi, my name is ' + name); 
   8:          }; 
   9:      return { 
  10:          name: name, 
  11:          setName: setName, 
  12:          sayHello: sayHello, 
  13:      } 
  14:  }
  15:   
  16:  var andoni = new Person(); 
  17:  andoni.setName('andoni'); 
  18:  andoni.sayHello();



Ahora si que recibiremos el mensaje esperado, debido a que la modificación se esta realizando sobre el mismo objeto y no sobre la propiedad del proxy que nos está siendo revelado.

Es un problema sutil y en ocasiones difícil de corregir así que espero que les sea de utilidad.

Un saludo desde Seattle!!

Andoni Arroyo