Definição vs assinatura de objeto
Este artigo foi retirado de 2ality com o propósito de estudo. Ler, escrever e reescrever é a maneira que escolhi para aprender coisas novas e poder consultar no futuro.
Todos os créditos ao Dr. Axel Rauschmayer
Definição (definition) e assinatura (assignment) de objeto são coisas diferentes. As diferenças são sutis, mas elas existem.
Definição de objeto usa uma função pra definir uma nova propriedade e é mais verboso:
Object.defineProperty(user, "name", descriptors);
O maior benefício dessa abordagem é que você pode passar descritores, que permite configurar o comportamento da propriedada que está sendo adicionada. Ele pode ser usado para criar ou modificar um objeto.
Assinatura de objeto é o mais comum:
user.name = "John";
A ideia do assignment é mudar um valor. Se existir um setter na propriedade name
em user
, ou no seu protótipo, ele será invocado. Caso contrário, o assignment vai acabar criando uma nova propriedade se ela não existir, com valores padrão.
Entendendo os atributos das propriedades
Em JavaScript, existem três tipos de propriedades:
- Named accessor properties: Existem graças a um getter ou setter.
- Named data properties: Uma propriedade que contém um valor. São as propriedades mais comuns. Elas pode conter um método.
- Internal properties: Usadas internamente pelo JavaScript.
Atributos das propriedades
Cada propriedade possui atributos específicos (campos), e esses campos mudarão o modo como a propriedade funciona.
- Todas as propriedades:
[[Enumerable]]
: Se a propriedade é enumerável, ela permite operações comofor...in
eObject.keys()
.[[Configurable]]
: Se a propriedade é configurável, então todos os seus atributos podem ser alterados. Caso contrário, apenas o atributo[[Value]]
pode ser mudado por definição (definition).
- Named data properties:
[[Value]]
: O valor da propriedade.[[Writable]]
: Determina se o valor pode ser alterado.
- Names accessor properties:
[[Get]]
: Contém um método get (getter).[[Set]]
: Contém um método set (setter).
Descritores da propriedade
Eles são um conjunto de atributos de propriedades, conforme mencionado acima, que compõe um descritor de objeto. Por exemplo:
{
value: 123,
writable: false
}
Eles são usados por métodos, como Object.defineProperty
, Object.getOwnPropertyDescriptor
e Object.create
.
Case existam propriedades faltando em um descritor, elas irão assumir os valores padrão:
Propriedade | Valor Padrão |
---|---|
value | undefined |
get | undefined |
set | undefined |
writable | false |
enumerable | false |
configurable | false |
Propriedades internas
Dentre outras, essas são algumas das propriedades internas:
[[Prototype]]
: O protótipo de um objeto.[[Extensible]]
: Se o objeto é extensível - se novas propriedades podem ser adicionadas a ele.[[DefineOwnProperty]]
: Método pra definir uma propriedades.[[Put]]
: Método para assinar/alterar uma propriedade.
O que realmente acontece na definition e assingment
Definição de uma propriedade é gerenciada internamente pelo [[DefineOwnProperty]](P, Desc, Throw)
. Essa função interna é chamada quando Object.defineProperty
ou Object.defineProperties
são executados.
P
é o nome de uma propriedade. Throw
especifica como a operação deve rejeitar uma mudança: Se Throw
é true
, então uma exceção é lançada, caso contrário, a operação é silenciosamente abortada. Quando [[DefineOwnProperty]]
é chamado:
- Se o objeto não é extensível, rejeita.
- Caso contrário, se a propriedade não existe, cria uma nova.
- Caso a propriedade exista e o objeto não seja configurável, ele será rejeitado se outra propriedade diferente de
[[Value]]
for alterada. - Caso contrário, a propriedade existente é configurável, e pode ser alterada.
Assinatura de uma propriedade é gerenciada pelo método interno [[Put]](P, Value, Throw)
. Ele é chamado em assingments (assinaturas), como:
user.name = "John";
P
e Throw
funcionam da mesma forma como no [[DefineOwnProperty]]
. Quando [[Put]]
é chamado, acontece o seguinte:
- Se
P
for read-only em algum lugar na cadeia do protótipo, a operação é rejeitada. - Se existir um setter associado a
P
em algum lugar na cadeia do protótipo, ele será chamado. - Se existe propriedade com o nome
P
e o objeto não é extensível, então a operação é rejeitada. - Caso contrário, uma nova propriedade é criada com o mesmo mecanismo anterior de
Object.defineProperty
, com os descritores:
this[[DefineOwnProperty]](
P,
{
value: Value,
writable: true,
enumerable: true,
configurable: true,
},
Throw
);
- Caso a propriedade exista e seja
writable
, isso será invocado:
this[[DefineOwnProperty]](
P,
{
value: Value,
},
Throw
);
Exemplos
Dê uma olhada em alguns exemplos que explicam como definição (definition) e assinatura (assignment) funcionam.
Propriedades read-only não previnem definição
"use strict";
const obj = {};
Object.defineProperty(obj, "foo", {
value: "a",
writable: false, // read-only
configurable: true,
});
Assinatura: resulta em exceção:
obj.foo = 'b';
TypeError: obj.foo is read-only
Definição: cria uma nova propriedade exclusiva:
Object.defineProperty(obj, "foo", {
value: "b",
});
obj.foo; // 'b'
Operator de assinatura não altera propriedades nos protótipos
Uma vez que o protótipo é compartilhado com seus descendentes, quando um deles decide alterar uma propriedade, uma nova propriedade local é criada e o protótipo não é afetado.
let proto = { foo: "a" };
let obj = Object.create(proto);
obj.foo = "b";
obj.foo; // 'b'
proto.foo; // 'a'
Apenas definição permite customização dos atributes de propriedade
Uma vez que assinatura possui atributos de propriedade padrão, se você quiser criar atributos específicos, precisará usar definição.
As propriedades de um objeto literal são adicionadas por definição
Essa sentença:
let obj = {
foo: "a",
};
É internamente traduzida para:
let obj = new Object();
obj.foo = "a";
Que é equivalente a:
let obj = new Object();
Object.defineProperties(obj, {
foo: {
value: "a",
enumerable: true,
configurable: true,
writable: true,
},
});
Conclusão
Assinatura (assignment) é a melhor escolha para a maioria dos casos. Eu mesmo não consigo lembrar quando precisei utilizar definição. Assinatura é mais fácil de ler e escrever e também possui melhor performance, uma vez que definição (definition) é custoso. No entanto, é bom saber as diferenças e nuances de cada um. Caso você precise exercer mais controle sobre uma propriedade, por exemplo, assinatura pode não ser suficiente e definição será a melhor escolha.