Object.defineProperty()
=======================

O método **`Object.defineProperty()`** define uma nova propriedade diretamente em um objeto, ou modifica uma propriedade já existente em um objeto, e retorna o objeto.

**Nota:** Você invoca este método diretamente no construtor do `Object` ao invés de invocar em uma instância do tipo `Object`.

Sintaxe
-------
```

Object.defineProperty(obj, prop, descriptor)
```

### Parâmetros

`obj`

O objeto no qual será definida a propriedade.

`prop`

O nome da propriedade que será definida ou modificada.

`descriptor`

O descritor para a propriedade que será definida ou modificada.

### Valor de retorno

O objeto que foi passado à função.

Descrição
---------

Esse método permite uma precisa inclusão ou modificação de uma propriedade em um objeto. Enquanto a inclusão de propriedades através de atribuição cria propriedades que são visíveis durante a enumeração (por repetições `for...in` ou pelo método `Object.keys`), e cujos valores podem ser alterados e deletados (en-US), esse método permite a modificação deste comportamento padrão. Por padrão, valores incluídos utilizando `Object.defineProperty()` são imutáveis.

Os descritores de propriedades presentes nos objetos se apresentam em duas variedades: descritores de dados e descritores de assessores. Um *descritor de dado* é uma propriedade que contém um valor, podendo este ser gravável ou não. Um *descritor de assessor* é uma propriedade definida como um par de funções getter-setter. Um descritor deve ser de uma destas variedades; não pode ser de ambas.

Ambos os descritores de dados e de assessor são objetos. Eles compartilham as seguintes chaves obrigatórias:

`configurable`

`true` se e somente se o tipo deste descritor de propriedade pode ser alterado e se a propriedade pode ser deletada do objeto correspondente.
 **Valor padrão é `false`.**

`enumerable`

`true` se e somente se esta propriedade aparece durante enumeração das propriedades no objeto correspondente.
 **Valor padrão é `false`.**


Um descritor de dados também possui as seguintes chaves opcionais:

`value`

 O valor associado com a propriedade. Pode ser qualquer valor válido em Javascript (número, objeto, função, etc).
 **Valor padrão é `undefined`.**

`writable`

`true` se e somente se o valor associado com a propriedade pode ser modificada com um operador de atribuição (en-US).
 **Valor padrão é `false`.**


Um descritor de assessor também possui as seguintes chaves opcionais:

`get`

 Uma função que servirá como um getter da propriedade, ou `undefined` se não houver getter. Quando a propriedade é acessada, esta função é chamada sem argumentos e com `this` define para o objeto no qual a propriedade é acessada (este pode não ser o objeto sobre o qual a propriedade está definida devido a herança). O valor retornado será usado como valor da propriedade.
 **Valor padrão é `undefined`.**

`set`

 A função que servirá como um setter para a propriedade, ou `undefined` se não houver setter. Quando a propriedade é atribuída, esta função é chamada com um argumento (o valor sendo atribuído para a propriedade) e com `this` configura o objeto através do qual a propriedade é atribuída.
 **Valor padrão é `undefined`.**


Se um descritor tem nenhum das chaves `value`, `writable`, `get` e `set`, ele é tratado como um descritor de dados. Se um descritor tem ambas chaves `value` ou `writable` e `get` ou `set` keys, uma exceção é lançada.

Tenha em mente que estes atributos não são necessariamente as propriedades do próprio descritor. Propriedades herdadas serão consideradas também. Para garantir que estes padrões sejam preservados, você pode congelar o `Object.prototype` (en-US) previamente, declarar todas as opções explicitamente, ou apontar para `null` com `Object.create(null)`.


```js
// usando \_\_proto\_\_
var obj = {};
var descriptor = Object.create(null); // não herdar propriedades
// não enumerável, não configurável, não gravável por padrão
descriptor.value = "static";
Object.defineProperty(obj, "key", descriptor);

// declarando explicitamente
Object.defineProperty(obj, "key", {
  enumerable: false, // não enumerável
  configurable: false, // não configurável
  writable: false, // não gravável
  value: "static",
});

// reciclando um mesmo objeto
function withValue(value) {
  var d =
    withValue.d ||
    (withValue.d = {
      enumerable: false,
      writable: false,
      configurable: false,
      value: null,
    });
  d.value = value;
  return d;
}

// ... e ...
Object.defineProperty(obj, "key", withValue("static"));

// se o método freeze estiver disponível, prevenir as propriedades
// (value, get, set, enumerable, writable, configurable) de serem
// incluídas ou removidas do protótipo do objeto
(Object.freeze || Object)(Object.prototype);
```

Exemplos
--------

Se você deseja utilizar o método `Object.defineProperty` com uma sintaxe estilo *flags-binárias*, veja os exemplos adicionais.

### Criando uma propriedade

Quando a propriedade especificada não existe no objeto, `Object.defineProperty()` cria uma nova propriedade conforme descrito anteriormente. Campos podem ser omitidos no descritor, e os valores padrão para esses campos serão introduzidos.

Todos os campos do tipo Boolean possuem como valor padrão `false`. Os campos `value`, `get`, e `set` possuem como padrão `undefined`. Uma propriedade que é definida sem os valores para `get`/`set`/`value`/`writable` é dita "genérica" e classificada como um descritor de dados.


```js
var o = {}; // Criar um novo objeto

// Exemplo de propriedade de objeto inserida através
// de defineProperty com descritor do tipo dado
Object.defineProperty(o, "a", {
  value: 37,
  writable: true,
  enumerable: true,
  configurable: true,
});
// A propriedade 'a' existe no objeto com valor 37

// Exemplo de propriedade de objeto inserida através
// de defineProperty com descritor do tipo assessor
var bValue = 38;

Object.defineProperty(o, "b", {
  get: function () {
    return bValue;
  },
  set: function (newValue) {
    bValue = newValue;
  },
  enumerable: true,
  configurable: true,
});

o.b; // 38
// A propriedade 'b' existe no objeto com valor 38
// O valor de o.b será sempre idêntico a bValue, a
// menos que o.b seja redefinido

// Você não pode combinar ambos os tipos:
Object.defineProperty(o, "conflict", {
  value: 0x9f91102,
  get: function () {
    return 0xdeadbeef;
  },
});
// lança um TypeError: value existe apenas em descritores
// de dado, get existe apenas em descritores de assessor
```

### Modificando uma propriedade

Quando uma propriedade já existe, `Object.defineProperty()` tenta modificá-la de acordo com os valores do descritor e a configuração atual do objeto. Se o descritor antigo possuía seu atributo `configurable` como `false` a propriedade é chamada "não configurável" e nenhum atributo pode ser alterado (exceto a alteração irreversível de *writable* para *false*). Não é possível alternar o tipo de uma propriedade entre dados e assessor quando esta for não-configurável.

Um `TypeError` é lançado quando são realizadas tentativas de se alterar propriedades não-configuráveis (exceto o atributo `writable`) a menos que o valor atual e o novo sejam os mesmos.

#### O atributo writable

Quando o atributo `writable` de uma propriedade é definido como `false`, a propriedade é dita "não-gravável". Seu valor não poderá ser alterado.


```js
var o = {}; // Cria um novo objeto

Object.defineProperty(o, "a", {
  value: 37,
  writable: false,
});

console.log(o.a); // escreve 37

o.a = 25; // Nenhum erro é lançado (no modo strict seria
// lançado mesmo que o valor fosse o mesmo)

console.log(o.a); // escreve 37. A atribuição não teve efeito.
```


Como visto no exemplo, tentativas de escrita em uma propriedade não-gravável não alteram seu valor, mas também não lançam erros.

#### O atributo enumerable

O atributo `enumerable` de uma propriedade define se ela deve ser exibida em uma repetição `for...in` e por `Object.keys()` ou não.


```js
var o = {};

Object.defineProperty(o, "a", {
  value: 1,
  enumerable: true,
});

Object.defineProperty(o, "b", {
  value: 2,
  enumerable: false,
});

Object.defineProperty(o, "c", {
  value: 3,
}); // o valor padrão para enumerable é false

o.d = 4; // o valor padrão para enumerable é true quando
// a propriedade é criada em uma atribuição

for (var i in o) {
  console.log(i);
}
// escreve 'a' e 'd' (em ordem indefinida)

Object.keys(o); // ['a', 'd']

o.propertyIsEnumerable("a"); // true
o.propertyIsEnumerable("b"); // false
o.propertyIsEnumerable("c"); // false
```


#### O atributo configurable

O atributo `configurable` controla ao mesmo se uma propriedade pode ser deletada do objeto, e se seus atributos (exceto a mudança de `writable` para `false`) podem ser alterados.


```js
var o = {};

Object.defineProperty(o, "a", {
  get: function () {
    return 1;
  },
  configurable: false,
});

Object.defineProperty(o, "a", {
  configurable: true,
}); // lança um TypeError

Object.defineProperty(o, "a", {
  enumerable: true,
}); // lança um TypeError

Object.defineProperty(o, "a", {
  set: function () {},
}); // lança um TypeError (o atributo set já estava definido)

Object.defineProperty(o, "a", {
  get: function () {
    return 1;
  },
}); // lança um TypeError
// (mesmo o novo get fazendo exatamente a mesma coisa)

Object.defineProperty(o, "a", {
  value: 12,
}); // lança um TypeError

console.log(o.a); // escreve 1
delete o.a; // Nada acontece
console.log(o.a); // escreve 1
```


Se o atributo `configurable` de `o.a` fosse `true`, nenhum dos erros seria lançado e a propriedade estaria deletada ao final.

### Incluindo propriedades e valores padrão

É importante considerar a forma como os valores padrão para atributos são aplicados. Normalmente existe diferença entre usar a notação por ponto para atribuir um valor e usar `Object.defineProperty()`, como pode ser visto no exemplo abaixo:


```js
var o = {};

o.a = 1;

// é equivalente a:
Object.defineProperty(o, "a", {
  value: 1,
  writable: true,
  configurable: true,
  enumerable: true,
});

// Por outro lado,
Object.defineProperty(o, "a", { value: 1 });

// é equivalente a:
Object.defineProperty(o, "a", {
  value: 1,
  writable: false,
  configurable: false,
  enumerable: false,
});
```

### Setters e getters customizados

O exemplo abaixo mostra como implementar um objeto auto-arquivável. Quando a propriedade `temperature` é atribuída, o array `archive` recebe uma nova entrada de log.


```js
function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, "temperature", {
    get: function () {
      console.log("get!");
      return temperature;
    },
    set: function (value) {
      temperature = value;
      archive.push({ val: temperature });
    },
  });

  this.getArchive = function () {
    return archive;
  };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
```


Neste exemplo, um getter sempre retorna o mesmo valor.


```js
var pattern = {
  get: function () {
    return (
      "Eu sempre retorno esta string, " + "não importa o que você atribuiu"
    );
  },
  set: function () {
    this.myname = "esta string é meu nome";
  },
};

function TestDefineSetAndGet() {
  Object.defineProperty(this, "myproperty", pattern);
}

var instance = new TestDefineSetAndGet();
instance.myproperty = "test";

console.log(instance.myproperty);
// Eu sempre retorno esta string, não importa o que você atribuiu

console.log(instance.myname); // esta string é meu nome
```

### Propriedades de Herança

Se uma propriedade de acessor é herdada, métodos `get` e `set` serão chamados quando a propriedade é acessada e modificada sobre os objetos descendentes. Se estes métodos usam uma variável para armazenar o valor, este valor será compartilhada por todos os objetos.
```

function myclass() {
}

var value;
Object.defineProperty(myclass.prototype, "x", {
  get() {
    return value;
  },
  set(x) {
    value = x;
  }
});

var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1
```


Isto pode ser corrigido armazenando o valor em outra propriedade. Em métodos `get` e `set`, `this` aponta para o objeto no qual é usado para acessar ou modificar a propriedade.
```

function myclass() {
}

Object.defineProperty(myclass.prototype, "x", {
  get() {
    return this.stored_x;
  },
  set(x) {
    this.stored_x = x;
  }
});

var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // undefined
```


Ao contrário das propriedades do acessor, propriedades do valor serão sempre configuradas sobre o próprio objeto, não sobre um protótipo. Entretanto, se uma propriedade de valor não-gravável é herdada, ele ainda previne de modicação a propriedade do objeto.
```

function myclass() {
}

myclass.prototype.x = 1;
Object.defineProperty(myclass.prototype, "y", {
  writable: false,
  value: 1
});

var a = new myclass();
a.x = 2;
console.log(a.x); // 2
console.log(myclass.prototype.x); // 1
a.y = 2; // Ignorado, lança no modo strict
console.log(a.y); // 1
console.log(myclass.prototype.y); // 1
```

Especificações
--------------

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.defineproperty |

Compatibilidade com navegadores
-------------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Notas de compatibilidade
------------------------

### Redefinindo a propriedade `length` de um objeto `Array`

É possível redefinir a propriedade `length` de arrays, sujeita às restrições de redefinição usuais. (A propriedade `length` é inicialmente não configurável, não enumerável, mas gravável. Assim, em um array que não foi modificado, é possível alterar o valor da propriedade `length` ou torná-la não-gravável. Não é permitido alterar sua enumerabilidade ou configurabilidade, ou quando se encontrar não-gravável, alterar seu valor ou torná-la gravável novamente.) Entretanto, nem todos os browsers permitem esta redefinição.

Das versões 4 até 22 do Firefox, um `TypeError` é lançado em qualquer tentativa (seja ela permitida ou não) de redefinir a propriedade `length` de um array.

Versões do Chrome que implementam `Object.defineProperty()` em algumas circunstâncias ignoram um valor para *length* diferente do valor atual da propriedade `length` do array. Em algumas circustâncias, alterar o atributo `writable` falha de forma silenciosa (sem lançar uma exceção). Além disso, alguns métodos que modificam o array como {jsxref("Array.prototype.push")}} não respeitam uma propriedade *length* não-gravável.

Versões do Safari que implementam `Object.defineProperty()` ignoram um valor para `length` diferente do valor atual da propriedade `length`, e tentantivas de alterar o atributo `writable` executam sem erros embora não modifiquem seu comportamento.

Apenas o Internet Explorer 9 a posteriores, e o Firefox 23 e posteriores, parecem implementar total e corretamente a redefinição da propriedade `length` de arrays. Por enquanto, não confie que a redefinição da propriedade `length` vá funcionar, mesmo que de uma forma particular. E mesmo quando você *puder* confiar, existem boas razões para não fazer isso.

### Notas específicas para o Internet Explorer 8

O Internet Explorer 8 implementa o método `Object.defineProperty()` para uso apenas em objetos DOM. Algumas observações:

* Tentativas de usar `Object.defineProperty()` em objetos nativos lançam um erro.
* Todos os atributos da propriedade devem ter seu valor definido. Os atributos `configurable`, `enumerable` e `writable` devem ser `true` para um descritor do tipo dado, e `true` para `configurable` e `false` para `enumerable` em descritores do tipo assessor. (?) Qualquer tentativa de usar outros valores (?) resultará no lançamento de um erro.
* Reconfigurar uma propriedade exige que ela seja deletada anteriormente. Se a propriedade não for deletada, ela permanecerá inalterada após a tentativa de reconfiguração.
Veja também
-----------

* Enumerabilidade and posse de propriedades (en-US)
* `Object.defineProperties()`
* `Object.propertyIsEnumerable()`
* `Object.getOwnPropertyDescriptor()`
* `Object.prototype.watch()`
* `Object.prototype.unwatch()`
* `get` (en-US)
* `set` (en-US)
* `Object.create()`
* Exemplos adicionais de `Object.defineProperty`
* `Reflect.defineProperty()`