JavaScript: controllare proprietà e comportamento degli oggetti con i descrittori

by Anastasia P.
SHARE
JavaScript: controllare proprietà e comportamento degli oggetti con i descrittori
© Guida-HTML5.it

Introduzione

In JavaScript un oggetto non contiene soltanto coppie chiave-valore. Ogni proprietà possiede anche alcune caratteristiche interne, chiamate descrittori di proprietà. Queste caratteristiche stabiliscono, per esempio, se una proprietà può essere modificata, eliminata o mostrata durante una scansione con Object.keys().

Conoscere i descrittori è utile quando si costruiscono oggetti più robusti, configurazioni applicative, moduli riutilizzabili o strutture dati che devono impedire modifiche accidentali. Le funzioni principali sono Object.defineProperty(), Object.defineProperties() e Object.getOwnPropertyDescriptor().

Ogni proprietà può essere definita in due modi:

  • come proprietà dati, con un valore concreto;
  • come proprietà accessor, tramite getter e setter.

Una proprietà dati utilizza descrittori come value, writable, enumerable e configurable. Una proprietà accessor utilizza invece get e set.

Codice completo

const prodotto = {
  nome: "Tastiera meccanica",
  prezzo: 89.90
};

// Aggiunge una proprietà con regole personalizzate
Object.defineProperty(prodotto, "codice", {
  value: "KB-001",
  writable: false,
  enumerable: true,
  configurable: false
});

// Proprietà privata simulata tramite chiusura e accessor
let quantitaDisponibile = 12;

Object.defineProperty(prodotto, "quantita", {
  enumerable: true,
  configurable: false,

  get() {
    return quantitaDisponibile;
  },

  set(nuovaQuantita) {
    if (!Number.isInteger(nuovaQuantita) || nuovaQuantita < 0) {
      throw new Error("La quantità deve essere un intero non negativo");
    }

    quantitaDisponibile = nuovaQuantita;
  }
});

// Metodo normale dell´oggetto
prodotto.isDisponibile = function () {
  return this.quantita > 0;
};

console.log(prodotto.nome);
console.log(prodotto.codice);
console.log(prodotto.quantita);
console.log(prodotto.isDisponibile());

// Il setter esegue la validazione
prodotto.quantita = 8;

try {
  prodotto.quantita = -3;
} catch (errore) {
  console.error(errore.message);
}

// codice è leggibile, ma non modificabile
prodotto.codice = "KB-999";
console.log(prodotto.codice); // KB-001

// Lettura del descrittore di una proprietà
const descrittore = Object.getOwnPropertyDescriptor(prodotto, "codice");
console.log(descrittore);

// Definizione di più proprietà in una sola operazione
Object.defineProperties(prodotto, {
  categoria: {
    value: "Accessori",
    writable: false,
    enumerable: true,
    configurable: false
  },

  descrizioneCompleta: {
    enumerable: true,
    configurable: true,

    get() {
      return `${this.nome} - ${this.categoria}`;
    }
  }
});

console.log(prodotto.descrizioneCompleta);

console.log(Object.keys(prodotto));

Spiegazione

Le proprietà dati

Nel codice, la proprietà codice viene creata con Object.defineProperty(). L´opzione writable: false impedisce di cambiare il valore dopo la definizione. In modalità non strict, un´assegnazione non valida può essere ignorata; in modalità strict, invece, genera un errore.

L´opzione enumerable: true rende la proprietà visibile durante operazioni come Object.keys(), Object.values() o un ciclo for...in. Se fosse impostata a false, la proprietà continuerebbe a essere accessibile direttamente, ma non apparirebbe nelle enumerazioni standard.

Infine, configurable: false impedisce di eliminare la proprietà e di modificarne successivamente il descrittore. Questa impostazione è utile per proteggere elementi fondamentali, come identificatori, codici o configurazioni iniziali.

Getter e setter come interfaccia controllata

La proprietà quantita non memorizza direttamente il valore nell´oggetto. Il valore reale si trova nella variabile quantitaDisponibile, accessibile soltanto dalle funzioni get e set. Questo schema permette di applicare regole prima di accettare un nuovo valore.

Il getter viene eseguito quando si legge prodotto.quantita. Il setter viene eseguito quando si assegna un valore. In questo modo l´oggetto espone una sintassi semplice, ma mantiene il controllo sulla validità dei dati.

Descrittori e metodi

Anche un metodo è una proprietà dell´oggetto. Quando viene aggiunto con una semplice assegnazione, come nel caso di isDisponibile, possiede descrittori predefiniti. Se invece si vuole controllare anche la visibilità o la possibilità di modifica del metodo, è possibile definirlo con Object.defineProperty().

Object.getOwnPropertyDescriptor() restituisce un nuovo oggetto che descrive la proprietà richiesta. Per una proprietà dati, il risultato contiene normalmente value, writable, enumerable e configurable. Per una proprietà accessor, contiene get, set, enumerable e configurable.

Best practice

  • Usa descrittori espliciti quando una proprietà deve seguire regole precise o non deve essere modificata accidentalmente.
  • Usa i setter per validazioni semplici e immediate, evitando di nascondere logiche applicative molto complesse dentro un´assegnazione.
  • Ricorda che Object.defineProperty() crea, per impostazione predefinita, proprietà non enumerabili, non scrivibili e non configurabili se non specifichi diversamente i descrittori.
  • Preferisci Object.defineProperties() quando devi configurare più proprietà correlate in modo dichiarativo.
  • Non usare descrittori avanzati soltanto per rendere il codice più complicato. Se una proprietà pubblica può essere modificata liberamente senza rischi, una semplice assegnazione è spesso più leggibile.
  • Utilizza la modalità strict, oppure i moduli JavaScript, per ottenere errori chiari quando tenti di modificare proprietà non scrivibili.
  • Ricorda che la protezione riguarda la proprietà stessa, non necessariamente gli oggetti annidati nel suo valore. Per una protezione profonda servono strategie aggiuntive.

Riepilogo

I descrittori permettono di controllare in modo dettagliato il comportamento delle proprietà JavaScript. Con Object.defineProperty() puoi creare proprietà leggibili ma non modificabili, nascoste dalle enumerazioni oppure non eliminabili. Con Object.defineProperties() puoi definire più proprietà in una sola operazione, mentre Object.getOwnPropertyDescriptor() consente di analizzare le regole già applicate.

Questa tecnica è particolarmente utile per progettare oggetti affidabili, validare i dati assegnati e proteggere le parti più importanti di un modello applicativo. Va però applicata con equilibrio: la chiarezza del codice rimane più importante della configurazione avanzata.

Approfondisci con risorse ufficiali

  • MDN Web Docs: documentazione su Object.defineProperty() e sui descrittori delle proprietà.
  • MDN Web Docs: riferimento a Object.defineProperties() per la definizione multipla.
  • MDN Web Docs: guida a Object.getOwnPropertyDescriptor() per leggere i metadati delle proprietà.
  • ECMAScript Language Specification: sezione dedicata ai Property Descriptors e agli Internal Methods degli oggetti.

SHARE