Gli oggetti solitamente vengono creati per rappresentare entità del mondo reale, come utenti, prodotti e molto altro:
let user = {
name: "John",
age: 30
};
Inoltre, nel mondo reale, un utente può agire: selezionare qualcosa dalla lista degli acquisti, effettuare login, logout etc.
In JavaScript le azioni vengono rappresentate tramite le funzioni.
Esempio di un metodo
Per iniziare, insegniamo a user a salutare:
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("Hello!");
};
user.sayHi(); // Hello!
Qui abbiamo appena utilizzato unâespressione di funzione per creare una funzione ed assegnarla alla proprietà user.sayHi dellâoggetto.
Successivamente possiamo chiamarla. Ora lâutente può parlare!
Una funzione che è una proprietà di un oggetto si chiama metodo.
Quindi, nellâesempio abbiamo un metodo sayHi dellâoggetto user.
Ovviamente possiamo utilizzare una funzione già dichiarata come metodo:
let user = {
// ...
};
// prima la dichiariamo
function sayHi() {
alert("Hello!");
};
// poi la aggiungiamo come metodo
user.sayHi = sayHi;
user.sayHi(); // Hello!
Quando scriviamo codice utilizzando gli oggetti per rappresentare le entità , questa viene definita programmazione orientata agli oggetti, in breve: âOOPâ.
OOP è una grande cosa, un ambito di interesse con i propri studi. Come scegliere le giuste entità ? Come organizzare le interazioni tra loro? Questa è lâarchitettura di un codice, e ci sono molti libri importanti che trattano questo argomento, come âDesign Patterns: Elements of Reusable Object-Oriented Softwareâ di E.Gamma, R.Helm, R.Johnson, J.Vissides oppure âObject-Oriented Analysis and Design with Applicationsâ di G.Booch, e molti altri.
La forma breve dei metodi
Esiste una sintassi più breve per i metodi in un oggetto letterale:
// questi oggetti fanno la stessa cosa
user = {
sayHi: function() {
alert("Hello");
}
};
// la sintassi più breve risulta più carina
user = {
sayHi() { // equivalente a "sayHi: function(){...}"
alert("Hello");
}
};
Come possiamo notare, si può omettere "function" e scrivere solamente sayHi().
A dire la verità , la notazione non è proprio uguale. Ci sono delle sottili differenze legate allâereditarietà degli oggetti (le studieremo più avanti), ma per ora non hanno importanza. Nella maggior parte dei casi la forma breve viene preferita.
âthisâ nei metodi
Eâ molto comune che, per eseguire determinate azioni, un metodo abbia necessità di accedere alle informazioni memorizzate nellâoggetto.
Ad esempio, il codice dentro user.sayHi() potrebbe aver bisogno del nome dellâuser.
Per accedere allâoggetto, un metodo può utilizzare la parola chiave this.
Il valore di this è lâoggetto âprima del puntoâ, quello che ha eseguito la chiamata del metodo.
Ad esempio:
let user = {
name: "John",
age: 30,
sayHi() {
// "this" is the "current object"
alert(this.name);
}
};
user.sayHi(); // John
In fase di esecuzione, quando viene chiamato il metodo user.sayHi(), il valore di this sarà user.
Tecnicamente, è possibile accedere allâoggetto anche senza this; lo si fa tramite riferimento alla variabile esterna:
let user = {
name: "John",
age: 30,
sayHi() {
alert(user.name); // "user" piuttosto di "this"
}
};
â¦Questo codice è instabile. Se decidessimo di copiare user in unâaltra variabile, ad esempio admin = user e sovrascrivere user con qualcosâaltro, verrebbe allora effettuato lâaccesso allâoggetto sbagliato.
Dimostriamolo:
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name ); // porta ad un errore
}
};
let admin = user;
user = null; // sovrascriviamo per rendere tutto più ovvio
admin.sayHi(); // Errore: non possiamo leggere la proprietà 'name' di null
Se scriviamo this.name piuttosto di user.name allâinterno di alert, il codice funzionerà .
âthisâ non ha limiti
In JavaScript, la parola chiave âthisâ si comporta diversamente da come fa in molti altri linguaggi di programmazione. Essa può essere usata in qualsiasi funzione, anche se non si tratta del metodo di un oggetto.
Non câè alcun errore di sintassi in un codice come questo:
function sayHi() {
alert( this.name );
}
Il valore di this viene valutato al momento dellâesecuzione. E può essere un valore qualsiasi.
Ad esempio, la stessa funzione potrebbe avere diversi âthisâ quando viene chiamata da oggetti diversi:
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// utilizziamo la stessa funzione su due oggetti
user.f = sayHi;
admin.f = sayHi;
// queste chiamate hanno un this diverso
// "this" all'interno della funzione è riferito all'oggetto "prima del punto"
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (il punto o le parentesi quadre forniscono entrambi accesso ai metodi - non c'è differenza)
La regola è semplice: se viene chiamato obj.f(), allora, durante la chiamata di f, this si riferisce a obj. Nellâesempio sopra assume il valore sia di user che di admin.
this == undefinedPossiamo anche chiamare la funzione senza un oggetto:
function sayHi() {
alert(this);
}
sayHi(); // undefined
In questo caso this è undefined in modalità strict. Se tentiamo di accedere a this.name, ci sarà un errore.
Se non è attiva la modalità strict il valore di this in questo caso sarà lâoggetto globale (window in un browser, lo studieremo più avanti nel capitolo Oggetto globale). Questo strano comportamento ha delle motivazioni storiche, che "use strict" risolve.
Solitamente questo tipo di chiamate significano un errore di programmazione. Se câè un this allâinterno di una funzione, ci si aspetta che sia chiamato da un oggetto.
thisSe avete utilizzato altri linguaggi di programmazione, probabilmente sarete abituati allâidea di un âthis limitatoâ: quando viene definito un metodo in un oggetto, questo avrà sempre in this il riferimento allâoggetto.
In JavaScript this è âliberoâ, il suo valore viene calcolato durante lâesecuzione e non dipende da dove il metodo è stato definito, ma piuttosto dallâoggetto âprima del puntoâ.
Il concetto di valutare this durante lâesecuzione ha i suoi pregi e difetti. Da una parte una funzione può essere riutilizzata per oggetti diversi, dallâaltra questa grande flessibilità può essere fonte di molti errori.
Il nostro scopo non è di giudicare se questa caratteristica del linguaggio sia buona o cattiva, ma di capire come lavorare con essa sfruttandone i benefici ed evitando i problemi.
Le arrow functions non hanno âthisâ
Ad esempio, qui arrow() usa this preso dal metodo esterno user.sayHi():
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya
Questa è una speciale caratteristica delle arrow functions; è utile quando non vogliamo avere un ulteriore this, ma utilizzare quello del contesto esterno. Più avanti nel capitolo Arrow functions rivisitate studieremo più in dettaglio le arrow functions.
Riepilogo
- Le funzioni che vengono memorizzate come proprietà di un oggetto vengono dette âmetodiâ.
- I metodi consentono agli oggetti di âagireâ, come
object.doSomething(). - I metodi possono riferirsi allâoggetto tramite
this.
Il valore this viene definito durante lâesecuzione (run-time).
- Quando una funzione viene dichiarata, può utilizzare
this, ma questothisnon avrà alcun valore fino a che la funzione non verrà chiamata. - Una funzione può essere copiata in vari oggetti.
- Quando una funzione viene chiamata come âmetodoâ:
object.method(), il valore dithisdurante la chiamata si riferisce aobject.
Da notare che le arrow functions sono speciali: non hanno this. Quando si prova ad accedere a this in una funzione freccia, questo verrà preso dal contesto esterno.
Commenti
<code>, per molte righe â includile nel tag<pre>, per più di 10 righe â utilizza una sandbox (plnkr, jsbin, codepenâ¦)