O autor do material, cuja tradução publicamos hoje, diz que os objetos JavaScript contêm muitas coisas, cuja existência você nem pode suspeitar, usando-as no trabalho diário. Os objetos em JavaScript são muito fáceis de criar, convenientes para trabalhar, parecem compreensíveis e flexíveis e muitos programadores simplesmente não pensam no fato de que os objetos não são tão simples assim.

Nota: as informações da publicação na prática devem ser aplicadas com muito cuidado e sob a supervisão de colegas mais experientes.
Aqui, falamos sobre o que está oculto nas profundezas dos objetos e discutimos os meandros de trabalhar com eles.
Depois de dominar este material, você saberá as respostas para as seguintes perguntas:
- Como tornar uma propriedade de um objeto inviável?
- O que são propriedades com métodos de acesso e quais são seus recursos?
- Como tornar uma propriedade imutável ou oculta?
- Por que algumas propriedades não são visíveis nos loops de entrada ou nos resultados do método
Object.keys()
e outras são visíveis? - Como "proteger" um objeto de modificação?
- Como entender um pedaço de código semelhante ao seguinte:
obj.id = 5; console.log(obj.id)
Tipos de propriedades do objeto
▍ Propriedades de armazenamento de dados
Você provavelmente criou inúmeros objetos semelhantes a este:
const obj = { name: 'Arfat', id: 5 } obj.name
As propriedades de
name
e
id
do objeto
obj
são chamadas de propriedades de
obj
dados ou "Propriedades de Dados". Essas são propriedades familiares que são constantemente encontradas no código JavaScript. Quais outros tipos de propriedades os objetos podem ter?
With Propriedades com métodos de acesso
Essas propriedades também são conhecidas como getters e setters; também são encontradas em outras linguagens de programação como C # ou Python. Uma propriedade com Accessor Property é uma combinação de duas funções -
get
e
set
.
Ao declarar essas propriedades, em vez de usar a construção de tipo de
:
tradicional, a seguinte sintaxe é usada:
const accessorObj = { get name() { return 'Arfat'; } }; accessorObj.name;
Dê uma olhada no objeto
accesorObj
e compare-o com o objeto
dataObj
. Aparentemente, agora eles mostram o mesmo comportamento. Ao descrever o primeiro objeto, usamos a palavra-chave
get
, seguida pela declaração da função. Para acessar uma propriedade semelhante, embora seja representada por uma função, não é necessário colocar parênteses após o nome da propriedade para chamar essa função. Ou seja, um design como
accessorObj.name();
incorreto.
Ao tentar acessar a propriedade
accessorObj.name
, ou seja, ao tentar lê-la, a função correspondente é executada e o valor retornado a ela se torna o valor da propriedade
name
.
As funções
get
são chamadas getters, são responsáveis por obter os valores. Se continuarmos nosso exemplo e tentarmos alterar o valor da propriedade
name
do objeto
accessorObj
, digamos, executando o comando
accessorObj.name = 'New Person';
, acontece que nada vai acontecer. O ponto aqui é que a função setter não está associada à tecla de
name
. Essas funções permitem personalizar a ordem de atribuição de novos valores às propriedades dos objetos, cujo acesso é organizado usando getters.
Aqui está a aparência de uma declaração de objeto com um getter e um setter:
const accessorObj = { _name: 'Arfat', get name() { return this._name; }, set name(value) { this._name = value; } };
A função setter recebe o que eles estão tentando atribuir à propriedade do objeto como um parâmetro. Agora você pode salvar algo na propriedade do objeto. Nesse caso, criamos uma propriedade "privada" do objeto
_name
. O primeiro caractere do nome dessa propriedade é um sublinhado, que nada mais é do que uma dica para o programador, indicando que essa propriedade é destinada às necessidades internas do objeto. Além disso, trabalhamos com ele ao acessar a propriedade do objeto
name
, cujo acesso é regulado pelo getter e setter.
Ao mesmo tempo, na função getter, antes de retornar o valor da propriedade
_name
, podemos modificá-lo.
Aqui está o que pode parecer:
const obj = { get name() { return this._name.toUpperCase(); }, set name(value) { this._name = value; }, get id() { return this._id.toString(2);
A propósito, este programa contém a resposta para uma das perguntas apresentadas no início do artigo, que diz respeito à análise do código incompreensível à primeira vista.
Por que alguém precisaria de propriedades com métodos de acesso se você pode trabalhar com segurança com propriedades comuns? Por exemplo, eles podem ser necessários para registrar informações sobre leituras de propriedades ou para armazenar um histórico de alterações nos valores das propriedades. As propriedades com métodos de acesso oferecem todas as possibilidades de processamento de dados usando funções e a característica de simplicidade de trabalhar com propriedades comuns. Leia mais sobre como usar essas propriedades
aqui .
Como o JavaScript distingue entre propriedades comuns que armazenam dados de propriedades com métodos de acesso? Descubra isso.
Descritores de Propriedade de Objeto
À primeira vista, pode parecer que existe uma correspondência direta entre as chaves e os valores armazenados nos objetos. No entanto, isso não é totalmente verdade.
▍ Atributos de propriedade
Cada chave do objeto está associada a um conjunto de atributos que determinam as características do valor associado a essa chave. Esses atributos também podem ser considerados como metadados que descrevem um par
:
.
Os atributos são usados para definir e descrever o estado das propriedades dos objetos. O conjunto de atributos de propriedade é chamado de descritor. Existem seis atributos de propriedade:
[[Value]]
[[Get]]
[[Set]]
[[Writable]]
[[Enumerable]]
[[Configurable]]
Por que os nomes de atributos de propriedades nesta lista estão incluídos na construção
[[]]
? Parênteses duplos indicam que essas são entidades usadas pelos mecanismos internos do idioma. Um programador JS não pode acessar essas propriedades diretamente. Para influenciá-los, métodos apropriados são usados.
Considere a seguinte imagem, tirada
daqui , na qual você pode ver o objeto e os atributos de suas propriedades.
Objeto e atributos de suas propriedadesNosso objeto tem 2 chaves -
x
e
y
. Além disso, um conjunto de atributos está associado a cada um deles.
Como, usando JavaScript, obter informações sobre o objeto, semelhantes às mostradas na figura anterior? Você pode usar a função
Object.getOwnPropertyDescriptor()
para isso. Ele pega um objeto e o nome de sua propriedade e, em seguida, retorna um objeto que contém os atributos dessa propriedade. Aqui está um exemplo:
const object = { x: 5, y: 6 }; Object.getOwnPropertyDescriptor(object, 'x');
Note-se que a composição dos atributos de uma propriedade específica depende do seu tipo. Todos os seis atributos da mesma propriedade não foram encontrados.
- Se estamos falando de propriedades com dados, elas terão apenas os atributos
[[Value]]
, [[Writable]]
, [[Enumerable]]
e [[Configurable]]
. - As propriedades com métodos de acesso, em vez dos atributos
[[Value]]
e [[Writable]]
, possuem os atributos [[Get]]
e [[Set]]
.
▍ [[valor]]
Este atributo armazena o que é retornado ao tentar obter o valor da propriedade de um objeto. Ou seja, se no exemplo anterior usamos uma construção do formulário
object.x
, obtemos o que é armazenado no atributo
[[Value]]
. O mesmo acontece quando você tenta ler as propriedades de um objeto usando colchetes.
▍ [[Get]]
Este atributo armazena uma referência a uma função, que é uma propriedade getter. Essa função é chamada sem argumentos ao tentar ler o valor de uma propriedade.
▍ [[Definir]]
É aqui que o link para a função declarada ao criar a propriedade setter é armazenado. É chamado com um argumento que representa o valor que eles tentaram atribuir à propriedade, ou seja, é chamado durante cada operação de atribuição de um novo valor a uma propriedade.
const obj = { set x(val) { console.log(val)
Neste exemplo, o lado direito da expressão é passado como um argumento
val
para a função setter.
Aqui está o código que demonstra o uso de setters e getters.
[[[Gravável]]
Este atributo possui um valor booleano. Indica se o valor da propriedade pode ser substituído ou não. Se
false
for armazenado aqui, as tentativas de alterar o valor da propriedade falharão.
▍ [[Enumerável]]
Um valor lógico também é armazenado aqui. Este atributo controla a emissão da propriedade
for-in
loops de entrada. Se estiver definido como
true
, será possível trabalhar com a propriedade usando esses ciclos.
▍ [[Configurável]]
Este atributo também é representado por um valor booleano. É o que acontece se o
false
for armazenado nele:
- A propriedade não pode ser excluída.
- Você não pode converter propriedades que armazenam dados em propriedades com métodos de acesso e vice-versa. Tentativas de realizar essas transformações não levarão a nada.
- Não será permitido alterar os valores dos atributos de propriedade. Ou seja, os valores atuais dos atributos
[[Enumerable]]
, [[Configurable]]
, [[Get]]
e [[Set]]
permanecerão inalterados.
O efeito de definir esse atributo como
false
também depende do tipo de propriedade. Este atributo, além dos efeitos acima nas propriedades, atua sobre elas e assim:
- Se essa é uma propriedade que armazena dados, o atributo
[[Writable]]
só pode ser alterado de true
para false
. - Até que o atributo
[[Writable]]
seja definido como false
, o atributo [[Value]]
pode ser alterado. Porém, depois que os atributos [[Writable]]
e [[Configurable]]
forem definidos como false
, a propriedade se tornará gravável, não passível de exclusão e imutável.
Trabalhar com descritores
Agora que nos familiarizamos com os atributos, nos perguntamos como podemos influenciá-los. O JavaScript possui funções especiais para trabalhar com descritores de propriedades. Vamos conversar sobre eles.
Método Método Object.getOwnPropertyDescriptor ()
Já conhecemos esse método. Ele, levando um objeto e o nome de sua propriedade, retorna
undefined
ou um objeto com um descritor de propriedade.
▍ Método Object.defineProperty ()
Este é um método estático de
Object
que permite adicionar propriedades a objetos ou alterar propriedades existentes. São necessários três argumentos - um objeto, um nome de propriedade e um objeto com um descritor. Este método retorna um objeto modificado. Considere um exemplo:
const obj = {};
Pode ser executado no Node.js. O código acabou sendo bastante grande, mas, de fato, é bastante simples. Vamos analisá-lo, focando nos comentários do formulário
// #n
.
No fragmento
#1
usamos a função
defineProperty
, passando o objeto
obj
, o nome da propriedade
id
e um objeto descritor que contém apenas a propriedade
value
, indicando que
42
será gravado no atributo
[[Value]]
. Lembre-se de que se você não passar valores para atributos como
[[Enumerable]]
ou
[[Configurable]]
neste objeto, eles serão configurados como
false
por padrão. Nesse caso, os atributos
[[Writable]]
,
[[Enumerable]]
e
[[Configurable]]
propriedade
id
são definidos como
false
.
No local marcado como
#2
, estamos tentando exibir uma representação de string do objeto no console. Como sua propriedade
id
não é enumerável, ela não será exibida. Além disso, a propriedade existe, o que comprova sua conclusão bem-sucedida pelo comando
#3
.
Criando um objeto (fragmento
#4
), definimos uma lista completa de atributos. Em particular, defina
[[Writable]]
como
false
.
Com os comandos
#5
e
#7
exibimos o valor da propriedade
name
. Mas entre eles (fragmento
#6
) tentamos mudar esse valor. Esta operação não alterou o valor da propriedade porque seu atributo
[[Writable]]
está definido como
false
. Como resultado, os dois comandos emitem a mesma coisa para o console.
O comando
#8
é uma tentativa de remover a propriedade
id
. Lembre-se de que seu atributo
[[Configurable]]
está definido como
false
, o que significa que não pode ser excluído. Isso é comprovado pela equipe
#9
.
O fragmento
#10
mostra o uso da função
Object.defineProperties () . Funciona da mesma maneira que a função
defineProperty()
, mas permite, em uma chamada, afetar várias propriedades do objeto, enquanto
defineProperty()
trabalha com apenas uma propriedade do objeto.
Proteção de Objetos
De tempos em tempos, o desenvolvedor precisa proteger objetos contra interferências externas. Por exemplo, dada a flexibilidade do JavaScript, é muito fácil alterar por engano as propriedades de um objeto que não deve ser alterado. Existem três maneiras principais de proteger objetos.
▍ Método Object.preventExtensions ()
O método
Object.preventExtensions()
impede que o objeto se expanda, ou seja, adicione novas propriedades a ele. Ele pega um objeto e o torna não expansível. Observe que você pode remover propriedades desse objeto. Considere um exemplo:
const obj = { id: 42 }; Object.preventExtensions(obj); obj.name = 'Arfat'; console.log(obj);
Para descobrir se um objeto não é expansível, você pode usar o método
Object.isExtensible()
. Se retornar
true
, você poderá adicionar novas propriedades ao objeto.
▍ Método Object.seal ()
O método
seal()
parece "selar" objetos. Aqui está o que estamos falando:
- Seu uso impede que novas propriedades sejam adicionadas ao objeto (nisso é semelhante a
Object.preventExtensions()
). - Torna todas as propriedades existentes de um objeto não configuráveis.
- Os valores das propriedades existentes, se o atributo
[[Writable]]
não estiver definido como false
, poderão ser alterados.
Como resultado, verifica-se que esse método impede a adição de novas propriedades ao objeto e a remoção das propriedades existentes nele.
Considere um exemplo:
const obj = { id: 42 }; Object.seal(obj); delete obj.id
Para verificar se o objeto está "selado" ou não, você pode usar o método
Object.isSealed()
.
▍ Método Object.freeze ()
O método
freeze()
permite "congelar" objetos, equipando-os com o nível mais alto possível de proteção em JavaScript. Veja como funciona:
- Sela um objeto usando
Object.seal()
. - Proíbe completamente a modificação de quaisquer propriedades existentes do objeto.
- Proíbe a modificação de descritores de propriedades.
Aqui está um exemplo:
const obj = { id: 42 }; Object.freeze(obj); delete obj.id
Você pode verificar se o objeto está "congelado" usando o método
Object.isFrozen()
.
▍ Visão geral dos métodos usados para proteger objetos
É importante observar que os métodos acima usados para proteger objetos afetam apenas suas propriedades que não são objetos.
Aqui está uma tabela de resumo dos métodos considerados para proteção de objetos, extraídos
daqui .
| Criação de Propriedade
| Leitura de propriedades
| Substituição de propriedade
| Remoção de propriedade
|
Object.freeze()
| - | +
| - | - |
Object.seal()
| - | +
| +
| - |
Object.preventExtensions()
| - | +
| +
| +
|
Sumário
Dada a frequência com que os objetos são usados no JavaScript, é importante que todo desenvolvedor saiba como eles estão organizados. Esperamos que o que você aprendeu lendo este material seja útil para você. Além disso, agora você sabe as respostas para as perguntas listadas no início do artigo.
Caros leitores! Como você protege objetos JavaScript?