20 marzo 2025
Padroneggiare gli Oggetti e i Prototipi in JavaScript
JavaScript è fondamentalmente progettato attorno agli oggetti. Gli oggetti sono un aspetto centrale del linguaggio e comprenderli a fondo è essenziale per padroneggiare JavaScript. In questo articolo esploreremo concetti chiave come la creazione degli oggetti, i prototipi, l’ereditarietà e come la parola chiave this
opera nei diversi contesti.
Creazione degli Oggetti
Gli oggetti in JavaScript possono essere creati in vari modi, e ogni approccio ha i suoi vantaggi unici. Esploriamo più da vicino i diversi metodi per creare oggetti.
Utilizzando gli Inizializzatori di Oggetti
Gli inizializzatori di oggetti, noti anche come letterali di oggetti, offrono un modo pulito e conciso per definire gli oggetti. La sintassi per creare oggetti usando i letterali di oggetti è semplice:
const obj = {
property1: value1, // property name may be an identifier
2: value2, // or a number
"property n": value3, // or a string
};
In questa sintassi:
- I nomi delle proprietà (prima dei due punti) possono essere identificatori, numeri o stringhe letterali.
- I valori delle proprietà (dopo i due punti) sono espressioni che assegnano valori a queste proprietà.
- I nomi delle proprietà possono anche essere espressioni. Ad esempio, è possibile utilizzare chiavi computate all’interno di parentesi quadre.
const key = "name";
const obj = {
[key]: "John", // dynamic property name
};
console.log(obj.name); // "John"
I letterali di oggetti sono espressioni, e ogni volta che vengono eseguiti, viene creato un nuovo oggetto. Gli inizializzatori di oggetti identici producono oggetti distinti che non vengono considerati uguali, anche se i loro contenuti sono gli stessi.
Utilizzando una Funzione Costruttore
Un altro modo per creare oggetti è tramite l’uso di funzioni costruttore. Ecco come funziona:
- Definisci un tipo di oggetto scrivendo una funzione costruttore. Di solito, inizia con una lettera maiuscola per seguire una convenzione.
- Crea un’istanza dell’oggetto utilizzando la parola chiave
new
.
function Person(name, age) {
this.name = name;
this.age = age;
}
const person1 = new Person("John", 30);
console.log(person1.name); // "John"
Le funzioni costruttore sono potenti, poiché ti permettono di definire oggetti con proprietà e metodi specifici.
Utilizzando il Metodo Object.create()
Il metodo Object.create()
offre un modo per creare un oggetto e specificare il suo prototipo. Questo metodo consente maggiore flessibilità rispetto alle funzioni costruttore, poiché non richiede la definizione di una funzione costruttore.
const prototype = {
greet() {
console.log("Hello!");
},
};
const obj = Object.create(prototype);
obj.greet(); // "Hello!"
Object.create()
ti consente di impostare il prototipo dell’oggetto appena creato, offrendo un modo semplice per stabilire l’ereditarietà.
Ereditarietà Prototipale
L’ereditarietà è un concetto fondamentale nella programmazione orientata agli oggetti. In JavaScript, l’ereditarietà viene implementata utilizzando i prototipi. Ogni oggetto JavaScript ha un oggetto prototipo, e questo oggetto prototipo ha a sua volta un prototipo, creando così una catena di prototipi.
Ereditarietà con la Catena dei Prototipi
Quando cerchi di accedere a una proprietà di un oggetto, JavaScript verifica innanzitutto se la proprietà esiste sull’oggetto stesso. Se non esiste, JavaScript cerca la proprietà nel prototipo dell’oggetto, e la ricerca continua lungo la catena dei prototipi fino a quando la proprietà non viene trovata o fino a quando non si raggiunge la fine della catena.
Considera il seguente esempio:
const obj = {
a: 1,
b: 2,
__proto__: {
b: 3,
c: 4,
__proto__: {
d: 5,
},
},
};
console.log(obj.d); // 5
In questo esempio, la proprietà d viene trovata attraversando la catena dei prototipi, partendo da obj
e salendo attraverso i suoi prototipi.
La Parola Chiave this
In JavaScript, la parola chiave this
si riferisce al contesto in cui una funzione viene chiamata. È fondamentale comprendere come this
si comporta in diversi scenari.
Il Contesto di this
Il valore di this
dipende da come una funzione viene invocata:
- Chiamata del Metodo: Quando una funzione viene chiamata come metodo di un oggetto (ad esempio,
obj.metodo()
),this
si riferisce all’oggetto (obj
). - Chiamata della Funzione in modo Standalone: Quando una funzione viene chiamata come una funzione normale (ad esempio,
func()
),this
tipicamente si riferisce all’oggetto globale (in modalità non-strict) oundefined
(in modalità strict). - Funzioni Costruttore: Quando una funzione viene utilizzata come costruttore con la parola chiave
new
,this
è legato al nuovo oggetto che viene creato.
Le Funzioni Arrow e this
Le funzioni arrow si comportano in modo diverso per quanto riguarda la parola chiave this
. Invece di avere un proprio this
, le funzioni arrow ereditano this
dal contesto che le racchiude, rendendole utili per preservare il contesto nelle callback e nei gestori di eventi.
const globalObject = this;
const foo = () => this;
console.log(foo() === globalObject); // true
Poiché le funzioni arrow ereditano this
, non è possibile impostare esplicitamente il loro this
utilizzando metodi come bind()
, call()
o apply()
.
Classi (ES6)
In ES6, JavaScript ha introdotto le classi come una sintassi semplificata per l’ereditarietà basata su prototipi esistente. Le classi offrono un modo più chiaro e strutturato per creare oggetti e gestire l’ereditarietà.
Definizione della Classe
Le classi possono essere definite in due modi:
- Dichiarazione della Classe:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
- Espressione della Classe:
const Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
Puoi anche definire una classe anonima o fornire un nome per l’espressione della classe, come mostrato sopra.
Corpo della Classe
Il corpo di una classe è racchiuso tra parentesi graffe {}
. Qui è dove definisci i membri della classe, come le proprietà e i metodi.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
area() {
return this.height * this.width;
}
}
Le classi in JavaScript seguono le regole della modalità strict per default, il che significa che alcuni comportamenti (come l’uso di variabili non dichiarate) sono vietati.
Metodi e Campi Statici
I metodi e i campi statici sono definiti sulla classe stessa, non sulle singole istanze. I metodi statici sono spesso utilizzati per funzioni di utilità, mentre i campi statici possono essere utilizzati per il caching o per dati condivisi tra le istanze.
class Rectangle {
static description = "I am a rectangle class";
static info() {
return Rectangle.description;
}
}
console.log(Rectangle.info()); // "I am a rectangle class"
Ereditarietà con le Classi
Le classi JavaScript supportano l’ereditarietà utilizzando la parola chiave extends
. Una classe può estendere un’altra classe per ereditare le sue proprietà e i suoi metodi.
Esempio di ereditarietà con le classi:
class Square extends Rectangle {
constructor(side) {
super(side, side); // Call the parent class constructor
}
}
In questo esempio, la classe Square
eredita dalla classe Rectangle
. La parola chiave super
viene utilizzata per chiamare il costruttore della classe padre.