Skip to content

Property flags and descriptors #196

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
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
148 changes: 74 additions & 74 deletions 1-js/07-object-properties/01-property-descriptors/article.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@

# Property flags and descriptors
# Attributi e descrittori di proprietà

As we know, objects can store properties.
Come già sappiamo, gli oggetti possono memorizzare proprietà.

Until now, a property was a simple "key-value" pair to us. But an object property is actually a more flexible and powerful thing.
Fino ad ora, per noi, una proprietà è sempre stata una coppia "chiave-valore". Ma in realtà, una proprietà è molto più potente e flessibile di cosi.

In this chapter we'll study additional configuration options, and in the next we'll see how to invisibly turn them into getter/setter functions.
In questo capitolo studieremo ulteriori opzioni di configurazione, e nel prossimo vedremo come trasformarle in funzioni getter/setter.

## Property flags
## Attributi di proprietà

Object properties, besides a **`value`**, have three special attributes (so-called "flags"):
Le proprietà degli oggetti, oltre ad un **`valore`**, possiedono tre attributi speciali (cosi detti "flags", o "bandiere"):

- **`writable`** -- if `true`, the value can be changed, otherwise it's read-only.
- **`enumerable`** -- if `true`, then listed in loops, otherwise not listed.
- **`configurable`** -- if `true`, the property can be deleted and these attributes can be modified, otherwise not.
- **`writable`** -- se impostato a `true`, il valore può essere modificato, altrimenti è possibile accedervi in sola lettura.
- **`enumerable`** -- se impostato a `true`, appare nei loop, altrimenti non verrà considerata.
- **`configurable`** -- se impostato a `true`, la proprietà può essere cancellata e questi attributi possono essere modificati.

We didn't see them yet, because generally they do not show up. When we create a property "the usual way", all of them are `true`. But we also can change them anytime.
Non li abbiamo mai visti fino ad ora, perché generalmente non vengono mostrati. Quando creiamo una proprietà in "modo ordinario", questi attributi vengono tutti impostati a `true`. Ma possiamo comunque modificarli in qualsiasi momento.

First, let's see how to get those flags.
Come prima cosa, vediamo come poter accedere a questi attributi.

The method [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) allows to query the *full* information about a property.
Il metodo [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) ritorna *tutte* le informazioni riguardo una proprietà.

The syntax is:
La sintassi:
```js
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
```

`obj`
: The object to get information from.
: L'oggetto da cui vogliamo ottenere le informazioni.

`propertyName`
: The name of the property.
: Il nome della proprietà.

The returned value is a so-called "property descriptor" object: it contains the value and all the flags.
Il valore ritornato viene chiamato "descrittore di proprietà" dell'oggetto: contiene il valore della proprietà e tutti i suoi attributi.

For instance:
Ad esempio:

```js run
let user = {
Expand All @@ -44,7 +44,7 @@ let user = {
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
/* descrittore di proprietà:
{
"value": "John",
"writable": true,
Expand All @@ -54,23 +54,23 @@ alert( JSON.stringify(descriptor, null, 2 ) );
*/
```

To change the flags, we can use [Object.defineProperty](mdn:js/Object/defineProperty).
Per modificare gli attributi possiamo utilizzare [Object.defineProperty](mdn:js/Object/defineProperty).

The syntax is:
La sintassi:

```js
Object.defineProperty(obj, propertyName, descriptor)
```

`obj`, `propertyName`
: The object and its property to apply the descriptor.
: L'oggetto e la proprietà a cui applicare il descrittore.

`descriptor`
: Property descriptor object to apply.
: Oggetto *descriptor* da utilizzare.

If the property exists, `defineProperty` updates its flags. Otherwise, it creates the property with the given value and flags; in that case, if a flag is not supplied, it is assumed `false`.
Se la proprietà esiste, `defineProperty` aggiornerà l'attributo. Altrimenti, creerà la proprietà con il valore e gli attributi forniti; se un attributo non viene fornito, gli verrà assegnato il valore `false`.

For instance, here a property `name` is created with all falsy flags:
Ad esempio, qui creiamo una proprietà `name` con tutti gli attributi `false`:

```js run
let user = {};
Expand All @@ -96,13 +96,13 @@ alert( JSON.stringify(descriptor, null, 2 ) );
*/
```

Compare it with "normally created" `user.name` above: now all flags are falsy. If that's not what we want then we'd better set them to `true` in `descriptor`.
Confrontandola con la proprietà "creata normalmente" `user.name` vista sopra, ora tutti gli attributi sono `false`. Se questo non è ciò che vogliamo, allora dovremmo impostarli a `true` tramite il `descriptor`.

Now let's see effects of the flags by example.
Ora analizziamo gli effetti degli attributi guardando alcuni esempi.

## Non-writable

Let's make `user.name` non-writable (can't be reassigned) by changing `writable` flag:
Vediamo come rendere `user.name` *non-writable* (la variabile non può essere riassegnata) modificando l'attributo `writable`:

```js run
let user = {
Expand All @@ -120,21 +120,21 @@ user.name = "Pete"; // Error: Cannot assign to read only property 'name'
*/!*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ci sarebbe il commento nel codice da tradurre, linea 119

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qui non ho tradotto perché suppongo (ma non sono sicuro) che sia il messaggio di errore che ti appare.. e ho paura che traducendolo perda di senso, visto che poi non apparirà mai in italiano... però non ci metterei la mano sul fuoco, smentitemi se sto dicendo una cavolata

```

Now no one can change the name of our user, unless they apply their own `defineProperty` to override ours.
Ora nessuno potrà modificare il nome dell'utente, a meno che non vada a sovrascrivere il valore degli attributi con `defineProperty`.

```smart header="Errors appear only in strict mode"
In the non-strict mode, no errors occur when writing to non-writable properties and such. But the operation still won't succeed. Flag-violating actions are just silently ignored in non-strict.
```smart header="Gli errori verranno mostrati solamente in strict mode"
Se non siamo in "strict mode", e tentiamo di sovrascrivere una proprietà non-writable, non verrà mostrato alcun errore. Nonostante non venga mostrato l'errore, l'operazione fallirà comunque. Quindi le violazioni di attributi fuori dalla strict mode verranno silenziosamente ignorate.
```

Here's the same example, but the property is created from scratch:
Qui vediamo lo stesso esempio, ma la proprietà viene creata dal nulla:

```js run
let user = { };

Object.defineProperty(user, "name", {
*!*
value: "John",
// for new properties we need to explicitly list what's true
// per le nuove proprietà dobbiamo esplicitare quali attributi sono true
enumerable: true,
configurable: true
*/!*
Expand All @@ -146,9 +146,9 @@ user.name = "Pete"; // Error

## Non-enumerable

Now let's add a custom `toString` to `user`.
Ora proviamo ad aggiungere un metodo `toString` ad `user`.

Normally, a built-in `toString` for objects is non-enumerable, it does not show up in `for..in`. But if we add a `toString` of our own, then by default it shows up in `for..in`, like this:
Normalmente, la funzione *built-in* (integrata) `toString` , per gli oggetti è non-enumerable, quindi non verrà mostrata nei cicli come `for..in`. Ma se proviamo ad aggiungere una nostra definizione di `toString`, allora questa verrà mostrata nei cicli `for..in`, come nell'esempio:

```js run
let user = {
Expand All @@ -158,11 +158,11 @@ let user = {
}
};

// By default, both our properties are listed:
// Di default, entrambe le proprietà verranno elencate
for (let key in user) alert(key); // name, toString
```

If we don't like it, then we can set `enumerable:false`. Then it won't appear in a `for..in` loop, just like the built-in one:
Se non è ciò che ci aspettiamo, possiamo impostare l'attributo `enumerable:false`. In questo modo non verrà più mostrata nei cicli `for..in`, proprio come la funzione già integrata (definita da Javascript):

```js run
let user = {
Expand All @@ -179,7 +179,7 @@ Object.defineProperty(user, "toString", {
});

*!*
// Now our toString disappears:
// In questo modo la nostra funzione toString sparirà
*/!*
for (let key in user) alert(key); // name
```
Expand All @@ -192,11 +192,11 @@ alert(Object.keys(user)); // name

## Non-configurable

The non-configurable flag (`configurable:false`) is sometimes preset for built-in objects and properties.
L'attributo non-configurable (`configurable:false`) è talvolta preimpostato negli oggetti e nelle proprietà integrate.

A non-configurable property can not be deleted.
Una proprietà *non-configurable* non può essere cancellata.

For instance, `Math.PI` is non-writable, non-enumerable and non-configurable:
Ad esempio, `Math.PI` è *non-writable*, *non-enumerable* e *non-configurable*:

```js run
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
Expand All @@ -211,25 +211,25 @@ alert( JSON.stringify(descriptor, null, 2 ) );
}
*/
```
So, a programmer is unable to change the value of `Math.PI` or overwrite it.
Quindi, uno sviluppatore non sarà in grado di cambiare il valore `Math.PI` o di sovrascriverlo.

```js run
Math.PI = 3; // Error

// delete Math.PI won't work either
// e nemmeno la cancellazione di Math.PI funzionerebbe
```

Making a property non-configurable is a one-way road. We cannot change it back with `defineProperty`.
Rendere una proprietà *non-configurable* è una "strada a senso unico". Non possiamo tornare indietro tramite `defineProperty`.

To be precise, non-configurability imposes several restrictions on `defineProperty`:
1. Can't change `configurable` flag.
2. Can't change `enumerable` flag.
3. Can't change `writable: false` to `true` (the other way round works).
4. Can't change `get/set` for an accessor property (but can assign them if absent).
Per essere precisi, l'attributo non-configurable impone diverse restrizioni a `defineProperty`:
1. Non possiamo modificare l'attributo `configurable`.
2. Non possiamo modificare l'attributo `enumerable`.
3. Non possiamo modificare l'attributo da `writable: false` a `true` (possiamo invece modificarlo da `true` a `false`).
4. Non possiamo modificare le funzioni di accesso `get/set` (ma possiamo assegnarle nel caso non siano definite).

**The idea of "configurable: false" is to prevent changes of property flags and its deletion, while allowing to change its value.**
**L'idea alla base di "configurable: false" è quella di prevenire la modifica e la rimozione degli attributi di una proprietà, permettendo comunque la modifica del suo valore.**

Here `user.name` is non-configurable, but we can still change it (as it's writable):
In questo esempio `user.name` è *non-configurable*, ma possiamo comunque modificarlo (poiché è *writable*):

```js run
let user = {
Expand All @@ -240,11 +240,11 @@ Object.defineProperty(user, "name", {
configurable: false
});

user.name = "Pete"; // works fine
user.name = "Pete"; // Funziona senza errori
delete user.name; // Error
```

And here we make `user.name` a "forever sealed" constant:
Qui invece "sigilliamo" per sempre `user.name` rendendolo un valore costante:

```js run
let user = {
Expand All @@ -256,8 +256,8 @@ Object.defineProperty(user, "name", {
configurable: false
});

// won't be able to change user.name or its flags
// all this won't work:
// non saremo in grado di modificare user.name o i suoi attribti
// nessuna delle seguenti istruzioni funzionerà
user.name = "Pete";
delete user.name;
Object.defineProperty(user, "name", { value: "Pete" });
Expand All @@ -266,9 +266,9 @@ Object.defineProperty(user, "name", { value: "Pete" });

## Object.defineProperties

There's a method [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) that allows to define many properties at once.
Utilizzando il metodo [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) abbiamo la possibilità di definire più proprietà alla volta.

The syntax is:
La sintassi è:

```js
Object.defineProperties(obj, {
Expand All @@ -278,7 +278,7 @@ Object.defineProperties(obj, {
});
```

For instance:
Ad esempio:

```js
Object.defineProperties(user, {
Expand All @@ -288,54 +288,54 @@ Object.defineProperties(user, {
});
```

So, we can set many properties at once.
In questo modo siamo in grado di impostare più proprietà in una volta sola.

## Object.getOwnPropertyDescriptors

To get all property descriptors at once, we can use the method [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors).
Per ottenere tutti i descrittori di una proprietà, possiamo utilizzare il metodo [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors).

Together with `Object.defineProperties` it can be used as a "flags-aware" way of cloning an object:
Il metodo `Object.defineProperties` può essere utilizzato per clonare un oggetto mantenendo gli attributi delle sue proprietà:

```js
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
```

Normally when we clone an object, we use an assignment to copy properties, like this:
Normalmente, quando cloniamo un oggetto, utilizziamo l'assegnazione per copiarne le proprietà, come nell'esempio:

```js
for (let key in user) {
clone[key] = user[key]
}
```

...But that does not copy flags. So if we want a "better" clone then `Object.defineProperties` is preferred.
...Ma in questo modo non stiamo copiando gli attributi. Quindi per una clonazione più completa, l'utilizzo di `Object.defineProperties` è la scelta migliore.

Another difference is that `for..in` ignores symbolic properties, but `Object.getOwnPropertyDescriptors` returns *all* property descriptors including symbolic ones.
Un'altra differenza è che `for..in` ignora le proprietà di tipo `symbol`, mentre `Object.getOwnPropertyDescriptors` ritorna *tutti* i descrittori, inclusi quelli di tipo symbol.

## Sealing an object globally
## Sigillare un oggetto globalmente

Property descriptors work at the level of individual properties.
I descrittori di proprietà permettono di lavorare a livello di proprietà.

There are also methods that limit access to the *whole* object:
Esistono però diversi metodi in grado di limitare l'accesso *all'intero* oggetto:

[Object.preventExtensions(obj)](mdn:js/Object/preventExtensions)
: Forbids the addition of new properties to the object.
: Vieta di aggiungere nuove proprietà all'oggetto.

[Object.seal(obj)](mdn:js/Object/seal)
: Forbids adding/removing of properties. Sets `configurable: false` for all existing properties.
: Vieta di aggiungere/rimuovere proprietà, ed imposta `configurable: false` su tutte le proprietà già esistenti dell'oggetto.

[Object.freeze(obj)](mdn:js/Object/freeze)
: Forbids adding/removing/changing of properties. Sets `configurable: false, writable: false` for all existing properties.
: Vieta di aggiungere/rimuovere/modificare le proprietà dell'oggetto. Imposta `configurable: false, writable: false` su tutte le proprietà già esistenti dell'oggetto.

And also there are tests for them:
Ed esistono anche dei metodi per verificare lo stato degli attributi di un oggetto:

[Object.isExtensible(obj)](mdn:js/Object/isExtensible)
: Returns `false` if adding properties is forbidden, otherwise `true`.
: Ritorna `false` se è vietato aggiungere nuove proprietà, altrimenti ritorna `true`.

[Object.isSealed(obj)](mdn:js/Object/isSealed)
: Returns `true` if adding/removing properties is forbidden, and all existing properties have `configurable: false`.
: Ritorna `true` se è vietato aggiungere/rimuovere proprietà, e tutte le altre proprietà sono impostate a `configurable: false`.

[Object.isFrozen(obj)](mdn:js/Object/isFrozen)
: Returns `true` if adding/removing/changing properties is forbidden, and all current properties are `configurable: false, writable: false`.
: Ritorna `true` se è vietato aggiungere/rimuovere/modificare proprietà, e tutte le altre proprietà sono impostate a `configurable: false, writable: false`.

These methods are rarely used in practice.
In pratica, tuttavia, questi metodi sono utilizzati molto raramente.