Jack Franklin

23 de diciembre de 2013

Por lo general, en JavaScript, lo que ves es lo que obtienes. Un valor es un valor; no hay trucos. A veces, sin embargo, quieres un valor que se basa en algunos otros valores: el nombre completo de alguien, por ejemplo, es una concatenación de sus nombres y apellidos. Si tienes un objeto person, y quieres que los usuarios de ese objeto puedan establecer el nombre completo, el nombre o el apellido, y ver ese cambio inmediatamente reflejado en los otros valores, lo construirías convencionalmente con funciones:

person.setLastName('Smith');person.setFirstName('Jimmy');person.getFullName(); // Jimmy Smith

Pero esto es feo, y requiere que los usuarios de tu objeto se preocupen de que las propiedades estén relacionadas; en un ejemplo más complejo, eso podría no ser tan obvio como con los nombres. Por suerte, hay una forma mejor, añadida en ECMAScript 5.

Conozca los getters y setters.

Cómo

Hagamos ese objeto persona. Queremos ser capaces de establecer el nombre, el apellido o el nombre completo, y que actualice los otros dos automáticamente.

var person = { firstName: 'Jimmy', lastName: 'Smith', get fullName() { return this.firstName + ' ' + this.lastName; }, set fullName (name) { var words = name.toString().split(' '); this.firstName = words || ''; this.lastName = words || ''; }}

person.fullName = 'Jack Franklin';console.log(person.firstName); // Jackconsole.log(person.lastName) // Franklin

¿Qué pasa aquí?

Las palabras clave get y set son importantes. Tras ellas está la propiedad a la que se refieren (fullName) y un cuerpo de función que define el comportamiento cuando se accede a la propiedad (name = person.fullName) o se modifica (person.fullName = 'Some Name').

Estas dos palabras clave definen funciones accesorias: un getter y un setter para la propiedad fullName. Cuando se accede a la propiedad, se utiliza el valor de retorno del getter. Cuando se establece un valor, se llama al setter y se le pasa el valor que se estableció. Depende de usted lo que haga con ese valor, pero lo que se devuelve desde el setter es el valor que se pasó – por lo que no necesita devolver nada.

La forma oficial: Object.defineProperty

Además del método inline de declarar getters y setters, también se puede hacer de forma más explícita a través de Object.defineProperty (Documentación MDN). Este método toma tres argumentos. El primero es el objeto al que añadir la propiedad, el segundo es el nombre de la propiedad, y el tercero es un objeto que describe la propiedad (conocido como el descriptor de la propiedad). Aquí hay un ejemplo que replica el ejemplo anterior:

var person = { firstName: 'Jimmy', lastName: 'Smith'};

Object.defineProperty(person, 'fullName', {get: function() {return firstName + ' ' + lastName;},set: function(name) {var words = name.split(' ');this.firstName = words || '';this.lastName = words || '';}});

La ventaja aquí no es inmediatamente evidente. Aparte de poder añadir propiedades después de crear el objeto inicial, ¿hay algún beneficio real?

Cuando defines una propiedad de esta manera, puedes hacer mucho más que definir un setter o getter. También puedes pasar las siguientes claves:

  • configurable (false por defecto): si es true, la configuración de la propiedad será modificable en el futuro.
  • enumerable (false por defecto): si es true, la propiedad aparecerá al hacer un bucle sobre el objeto (for (var key in obj)).

También podemos definir propiedades que no tienen getters o setters explícitos:

Object.defineProperty(person, 'age', { value: 42});

Esto creará person.age, y la pondrá al valor 42. Es importante tener en cuenta que esta propiedad no es escribible. Llamar a person.age = 99 no tendrá ningún efecto. De esta manera se pueden crear propiedades de sólo lectura. Si una propiedad tiene una clave value establecida, no puede tener un getter o setter. Las propiedades pueden tener valores o accesores, no ambos.

No sólo eso, sino que como la propiedad enumerable tiene por defecto false, esta propiedad no aparecerá cuando hagamos un bucle sobre las claves del objeto.

Si quisiéramos que una propiedad fuera escribible, tendríamos que establecer la propiedad writable:

Object.defineProperty(person, 'age', { value: 42, writable: true});

Ahora, person.age = 99; tendrá el efecto deseado.

Overuse

Recuerda: sólo porque exista una función, no es necesario usarla todo el tiempo. Los Getters y Setters tienen sus casos de uso, pero no te pases de la raya, o lo más probable es que acabes con un diseño confuso para los que interactúan con tus objetos. Usados con cuidado, son muy poderosos. Pero un gran poder conlleva una gran responsabilidad.

¿Soporte para navegadores?

IE9 y superiores tienen soporte completo para Object.defineProperty, junto con Safari 5+, Firefox 4+, Chrome 5+ y Opera 12+. Si estás trabajando con Node.js, hay soporte completo. ¿No te encanta Node?

Este artículo ha sido escrito conjuntamente con Tom Ashworth. Gracias a Tom por toda su ayuda en la elaboración de este artículo.

Si te ha gustado este artículo, únete al boletín de noticias para recibir actualizaciones sobre nuevos contenidos 😎.

Todos los artículos del blog

Si te ha gustado este artículo, asegúrate de seguirme en Twitter.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.