Configurar propiedades de clases en javascript

De ChuWiki

En javascript podemos definir una clase con propiedades o atributos de esta forma

var a = {x:11, y:22}

Estas propiedades son por defecto visibles y modificables, pero tenemos formas de configurar todo esto. Vamos a ver algunas posibilidades


defineProperty()[editar]

La clase Object de javascript tiene un método defineProperty() que nos permite definir propiedades en otras clases, de forma similar a como definirmo x e y en el ejemplo anterior. La ventaja de defineProperty() es que nos permite configurar esa propiedad con cuatro posibles características:

  • value es el valor que queremos que tenga, 11 y 22 en el ejemplo anterior
  • writable puede ser true o false y nos indica si esa propiedad admite que se cambie su valor o no.
  • enumearable puede ser true o false y nos indica si esa propiedad aparecerá en un bucle que hagamos recorriendo las propiedades de un objeto (ver más abajo).
  • configurable puede ser true o false e indica si esa propiedad puede ser reconfigurada o no.

Object.defineProperty() admite tres parámetros, el objeto al que queremos añadir/modificar la propiedad, el nombre de la propiedad y las opciones que queremos ponerle/modificarle. Devuelve el objeto modificado, así que serían posibles estas llamadas

// Si estamos creando un objeto nuevo
var a = Object.defineProperty({}, "x", { value:11, writable:true, enumerable:true, configurable:true});

// Si el objeto ya está definido
var a = {};
Object.defineProperty(a, "x", { value:11, writable:true, enumerable:true, configurable:true});

Un detalle a tener en cuenta. Si la propiedad es nueva, los valores por defecto para cada opción son false, por lo que si no rellenamos alguna, será false. En el siguiente caso por ejemplo

var a = Object.defineProperty({}, "x", {value:11});

la propiedad x no será writable, no será enumerable y no será configurable. Si la propiedad ya existe, entonces las opcoines que no pongamos no se modificarán y permanecerán como ya las tuviera la propiedad. Por ejemplo, si hacemos

var a = {x:11};   // es writable, enumerable y configurable por defecto al definirla asi
Object.setProperty(a, "x", {writable:false});  // Ahora es no writable, pero sigue siendo enumerable y configurable.

value[editar]

Si tenemos una objeto a, podemos definirle una propiedad y darle un valor de esta forma

var a = {};
Object.defineProperty (a, "x", {value:11});
// ahora a.x vale 11

Como comentábamos antes, al no poner el resto de opciones, esta a.x es no writable, no enumerable y no configurable.


writable[editar]

Si definimos así la propiedad

var a = Object.defineProperty({}, "x", {value:11, writable:false});

La propiedad vale 11 y no se puede modificar. Si hacemos

a.x=22;

no dará error, pero a.x seguirá valiendo 11. Un detalle a tener en cuenta es que si la propiedad no es writable, pero sí es configurable, podemos cambiar su valor a través de defineProperty. Por ejemplo

var a = Object.defineProperty({}, "x", {value:11, writable:false, configurable:true});
Object.defineProperty(a, "x", {value:22});  // Esto cambia el valor aunque sea no writable. Ahora x.a vale 22


enumerable[editar]

Si definimos un objeto así

var a = {x:11, y:22};

podemos hacer un bucle para recorrer cada una de sus propiedades de esta forma

for ( propiedad in a ) {
   console.log (propiedad);  // Va escribiendo cada propiedad
}
// Esto saca en pantalla
// x
// y

Pues bien, si hacemos que una propiedad sea no enumerable, no aparecera al recorrerla con este tipo de bucle

Object.defineProperty (a, "x", {enumerable:false});
for ( propiedad in a ) {
   console.log (propiedad);  // Va escribiendo cada propiedad
}
// Esto saca en pantalla solo la y
// y


configurable[editar]

Esta es la opción que nos indica si vamos a poder usar el defineProperty() más adelante otra vez para cambiar algo o no. Si no es configurable, no podemos volver a cambiar nada, excepto hacer cambiar writable true por false. Por ejemplo, si hacemos

var a = {x:1};
Object.defineProperty(a, "x", {configurable:false});

a partir de aquí obtenemos error con cualquiera de las siguientes

Object.defineProperty(a, "x", {configurable:true});
Object.defineProperty(a, "x", {writable:true});
Object.defineProperty(a, "x", {value:2});
// Dan error "TypeError: Cannot redefine property: x"

setters y getters[editar]

A la hora de definir una propiedad con defineProperty(), en vez de indicar las opciones value y writable, podemos indicar las opciones get y set, pasando una función a esas opciones. Cuando intentemos acceder para leer la propiedad, se ejecutará la función get, cuando lo intentemos en modo escritura, se ejecutará la opción set. En el siguiente ejemplo

var a = Object.defineProperty ({}, "x", {
   get : function() {return Math.random();}, 
   set : function(valor) {console.log('me pasan '+valor)}, 
   enumerable:true, 
   configurable:true})

Si intentamos acceder a a.x para leer, obtendremos cada vez un número aleatorio

> a.x
0.28053418290801346

Si estamos con algún debugger de javascript con console como el de google chrome, cada vez que intenemos fijar un valor a a.x se sacará por pantalla un texto indicándolo, pero no se guardará nada.

> a.x=33
me pasan 33

Esto puede tener en algún momento dado alguna utilidad, pero su verdadera utilida es cuando a través de estos métodos set y get accedermos y modificamos otros atributos de la clase. Por ejemplo, podemos tener un atributo x y un método getter/setter doble, de forma que nos devuelva el doble de x o haga que x sea la mitad

var a = Object.defineProperty ({x:11}, "doble", {
   get : function() {return this.x*2;}, 
   set : function(valor) {this.x=valor/2}, 
   enumerable:true, 
   configurable:true})

En a tenermos ahora un objeto con un atributo x que vale 11 y un atributo doble (en realidad un setter y un getter) que devuelve el doble de x o lo convierte a la mitad

> a.x=22  // asignamos 22 a x
22
> a.doble   // su doble es 44
44
> a.doble=10   // hacemos que el doble sea 10 ...
10
> a.x    // ... por lo que x es ahora 5
5


defineProperties[editar]

Además de Object.defineProperty() que define una propiedad, tenemos el método defineProperties que permite definir muchas de golpe. en el siguiente ejemplo

var a = Object.defineProperties ( {} , {
   "x" : {
      value : 11;
      writable : true;
      enumerable : false;
      configurable : true;
   },
   "y" : {
      value : 22;
      writable : false;
      enumerable : true;
      configurable : false;
   });

estamos definiendo un objeto con dos propiedades x e y, cada una de ellas con las opciones que aparecen en el ejemplo.