Skip to content
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

Property flags and descriptors #219

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
164 changes: 82 additions & 82 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
# Sinalizadores e descritores de propriedade
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

As we know, objects can store properties.
Como sabemos, objetos podem armazenar propriedades.

Until now, a property was a simple "key-value" pair to us. But an object property is actually a more flexible and powerful thing.
Até agora, para nós, uma propriedade era um simples par "chave-valor". Mas uma propriedade de objeto é na verdade uma coisa mais flexível e poderosa.

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.
Neste capítulo, nós vamos estudar opções de configuração adicional, e no próximo nós vamos ver como invisivelmente tornar elas em funções getter/setter.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

## Property flags
## Sinalizadores de propriedade

Object properties, besides a **`value`**, have three special attributes (so-called "flags"):
Propriedades de objeto, além do **`valor`** tem três atributos especiais (também chamados "sinalizadores"):
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

- **`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.
- **`gravável`** -- se `true`, o valor pode ser alterado, caso contrário, é apenas-leitura.
- **`enúmeravel`** -- se `true`, então pode ser listado em loops, caso contrário, não pode.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
- **`configurável`** -- se `true`, a propriedade pode ser deletada e seus atributos modificados, caso contrário não.

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.
Nós não vimos eles ainda, porque geralmente eles não aparecem. Quando criamos uma propriedade "do jeito comum", todos eles são `true`. Mas nós também podemos mudá-los a qualquer hora.

First, let's see how to get those flags.
Primeiro, vamos ver como obter esses sinalizadores.

The method [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) allows to query the *full* information about a property.
O método [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) nos permite consultar a informação *completa* sobre a propriedade.

The syntax is:
A sintaxe é:
```js
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
```

`obj`
: The object to get information from.
: O objeto do qual vamos obter a informação.

`propertyName`
: The name of the property.
: O nome da propriedade.

The returned value is a so-called "property descriptor" object: it contains the value and all the flags.
O valor retornado é também chamado de objeto "descritor de propriedade": ele contém o valor e todos os sinalizadores.

For instance:
Por exemplo:

```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:
/* descritor de propriedade:
{
"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).
Para mudar os sinalizadores, nós podemos usar o [Object.defineProperty](mdn:js/Object/defineProperty).

The syntax is:
A sintaxe é:

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

`obj`, `propertyName`
: The object and property to work on.
: O objeto e a propriedade nos quais atuar.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

`descriptor`
: Property descriptor object to apply.
: Descritor de propriedade de objeto a aplicar.

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 a proprieade existe, `defineProperty` atualiza seu sinalizador. Caso contrário, é criada uma propriedade com os sinalizadores setados; neste caso, se um sinalizador não é enviado, seu valor é assumido como `false`.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

For instance, here a property `name` is created with all falsy flags:
Por exemplo, aqui a propriedade `name` é criada com todos os sinalizadores falsos:
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

```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`.
Compare isso com o `user.name` "criado normalmente" acima: agora todos os sinalizadores são falsos. Se não é isso que queremos, então é melhor setá-los como `true` no `descriptor`.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

Now let's see effects of the flags by example.
Agora vamos ver os efeitos dos sinalizadores, por exemplo:

## Non-writable
## Não-gravável

Let's make `user.name` non-writable (can't be reassigned) by changing `writable` flag:
Vamos deixar `user.name` não-gravável (não pode ser reatribuído) alterando o sinalizador `writable`:

```js run
let user = {
Expand All @@ -120,36 +120,36 @@ user.name = "Pete"; // Error: Cannot assign to read only property 'name'...
*/!*
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
```

Now no one can change the name of our user, unless they apply their own `defineProperty` to override ours.
Agora, ninguém pode alterar o nome do nosso usuário, a não ser que eles apliquem seus próprios `defineProperty` para sobrescrever o nosso.

```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="Erros aparecem apenas em strict mode"
No modo não-estrito, os erros não ocorrem quando gravando em propriedades não-graváveis, etc. Mas a operação ainda não terá sucesso. Ações que violam os sinalizadores são apenas ignoradas silenciosamentes em modo não-estrito.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
```

Here's the same example, but the property is created from scratch:
Aqui está o mesmo exemplo, mas a propriedade é criada do zero.

```js run
let user = { };

Object.defineProperty(user, "name", {
*!*
value: "John",
// for new properties we need to explicitly list what's true
// para novas proprieades, precisamos explicitamente listar o que é true
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
enumerable: true,
configurable: true
*/!*
});

alert(user.name); // Pete
user.name = "Alice"; // Error
alert(user.name); // John
user.name = "Alice"; // Erro
```


## Non-enumerable
## Não-enumerável

Now let's add a custom `toString` to `user`.
Agora, vamos adicionar um `toString` customizado ao `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, um `toString` embutido em objetos é não-enumerável, e não aparece em `for...in`. Mas se nós adicionarmos um `toString` por nós mesmos, então por padrão ele aparece em `for...in`, desta forma:
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

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

// By default, both our properties are listed:
// Por padrão, ambas as nossas propriedades são listadas:
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 nós não gostarmos disso, então podemos setar `enumerable:false`. Então ela não vai aparecer no loop `for...in`, assim como as propriedades embutidas:
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

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

*!*
// Now our toString disappears:
// Agora nosso toString desaparece:
*/!*
for (let key in user) alert(key); // name
```

Non-enumerable properties are also excluded from `Object.keys`:
Propriedades não-enumeráveis também são excluídas de `Object.keys`:

```js
alert(Object.keys(user)); // name
```

## Non-configurable
## Não-configurável

The non-configurable flag (`configurable:false`) is sometimes preset for built-in objects and properties.
O sinalizador não-configurável (`configurable:false`) algumas vezes é predefinido por objetos e propriedades embutidas.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

A non-configurable property can not be deleted.
Uma propriedade não-configurável não pode ser deletada.

For instance, `Math.PI` is non-writable, non-enumerable and non-configurable:
Por exemplo, `Math.PI` é não-gravável, não-enumerável e não-configurável:

```js run
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
Expand All @@ -212,25 +212,25 @@ alert( JSON.stringify(descriptor, null, 2 ) );
}
*/
```
So, a programmer is unable to change the value of `Math.PI` or overwrite it.
Então, um programador é impossibilitado de mudar o valor de `Math.PI` ou sobrescrevê-lo.

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

// delete Math.PI won't work either
// deletar Math.PI também não irá funcionar
```

Making a property non-configurable is a one-way road. We cannot change it back with `defineProperty`.
Deixar uma propriedade não-configurável, é um caminho só de ida. Nós não podemos alterar isso novamente com `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).
Para ser preciso, não-configurabilidade impões várias restrições em `defineProperty`:
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
1. Não poder mudar o sinalizador `configurable`.
1. Não poder mudar o sinalizador `enumerable`.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
3. Não poder mudar `writable: false` para `true` (o contrário funciona).
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
4. Não poder mudar `get/set` por um acessador de propriedade (mas pode atribuí-los se ausente).
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

**The idea of "configurable: false" is to prevent changes of property flags and its deletion, while allowing to change its value.**
**A ideia de "configurable: false" é para prevenir mudanças de sinalizadores de propriedades e sua eliminação, enquanto permite alterar seu valor.**
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

Here `user.name` is non-configurable, but we can still change it (as it's writable):
Aqui `user.name` é não-configurável, mas nós ainda podemos alterá-lo (pois é gravável):

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

user.name = "Pete"; // works fine
delete user.name; // Error
user.name = "Pete"; // funciona corretamente
delete user.name; // Erro
```

And here we make `user.name` a "forever sealed" constant:
E aqui nós deixamos `user.name` uma constante "selada para sempre":

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

// won't be able to change user.name or its flags
// all this won't work:
// não será possível alterar user.name ou seus sinalizadores
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
// nada disso irá funcionar
user.name = "Pete";
delete user.name;
Object.defineProperty(user, "name", { value: "Pete" });
Expand All @@ -267,9 +267,9 @@ Object.defineProperty(user, "name", { value: "Pete" });

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
```smart header="A única mudança de atributo possivel: writable true -> false"
Existe uma pequena exceção sobre alterar sinalizadores.
Nós podemos mudar `writable: true` para `false` para uma propriedade não-configurável, e assim prevenir a modificação do seu valor (adicionando uma outra camada de proteção). Mas não o inverso.
```

## Object.defineProperties

There's a method [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) that allows to define many properties at once.
Existe um método [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) que permite definir várias propriedades de uma vez.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

The syntax is:
A sintaxe é:

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

For instance:
Por exemplo:

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

So, we can set many properties at once.
Então, nós podemos setar várias propriedades de uma vez.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

## Object.getOwnPropertyDescriptors

To get all property descriptors at once, we can use the method [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors).
Para obter todos os sinalizadores de propriedade de uma vez, nós podemos usar o método [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors).
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

Together with `Object.defineProperties` it can be used as a "flags-aware" way of cloning an object:
Juntamente com `Object.defineProperties` isso pode ser usado como um jeito "consciente-de-sinalizadores" de clonar objetos:
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

```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 nós clonamos um objeto, nós usamos uma atribuição para copiar propriedades, desta forma:

```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.
...Mas isso não copia os sinalizadores. Então se nós quisermos um clone "melhor" então é preferível `Object.defineProperties`.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

Another difference is that `for..in` ignores symbolic properties, but `Object.getOwnPropertyDescriptors` returns *all* property descriptors including symbolic ones.
Outra diferença é que `for..in` ignora propriedades simbólicas, mas `Object.getOwnPropertyDescriptors` returna *todas* as propriedades descritoras, incluindo as simbólicas.

## Sealing an object globally
## Selando um objeto globalmente

Property descriptors work at the level of individual properties.
Descritores de propriedade atuam no mesmo nível de propriedades individuais.

There are also methods that limit access to the *whole* object:
Também existem métodos que limitam o acesso ao objeto *inteiro*:

[Object.preventExtensions(obj)](mdn:js/Object/preventExtensions)
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
: Forbids the addition of new properties to the object.
: Proíbe a adição de novas propriedades ao objeto.

[Object.seal(obj)](mdn:js/Object/seal)
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved
: Forbids adding/removing of properties. Sets `configurable: false` for all existing properties.
: Proíbe a adição/remoção de propriedades. Seta `configurable: false` para todas as propriedades existentes.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

[Object.freeze(obj)](mdn:js/Object/freeze)
: Forbids adding/removing/changing of properties. Sets `configurable: false, writable: false` for all existing properties.
: Proíbe adicionar/remover/alterar propriedades. Seta `configurable: false, writable: false` para todas as propriedades existentes.
herbertpdl marked this conversation as resolved.
Show resolved Hide resolved

And also there are tests for them:
E também existem testes para eles:

[Object.isExtensible(obj)](mdn:js/Object/isExtensible)
: Returns `false` if adding properties is forbidden, otherwise `true`.
: Retorna `false` se a adição de propriedades é proibida, caso contrátio `true`.

[Object.isSealed(obj)](mdn:js/Object/isSealed)
: Returns `true` if adding/removing properties is forbidden, and all existing properties have `configurable: false`.
: Retorna `true` se adição/remoção de propriedades são proibidas, e todas as propriedades existentes são `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`.
: Retorna `true` se adição/remoção/alteração de propriedades são proibidas, e todas as propriedades atuais são `configurable: false, writable: false`.

These methods are rarely used in practice.
Estes métodos são raramentes usados na prática.