DMarlon Studio

Lección 5 · 5 de 20

Getters, setters y campos privados · JavaScript

Proteja el estado de sus objetos en JavaScript con campos privados #, valide valores con setters y calcule propiedades con getters.

JavaScript · Getters, setters y campos privados en DMarlon Studio: el código del ejemplo y su salida en la terminal

Los campos que empiezan por `#` son privados: solo el código de dentro de la clase puede leerlos o cambiarlos. #price y #changes se declaran arriba, con su valor inicial, y desde fuera no existen: mouse["#price"] da undefined y Object.keys(mouse) solo muestra name. Escribir mouse.#price fuera de la clase sería un error de sintaxis.

Un setter (set price(value)) parece una propiedad normal, pero ejecuta código al asignar. Por eso mouse.price = 18 pasa por la validación y mouse.price = -3 lanza un RangeError, que el programa captura y muestra. Incluso el constructor usa this.price = price, así que el precio inicial también se valida y cuenta como cambio: de ahí los 2 changes.

Un getter (get price()) se lee sin paréntesis: mouse.price. Sirve además para propiedades calculadas: priceWithVat no se guarda en ningún sitio, se calcula cada vez a partir de #price. Como no tiene setter, la asignación mouse.priceWithVat = 1 no hace nada (en modo estricto lanzaría un TypeError).

Juntos, los campos privados, los getters y los setters permiten encapsular: el objeto decide qué se ve, qué se puede cambiar y con qué reglas, y nadie puede dejarlo en un estado incorrecto, como un producto con precio negativo.

Código · main.js

class Product {  #price = 0; // private: only code inside the class can touch it  #changes = 0;  constructor(name, price) {    this.name = name;    this.price = price; // goes through the setter below  }  get price() {    return this.#price;  }  set price(value) {    if (typeof value !== "number" || value < 0) {      throw new RangeError(`Invalid price for ${this.name}: ${value}`);    }    this.#price = value;    this.#changes++;  }  get priceWithVat() {    return +(this.#price * 1.21).toFixed(2);  }  get changes() {    return this.#changes;  }}const mouse = new Product("Mouse", 20);mouse.price = 18;console.log(mouse.name, mouse.price, "->", mouse.priceWithVat, "with VAT");try { mouse.price = -3; } catch (err) { console.log(`${err.name}: ${err.message}`); }console.log("Price is still", mouse.price, "after", mouse.changes, "changes");mouse.priceWithVat = 1; // a getter without a setter ignores assignmentsconsole.log("With VAT:", mouse.priceWithVat, "| keys:", Object.keys(mouse).join(", "));console.log("From outside:", mouse["#price"]);

Salida · node main.js

Mouse 18 -> 21.78 with VAT
RangeError: Invalid price for Mouse: -3
Price is still 18 after 2 changes
With VAT: 21.78 | keys: name
From outside: undefined

Para recordar

  • Un campo #privado solo es accesible desde dentro de la clase.
  • Un setter valida o transforma el valor en cada asignación.
  • Un getter se lee como una propiedad y puede calcular su valor.
  • Un getter sin setter es una propiedad de solo lectura.

Ejercicio

Añada un setter discount que admita un porcentaje entre 0 y 50, lo guarde en un campo privado #discount y haga que priceWithVat lo descuente; pruebe un descuento válido y otro de 80.