Skip to content

Optional chaining '?.' #187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 78 additions & 78 deletions 1-js/04-object-basics/07-optional-chaining/article.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,98 @@

# Optional chaining '?.'
# Concatenamento opzionale '?.'

[recent browser="new"]

The optional chaining `?.` is a safe way to access nested object properties, even if an intermediate property doesn't exist.
Il concatenamento opzionale (optional chaining), `?.`, è un modo sicuro di accedere alle proprietà annidate di un oggetto, anche nel caso in cui una proprietà intermedia non dovesse esistere.

## The "non-existing property" problem
## Il problema della "proprietà inesistente"

If you've just started to read the tutorial and learn JavaScript, maybe the problem hasn't touched you yet, but it's quite common.
Se avete appena cominciato a leggere il tutorial e a imparare JavaScript, forse questo problema non lo avete ancora affrontato, ma è piuttosto comune.

As an example, let's say we have `user` objects that hold the information about our users.
Ad esempio, ipotizziamo di avere un oggetto `user`, in cui sono memorizzate le informazioni relative ai nostri utenti.

Most of our users have addresses in `user.address` property, with the street `user.address.street`, but some did not provide them.
La maggior parte dei nostri utenti possiedono l'indirizzo nella proprietà `user.address`, la via in `user.address.street`, ma qualcuno potrebbe non averle fornite.

In such case, when we attempt to get `user.address.street`, and the user happens to be without an address, we get an error:
In questo caso, quando proviamo ad accedere a `user.address.street`, e l'utente non possiede un indirizzo, otterremo un errore:

```js run
let user = {}; // a user without "address" property
let user = {}; // un utente senza la proprietà "address"

alert(user.address.street); // Error!
alert(user.address.street); // Errore!
```

That's the expected result. JavaScript works like this. As `user.address` is `undefined`, an attempt to get `user.address.street` fails with an error.
Questo è il risultato che ci si aspetta. JavaScript funziona in questo modo. Se `user.address` è `undefined`, un tentativo di accesso a `user.address.street` fallirà con un errore.

In many practical cases we'd prefer to get `undefined` instead of an error here (meaning "no street").
Nella maggior parte dei casi, preferiremmo avere `undefined` piuttosto di un errore (con il significato "nessuna via").

...And another example. In the web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element.
... Un altro esempio. Il metodo `document.querySelector('.elem')` ritorna un oggetto che corrisponde ad un elemento della pagina web, che ritorna `null` quando l'elemento non esite.

```js run
// document.querySelector('.elem') is null if there's no element
let html = document.querySelector('.elem').innerHTML; // error if it's null
// document.querySelector('.elem') è null se non esiste l'elemento
let html = document.querySelector('.elem').innerHTML; // errore se è null
```

Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result.
Di nuovo, se un elemente non esiste, otterremo un errore nel tentativo di accedere a `.innerHTML` di `null`. In alcuni casi, in cui l'assenza di un elemento è normale, vorremo evitare l'errore e accettare come risultato `html = null`.

How can we do this?
Come possiamo farlo?

The obvious solution would be to check the value using `if` or the conditional operator `?`, before accessing its property, like this:
La soluzione più ovvia sarebbe di controllare il valore utilizzando `if` o l'operatore condizionale `?`, prima di accedere alle proprietà, come nell'esempio:

```js
let user = {};

alert(user.address ? user.address.street : undefined);
```

It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code. For more deeply nested properties, that becomes a problem as more repetitions are required.
Funziona, nessun errore... Ma è poco elegante. Come potete vedere , `"user.address"` appare due volte nel codice. Per proprietà molto più annidate, potrebbe diventare un problema, in quanto saranno necessarie molte più ripetizioni.

E.g. let's try getting `user.address.street.name`.
Ad esempio, proviamo a recuperare il valore di `user.address.street.name`.

We need to check both `user.address` and `user.address.street`:
Dobbiamo verificare sia `user.address` che `user.address.street`:

```js
let user = {}; // user has no address
let user = {}; // l'utente non ha address

alert(user.address ? user.address.street ? user.address.street.name : null : null);
```

That's just awful, one may even have problems understanding such code.
Questo è semplicemente terribile, un codice del genere potrebbe essere difficile da comprendere.

Don't even care to, as there's a better way to write it, using the `&&` operator:
Ci sarebbe un modo migliore per riscriverlo, utilizzando l'operatore `&&`:

```js run
let user = {}; // user has no address
let user = {}; // l'utente non ha address

alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)
alert( user.address && user.address.street && user.address.street.name ); // undefined (nessune errore)
```

AND'ing the whole path to the property ensures that all components exist (if not, the evaluation stops), but also isn't ideal.
Concatenare con `&&` l'intero percorso verso la proprietà ci assicura che tutti i componenti esistano (in caso contrario, la valutazione si interrompe), ma non è comunque l'ideale.

As you can see, property names are still duplicated in the code. E.g. in the code above, `user.address` appears three times.
Come potete vedere, il nome della proprietà è ancora duplicato nel codice. Ad esempio, nel codice sopra, `user.address` è ripetuto tre volte.

That's why the optional chaining `?.` was added to the language. To solve this problem once and for all!
Questo è il motivo per cui la concatenazione opzionale `?.` è stata aggiunta al linguaggio. Per risolvere questo problema una volta per tutte!

## Optional chaining
## Concatenazione opzionale

The optional chaining `?.` stops the evaluation if the value before `?.` is `undefined` or `null` and returns `undefined`.
La concatenazione opzionale `?.` interrompe la valutazione se il valore prima di `?.` è `undefined` o `null`, e ritorna `undefined`.

**Further in this article, for brevity, we'll be saying that something "exists" if it's not `null` and not `undefined`.**
**D'ora in poi in questo articolo, per brevità, diremo che qualcosa "esiste" se non è ne `null` `undefined`.**

In other words, `value?.prop`:
- works as `value.prop`, if `value` exists,
- otherwise (when `value` is `undefined/null`) it returns `undefined`.
In altre parole, `value?.prop`:
- funziona come `value.prop`, se `value` esiste,
- altrimenti (quando `value` è `undefined/null`) ritorna `undefined`.

Here's the safe way to access `user.address.street` using `?.`:
Vediamo un modo sicuro per accedere a `user.address.street` utilizzando `?.`:

```js run
let user = {}; // user has no address
let user = {}; // user non possiede l'address

alert( user?.address?.street ); // undefined (no error)
alert( user?.address?.street ); // undefined (nessun errore)
```

The code is short and clean, there's no duplication at all.
Il codice è corto e pulito, non c'è alcuna duplicazione.

Reading the address with `user?.address` works even if `user` object doesn't exist:
Leggendo l'indirizzo con `user?.address` funzionerebbe anche se l'oggetto `user` non esistesse:

```js run
let user = null;
Expand All @@ -101,52 +101,52 @@ alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
```

Please note: the `?.` syntax makes optional the value before it, but not any further.
Da notare: la sintassi `?.` rende opzionale il valore che la precede, nulla di più.

E.g. in `user?.address.street.name` the `?.` allows `user` to safely be `null/undefined` (and returns `undefined` in that case), but that's only for `user`. Further properties are accessed in a regular way. If we want some of them to be optional, then we'll need to replace more `.` with `?.`.
Ad esempio in `user?.address.street.name` il costrutto `?.` permette alla proprietà `user` di essere `null/undefined` in sicurezza (e ritornare `undefined` in questo caso), ma questo vale solamente per `user`. Si accederà alle altre proprietà normalmente. Se vogliamo che anche altre proprietà siano opzionali, dobbiamo rimpiazzare `.` con `?.`.

```warn header="Don't overuse the optional chaining"
We should use `?.` only where it's ok that something doesn't exist.
```warn header="Non abusate della concatenazione opzionale"
Dovremmo utilizzare `?.` solamente quando va bene che una proprietà possa non esistere.

For example, if according to our coding logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`.
Ad esempio, considerando la logica del nostro codice, l'oggetto `user` deve necessariamente esistere, mentre `address` è opzionale, quindi dovremmo scrivere `user.address?.street`, non `user?.address?.street`.

So, if `user` happens to be undefined due to a mistake, we'll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
Quindi, se `user` dovesse essere `undefined` per errore, otterremo un errore e potremmo sistemarlo. Altrimenti, gli errori di programmazione potrebbero essere silenziati in modo non appropriato, rendendo il debug molto difficile.
```

````warn header="The variable before `?.` must be declared"
If there's no variable `user` at all, then `user?.anything` triggers an error:
````warn header="La variabile che precede `?.` deve essere dichiarata"
Se non esiste alcuna variabile `user`, allora `user?.anything` provocherà un errore:

```js run
// ReferenceError: user is not defined
user?.address;
```
The variable must be declared (e.g. `let/const/var user` or as a function parameter). The optional chaining works only for declared variables.
La variabile deve essere dichiarata (ad esempio come `let/const/var user` o come parametro di funzione). La concatenazione opzionale funziona solamente con le variabili dichiarate.
````

## Short-circuiting
## Corto circuito

As it was said before, the `?.` immediately stops ("short-circuits") the evaluation if the left part doesn't exist.
Come detto in precedenza, il costrutto `?.` interrompe immediatamente (manda in "corto circuito") la valutazione se la proprietà a destra non esiste.

So, if there are any further function calls or side effects, they don't occur.
Quindi, nel caso ci siano ulteriori chiamate a funzione o side-effects, questi non verranno elaborati.

For instance:
Ad esempio:

```js run
let user = null;
let x = 0;

user?.sayHi(x++); // no "sayHi", so the execution doesn't reach x++
user?.sayHi(x++); // non esiste "sayHi", quindi l'esecuzione non raggiungerà x++

alert(x); // 0, value not incremented
alert(x); // 0, valore non incrementato
```

## Other variants: ?.(), ?.[]
## Altre varianti: ?.(), ?.[]

The optional chaining `?.` is not an operator, but a special syntax construct, that also works with functions and square brackets.
La concatenazione opzionale `?.` non è un operatore, ma uno speciale costrutto sintattico, che funziona anche con le funzioni e le parentesi quadre.

For example, `?.()` is used to call a function that may not exist.
Ad esempio, `?.()` viene utilizzato per invocare una funzione che potrebbe non esistere.

In the code below, some of our users have `admin` method, and some don't:
Nel codice sotto, alcuni dei nostri utenti possiedono il metodo `admin`, mentre altri no:

```js run
let userAdmin = {
Expand All @@ -162,15 +162,15 @@ userAdmin.admin?.(); // I am admin
*/!*

*!*
userGuest.admin?.(); // nothing (no such method)
userGuest.admin?.(); // niente (il metodo non esiste)
*/!*
```

Here, in both lines we first use the dot (`user1.admin`) to get `admin` property, because the user object must exist, so it's safe read from it.
Qui, in entrambe le righe, come prima cosa abbiamo utilizzato il punto (`user1.admin`) per ottenere la proprietà `admin`, poiché l'oggetto `user` deve necessariamente esistere, quindi l'accesso è sicuro.

Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `user1`). Otherwise (for `user2`) the evaluation stops without errors.
Successivamente `?.()` controlla la parte sinistra: se la funzione `admin` esiste, allora viene eseguita (ciò che accade con `user1`). Altrimenti (con `user2`) la valutazione si interrompe senza errori.

The `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist.
La sintassi `?.` funziona anche con le parentesi `[]` (invece del punto `.`). Come nei casi precedenti, possiamo accedere con sicurezza ad una proprietà di un oggetto che potrebbe non esistere.

```js run
let key = "firstName";
Expand All @@ -185,36 +185,36 @@ alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
```

Also we can use `?.` with `delete`:
Possiamo anche utilizzare `?.` con `delete`:

```js run
delete user?.name; // delete user.name if user exists
delete user?.name; // cancella user.name se l'utente esiste
```

````warn header="We can use `?.` for safe reading and deleting, but not writing"
The optional chaining `?.` has no use at the left side of an assignment.
````warn header="Possiamo utilizzare `?.` per l'accesso e la rimozione sicura, ma non per la scrittura"
La concatenazione opzionale `?.` non ha alcun significato alla sinistra di un'assegnazione.

For example:
Ad esempio:
```js run
let user = null;

user?.name = "John"; // Error, doesn't work
// because it evaluates to undefined = "John"
user?.name = "John"; // Errore, non funziona
// poiché valuta undefined = "John"
```

It's just not that smart.
Non è cosi intelligente.
````

## Summary
## Riepilogo

The optional chaining `?.` syntax has three forms:
La concatenazione opzionale `?.` ha tre forme:

1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`.
2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`.
3. `obj.method?.()` -- calls `obj.method()` if `obj.method` exists, otherwise returns `undefined`.
1. `obj?.prop` -- ritorna `obj.prop` se `obj` esiste, altrimenti ritorna `undefined`.
2. `obj?.[prop]` -- ritorna `obj[prop]` se `obj` esiste, altrimenti ritorna `undefined`.
3. `obj.method?.()` -- invoca `obj.method()` se `obj.method` esiste, altrimenti ritorna `undefined`.

As we can see, all of them are straightforward and simple to use. The `?.` checks the left part for `null/undefined` and allows the evaluation to proceed if it's not so.
Come possiamo vedere, le tre forme sono semplici da utilizzare. Il costrutto `?.` verifica che la parte sinistra non sia `null/undefined`; se non lo è, permette alla valutazione di proseguire, altrimenti interrompe immediatamente la valutazione.

A chain of `?.` allows to safely access nested properties.
La concatenazione di `?.` permette di accedere in sicurezza a proprietà annidate.

Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't exist. So that it won't hide programming errors from us, if they occur.
In ogni caso, dovremmo applicare `?.` con prudenza, solamente nei casi in cui è accettabile che la parte sinistra possa non esistere. In questo modo evitiamo di nascondere errori di programmazione, nel caso ce ne siano.