Getters y Setters en JavaScript
Aprende getters y setters en JavaScript: sintaxis get/set, propiedades de acceso vs. de datos, validación, propiedades computadas y uso en clases.
La mayoría de las propiedades de los objetos en JavaScript son propiedades de datos — simplemente almacenan un valor. Sin embargo, los objetos también admiten propiedades de acceso: propiedades respaldadas por funciones que se ejecutan cada vez que la propiedad es leída o escrita. Estas funciones se llaman getters y setters. Para el mundo exterior, una propiedad de acceso parece una propiedad ordinaria (user.age), pero detrás de escena tu propio código decide qué hace realmente leer o asignar un valor.
Esta guía cubre la sintaxis get/set, cómo difieren las propiedades de acceso de las propiedades de datos, los casos de uso más comunes (validación y valores computados), cómo funcionan los getters y setters dentro de las clases, y los errores más habituales que conviene conocer.
Introducción a los Getters y Setters de Propiedades
Un getter es una función que se ejecuta cuando lees una propiedad; su valor de retorno se convierte en el valor del acceso. Un setter es una función que se ejecuta cuando asignas un valor a una propiedad; recibe el valor asignado como único argumento. Al ser funciones, pueden ejecutar validaciones, calcular un resultado al vuelo, registrar accesos o actualizar otras propiedades — todo mientras el código que las llama utiliza la sintaxis habitual de propiedades.
Sintaxis
Dentro de un literal de object, define un getter con la palabra clave get seguida de un método, y un setter con la palabra clave set seguida de un método que acepta un parámetro:
let obj = {
get propName() {
// getter, the code executed when obj.propName is read
},
set propName(value) {
// setter, the code executed when obj.propName is written
}
};Puedes definir ambos, o solo uno. Una propiedad con únicamente un getter es de solo lectura; asignarle un valor no hace nada en modo no estricto y lanza un TypeError en modo estricto. Una propiedad con únicamente un setter es de solo escritura — leerla devuelve undefined.
Propiedades de acceso vs. propiedades de datos
Vale la pena ser precisos sobre qué tipo de propiedad crea un getter/setter. Una propiedad regular como width: 5 es una propiedad de datos con un value. Un par getter/setter, en cambio, crea una propiedad de acceso que no tiene value propio — solo funciones get y set. Ambos tipos son mutuamente excluyentes: un único descriptor de propiedad no puede tener a la vez un value y un get/set.
Por eso, los ejemplos de validación a continuación guardan el número real en un campo de respaldo separado (_age): el accessor público age necesita algún lugar donde almacenar los datos, ya que no tiene una ranura de valor propia. Para inspeccionarlo directamente, consulta Flags y descriptores de propiedades.
¿Por Qué Usar Getters y Setters?
Los getters y setters ofrecen varios beneficios, entre ellos:
- Encapsulación: Ocultan la representación interna detrás de una interfaz pública estable. Puedes cambiar cómo se almacena un valor sin romper el código que lo lee.
- Validación: Puedes rechazar o normalizar valores en el setter antes de que sean almacenados.
- Propiedades Computadas: Un getter puede derivar su resultado de otras propiedades, de modo que el valor esté siempre actualizado y nunca quede obsoleto.
- Compatibilidad hacia atrás: Si una propiedad que antes era un campo simple necesita lógica añadida más adelante, puedes reemplazarla con un accessor del mismo nombre — el código que la utiliza no cambia.
Ejemplos Prácticos
Veamos algunos ejemplos prácticos para ilustrar cómo se pueden usar getters y setters en escenarios del mundo real.
Ejemplo 1: Object de Usuario con Validación de Edad
Considera un object de usuario en el que queremos asegurarnos de que la propiedad edad siempre esté dentro de un rango razonable. El setter valida la entrada; el getter simplemente devuelve el campo de respaldo.
Ejemplo 2: Creación de Propiedades Computadas
Los getters nos permiten crear propiedades que se calculan al vuelo en función de otros datos. Cada vez que se lee area, se recalcula, por lo que permanece sincronizada si width o height cambian.
Ejemplo 3: Definición de accessors con Object.defineProperty
La sintaxis literal get/set es la forma más común de declarar accessors, pero también puedes añadirlos a un object existente — incluso después de haberlo creado — con Object.defineProperty. Esto resulta útil cuando el nombre de la propiedad es dinámico o cuando quieres controlar flags como enumerable.
Buenas Prácticas
Al usar getters y setters, ten en cuenta las siguientes buenas prácticas para garantizar que tu código sea limpio, mantenible y eficiente:
- Evita Efectos Secundarios en los Getters: Los getters deben ser rápidos y no tener efectos secundarios, ya que a menudo son invocados implícitamente por el motor o durante la enumeración de propiedades.
- Validación: Siempre valida los datos en los setters para evitar que se almacenen datos inválidos o dañinos.
- Convenciones de Nomenclatura: Usa un guion bajo inicial (
_) en los nombres de los campos de respaldo para indicar que son privados. - Serialización JSON: Ten en cuenta que
JSON.stringify()ignora los getters por defecto. Usa una función de reemplazo o serialización explícita si necesitas incluir valores computados.
Casos de Uso Avanzados
Nombres de Propiedades Dinámicos
JavaScript ES6 introdujo los nombres de propiedades computados, que pueden combinarse con getters y setters para definir dinámicamente claves de accessor basadas en variables o expresiones.
Integración con Clases
Los getters y setters también son muy útiles en la programación basada en clases, ya que ofrecen una forma de encapsular y controlar el acceso a las propiedades de clase. La sintaxis es idéntica — simplemente escribe métodos get/set en el cuerpo de la clase — y se colocan en el prototipo, por lo que todas las instancias los comparten.
Errores Comunes
Algunos errores suelen sorprender a los desarrolladores:
- Recursión infinita por un campo de respaldo incorrecto. Si un setter para
nameasigna athis.nameen lugar de a un campo separado comothis._name, la asignación vuelve a disparar el setter indefinidamente. Siempre almacena el valor bajo una clave diferente. - Un getter sin setter es de solo lectura. Asignarle un valor no hace nada (o lanza una excepción en modo estricto), lo que puede parecer un error silencioso.
JSON.stringify()llama a los getters pero ignora los setters. Un getter computado sí se serializa (su valor de retorno se incluye), pero no hay forma de restaurarlo mediante un setter enJSON.parse()— solo recuperas datos planos.thisdepende del lugar de la llamada. Dentro de un getter o setter,thises el object en el que se accedió a la propiedad. Si copias el accessor a otro object,thiscambia en consecuencia — consulta Métodos de object y "this".
El ejemplo a continuación muestra la trampa de la recursión y su solución:
Conclusión
Dominar los getters y setters de propiedades es un paso fundamental para convertirse en un desarrollador JavaScript competente. Estas características no solo mejoran la funcionalidad y la seguridad del código, sino que también allanan el camino hacia bases de código más legibles y mantenibles. Siguiendo las buenas prácticas y los ejemplos proporcionados en esta guía, los desarrolladores pueden aprovechar eficazmente los getters y setters, logrando aplicaciones JavaScript más robustas y eficientes.