preloader

Getters y Setters de Propiedad en JavaScript

Getters y Setters de Propiedad

En JavaScript, los objetos pueden tener dos tipos principales de propiedades.

El primer tipo son las propiedades de datos, que ya conocemos. Todas las propiedades que hemos utilizado hasta ahora son propiedades de datos.

El segundo tipo son las propiedades de acceso, también conocidas como accessors. Estas son funciones que se ejecutan para obtener (“get”) y establecer (“set”) un valor, pero desde fuera parecen propiedades normales.

Getters y Setters

Las propiedades de acceso se crean con métodos de obtención (getter) y asignación (setter). En un objeto literal, se denotan con get y set:

				
					let obj = {
  get propName() {
    // getter, el código ejecutado para obtener obj.propName
  },

  set propName(value) {
    // setter, el código ejecutado para asignar obj.propName = value
  }
};

				
			
  • El getter se activa cuando se lee obj.propName, y el setter se activa cuando se asigna un valor a obj.propName.

    Por ejemplo, imaginemos un objeto user con propiedades name y surname:

				
					let user = {
  name: "John",
  surname: "Smith"
};

				
			

Queremos agregar una propiedad fullName que combine name y surname en un solo valor, como «John Smith». En lugar de duplicar la información existente, podemos crearla como una propiedad de acceso:

				
					let user = {
  name: "John",
  surname: "Smith",

  get fullName() {
    return `${this.name} ${this.surname}`;
  }
};

alert(user.fullName); // John Smith

				
			
  • Desde fuera, fullName parece una propiedad normal. No llamamos a user.fullName como si fuera una función; simplemente la leemos, y el getter se ejecuta en segundo plano.

    Actualmente, fullName solo tiene un getter. Si intentamos asignarle un valor, obtendremos un error:

				
					let user = {
  get fullName() {
    return `...`;
  }
};

user.fullName = "Test"; // Error (property has only a getter)

				
			

Solucionemos esto añadiendo un setter para user.fullName:

				
					let user = {
  name: "John",
  surname: "Smith",

  get fullName() {
    return `${this.name} ${this.surname}`;
  },

  set fullName(value) {
    [this.name, this.surname] = value.split(" ");
  }
};

// El setter fullName se ejecuta con el valor dado.
user.fullName = "Alice Cooper";

alert(user.name); // Alice
alert(user.surname); // Cooper

				
			

Como resultado, ahora tenemos una propiedad virtual fullName que puede ser leída y escrita.

Descriptores de Acceso

Los descriptores de propiedades de acceso son diferentes a los descriptores de propiedades de datos.

Para las propiedades de acceso, no existen value ni writable; en su lugar, tenemos get y set.

Un descriptor de acceso puede tener:

  • get: una función sin argumentos que se ejecuta cuando se lee la propiedad.
  • set: una función con un argumento que se llama cuando se establece la propiedad.
  • enumerable: igual que para las propiedades de datos.
  • configurable: igual que para las propiedades de datos.

Por ejemplo, para crear una propiedad de acceso fullName usando defineProperty, podemos pasar un descriptor con get y set:

				
					let user = {
  name: "John",
  surname: "Smith"
};

Object.defineProperty(user, 'fullName', {
  get() {
    return `${this.name} ${this.surname}`;
  },

  set(value) {
    [this.name, this.surname] = value.split(" ");
  }
});

alert(user.fullName); // John Smith

for (let key in user) alert(key); // name, surname

				
			

Tenga en cuenta que una propiedad puede ser un accessor (tiene métodos get/set) o una propiedad de datos (tiene un value), pero no ambas.

Si intentamos poner get y value en el mismo descriptor, obtendremos un error:

				
					// Error: Descriptor de propiedad inválido.
Object.defineProperty({}, 'prop', {
  get() {
    return 1;
  },

  value: 2
});

				
			

Getters y Setters más Inteligentes

Los getters y setters pueden ser utilizados como envoltorios alrededor de valores de propiedad “reales” para obtener más control sobre ellos.

Por ejemplo, si queremos prohibir nombres demasiado cortos para user, podemos guardar name en una propiedad especial _name y filtrar las asignaciones en el setter:

				
					let user = {
  get name() {
    return this._name;
  },

  set name(value) {
    if (value.length < 4) {
      alert("El nombre es demasiado corto, necesita al menos 4 caracteres");
      return;
    }
    this._name = value;
  }
};

user.name = "Pete";
alert(user.name); // Pete

user.name = ""; // El nombre es demasiado corto...

				
			

Entonces, name se almacena en la propiedad _name, y el acceso se realiza a través del getter y setter.

Técnicamente, el código externo aún puede acceder directamente a _name, pero hay una convención general de que las propiedades que comienzan con un guion bajo _ son internas y no deben ser manipuladas desde fuera del objeto.

Uso para Compatibilidad

Una de las grandes ventajas de los getters y setters es que permiten tomar el control de una propiedad de datos “normal” y reemplazarla con un getter y un setter para refinar su comportamiento.

Imaginemos que empezamos a implementar objetos user usando las propiedades de datos name y age:

				
					function User(name, age) {
  this.name = name;
  this.age = age;
}

let john = new User("John", 25);

alert(john.age); // 25

				
			

Pero eventualmente, podemos decidir almacenar birthday en lugar de age, ya que es más preciso y conveniente:

				
					function User(name, birthday) {
  this.name = name;
  this.birthday = birthday;
}

let john = new User("John", new Date(1992, 6, 1));

				
			

Ahora, ¿qué hacemos con el viejo código que aún usa la propiedad age?

Podríamos intentar encontrar todos esos lugares y corregirlos, pero eso lleva tiempo y puede ser difícil si ese código fue escrito por otras personas. Además, age es una propiedad útil para tener en user.

Podemos mantener la propiedad age añadiendo un getter que calcule la edad a partir de birthday:

				
					function User(name, birthday) {
  this.name = name;
  this.birthday = birthday;

  Object.defineProperty(this, "age", {
    get() {
      let todayYear = new Date().getFullYear();
      return todayYear - this.birthday.getFullYear();
    }
  });
}

let john = new User("John", new Date(1992, 6, 1));

alert(john.birthday); // El cumpleaños está disponible
alert(john.age);      // ...así como la edad

				
			

Ahora, el código antiguo también funciona, y tenemos una propiedad adicional útil.

Related Post

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *