Configurar propiedades de clases en javascript
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.