Descritores de propriedade

É provável que a maior parte da sua interação com as propriedades do objeto seja no nível da superfície, incluindo a criação de literais de objetos e a configuração e o acesso usando chaves. No entanto, é possível configurar internamente qualquer propriedade um objeto para controle refinado sobre como essas propriedades são acessadas, alterados e definidos. Cada propriedade de objeto tem um conjunto de atributos invisíveis que contêm metadados associados à propriedade, chamados de "propriedade descritores".

Há dois tipos de descritores associados a qualquer propriedade: data descritores e descritores do acessador. Um descritor de dados inclui pares de chave-valor que contêm o valor de uma propriedade, independentemente dela o valor é gravável, configurável ou enumerável. Os descritores do acessador contêm que são executadas quando uma propriedade é definida, alterada ou acessada.

Propriedade Tipo de descritor Valor padrão de
Object.defineProperty()
Descrição
[[Value]] Dados undefined Contém o valor de uma propriedade.
[[Writable]] Dados false Determina se você pode mudar o valor da propriedade.
[[Get]] Acessador undefined A função getter da propriedade, que é executada quando o é acessada.
[[Set]] Acessador undefined A função setter da propriedade, que é executada quando o é definida ou alterada.
[[Configurable]] Ambos false Se o valor for false, não será possível excluir a propriedade e atributos não podem ser alterados. Se for false e [[Writable]] for true, o valor da propriedade poderá ainda podem ser alteradas.
[[Enumerable]] Ambos false Se for true, você poderá iterar na propriedade usando Loops for...in ou a Object.keys() estática .

Cada uma dessas propriedades usa a mesma abreviação de [[Prototype]], indicando para que essas propriedades não sejam acessadas diretamente. Em vez disso, use o método Método estático Object.defineProperty() para definir ou modificar as propriedades de um objeto. Object.defineProperty() aceita três argumentos: o objeto em que a ação será aplicada, a chave de propriedade a ser criada ou modificada e um objeto contendo a descritores associados à propriedade que está sendo criada ou modificada.

Por padrão, as propriedades criadas usando Object.defineProperty() não sejam graváveis, enumeráveis ou configuráveis. No entanto, qualquer propriedade que você criar como parte do literal do objeto ou usando a notação de ponto ou colchete é graváveis, enumeráveis e configuráveis.

const myObj = {};

Object.defineProperty(myObj, 'myProperty', {
  value: true,
  writable: false
});

myObj.myProperty;
> true

myObj.myProperty = false;

myObj.myProperty;
> true

Por exemplo, quando [[Writable]] tem um valor false, tentar definir um novo valor da propriedade associada falha silenciosamente fora do modo restrito e gera uma erro no modo estrito:

{
    const myObj = {};

    Object.defineProperty(myObj, 'myProperty', {
    value: true,
    writable: false
    });

    myObj.myProperty = false;
    myObj.myProperty;
}
> true

(function () {
    "use strict";
    const myObj = {};

    Object.defineProperty(myObj, 'myProperty', {
    value: true,
    writable: false
    });

    myObj.myProperty = false;
    myObj.myProperty;
}());\
> Uncaught TypeError: "myProperty" is read-only

O uso eficaz de descritores é um conceito bastante avançado, mas entender a estrutura interna de um objeto é essencial sintaxes envolvidas no trabalho com objetos de formas mais comuns. Por exemplo: esses conceitos entram em jogo ao usar o método estático Object.create(), o que oferece um controle detalhado sobre quaisquer protótipos anexados ao novo objeto.

Object.create() cria um novo objeto usando um objeto existente como o protótipo. Isso permite que o novo objeto herde propriedades e métodos de outro definido pelo usuário da mesma forma que os objetos herdam as propriedades Protótipo Object integrado do JavaScript. Quando você invoca Object.create() com um objeto como argumento, ele cria um objeto vazio com o objeto passado como no protótipo.

const myCustomPrototype = {
  'myInheritedProp': 10
};

const newObject = Object.create( myCustomPrototype );

newObject;
> Object {  }
<prototype>: Object { myInheritedProp: 10 }
  myInheritedProp: 10
  <prototype>: Object { … }

Object.create pode ter um segundo argumento que especifica as próprias propriedades para o objeto recém-criado usando uma sintaxe semelhante a Object.defineProperty(): ou seja, chaves de mapeamento de objeto para um conjunto de atributos de descritor:

const myCustomPrototype = {
  'myInheritedProp': 10
};

const myObj = Object.create( myCustomPrototype, {
        myProperty: {
            value: "The new property value.",
            writable: true,
            configurable: true
        }
  });

myObj;
> Object { … }
    myProperty: "The new property value."
    <prototype>: Object { myInheritedProp: 10 }

Neste exemplo, o novo objeto (myObj) usa um literal de objeto. (myCustomPrototype) como protótipo, que contém o arquivo herdado Object.prototype, o que resulta em uma série de protótipos herdados chamada de cadeia de protótipos. Cada objeto tem um protótipo, atribuído ou herdado, que tem um protótipo atribuído ou herdado próprio. Essa cadeia termina em null, que não tem um protótipo próprio.

const myPrototype = {
  'protoProp': 10
};

const newObject = Object.setPrototypeOf( { 'objProp' : true }, myPrototype );

newObject;
> Object { objProp: true }
    objProp: true
    <prototype>: Object { protoProp: 10 }
        protoProp: 10
        <prototype>: Object { … }

As propriedades contidas em um protótipo de valor estão disponíveis no "nível superior" de um objeto, sem precisar acessar a propriedade do protótipo diretamente:

const objectLiteral = {
    "value" : true
};

objectLiteral;
> Object { value: true }
    value: true
    <prototype>: Object { … }

objectLiteral.toString();
"[object Object]"

Esse padrão se aplica a toda a cadeia de protótipos associada a uma objeto: ao tentar acessar uma propriedade, o intérprete procura por essa propriedade em cada "nível" da cadeia do protótipo, de cima para baixo, até encontra a propriedade ou a cadeia termina:

const myCustomPrototype = {
  'protoProp': "Prototype property value."
};

const myObj = Object.create( myCustomPrototype, {
    myProperty: {
        value: "Top-level property value.",
        writable: true,
        configurable: true
    }
});

myObj.protoProp;
> "Prototype property value."

Teste seu conhecimento

Quais descritores são acessadores?

[[Get]]
[[Set]]
[[Writable]]