Estrutura e Criação de Objetos
Em JavaScript, os objetos funcionam como coleções de pares chave-valor, assemelhando-se às estruturas (structs) da linguagem C. As chaves (ou nomes de propriedades) são sempre tratadas como strings, enquanto os valores podem ser de qualquer tipo, inclusive outros objetos, permitindo estruturas aninhadas complexas.
A criação de um objeto pode ser feita através da sintaxe de construtor ou usando literais de objeto:
// Sintaxe com construtor
let book = new Object();
// Sintaxe de literal (mais comum)
let emptyBook = {};
// Objeto com propriedades iniciais, incluindo chaves com espaços
let techBook = {
title: "Clean Code",
publishYear: 2008,
"page count": 464
};
Acesso às Propriedades
Existem duas notações para acessar ou modificar propriedades. A notação de ponto (.) é a mais direta, mas exige que o nome da propriedade seja um identificador válido. Para chaves dinâmicas ou que contêm caracteres especiais (como espaços), a notação de colchetes ([]) é obrigatória. O interpretador avalia o conteúdo dos colchetes como uma string.
let propKey = "title";
console.log(techBook[propKey]); // "Clean Code"
console.log(techBook["pub" + "lishYear"]); // 2008
console.log(techBook["page count"]); // 464
// console.log(techBook.propKey); // undefined (busca uma chave literal chamada "propKey")
Referências e Atribuição Dinâmica
As variáveis em JavaScript não armazenam o objeto em si, mas sim uma referência (ponteiro) para a localização do objeto na memória. Isso significa que múltiplas variáveis podem apontar para o mesmo objeto, e essas referências podem ser livremente reatribuídas, inclusive entre objetos e funções.
let systemA = {
status: "online",
uptime: 99.9,
log: () => console.log("System A running")
};
let systemB = { mode: "maintenance", code: 503 };
// systemA agora aponta para o mesmo objeto que systemB
systemA = systemB;
// systemA.log(); // TypeError: systemA.log is not a function
function calculateArea(width, height) {
return width * height;
}
// A variável systemA agora aponta para uma função
systemA = calculateArea;
console.log(systemA(5, 10)); // 50
// A função agora aponta para um objeto
calculateArea = systemB;
console.log(calculateArea.code); // 503
Propriedades Computadas e Sintaxe Abreviada
Ao utilizar literais de objeto, é possível definir chaves dinamicamente usando a sintaxe de colchetes [variavel]. O valor da variável será avaliado e convertido em string para ser usado como nome da propriedade.
let selectedTheme = "darkMode";
let userPreferences = {
[selectedTheme]: true,
fontSize: 16
};
console.log(userPreferences.darkMode); // true
Além disso, quando o nome da variável que fornece o valor é idêntico ao nome da propriedade desejada, o ES6 permite uma sintaxe abreviada, eliminando a redundância:
function createServer(host, port) {
return {
host, // Equivalente a host: host
port // Equivalente a port: port
};
}
let dbServer = createServer("127.0.0.1", 5432);
Palavras Reservadas e Nomes Especiais
Com exceção da propriedade __proto__, o JavaScript permite o uso de palavras reservadas da linguagem como chaves de objeto sem gerar conflitos. Números utilizados como chaves são automaticamente convertidos para strings.
let config = {
for: "loop",
let: "variable",
return: "value",
0: "zero index"
};
console.log(config.return); // "value"
Verificação e Iteração de Propriedades
O operador in verifica a existência de uma propriedade (própria ou herdada) em um objeto, retornando um valor booleano. Já o laço for...in permite iterar sobre todas as chaves enumeráveis.
let device = {
brand: "TechCorp",
battery: 85
};
let query = "brand";
console.log(query in device); // true
for (let key in device) {
console.log(`${key}: ${device[key]}`);
}
Ordenação de Chaves Numéricas
Durante a iteração com for...in, o motor JavaScript aplica uma regra específica de ordenação: chaves que são strings representando números inteiros não negativos são iteradas primeiro, em ordem numérica ascendente. As demais chaves seguem a ordem de enserção. Para preservar a ordem de inserção de valores numéricos, é comum prefixá-los com um caractere não numérico.
let regionMetrics = {
"id_44": "Reino Unido",
"id_1": "Estados Unidos",
"id_81": "Japão"
};
// A ordem de inserção é preservada devido ao prefixo "id_"
for (let key in regionMetrics) {
let numericCode = Number(key.replace("id_", ""));
console.log(numericCode); // 44, 1, 81
}
Métodos e o Contexto this
Funções que são atribuídas como propriedades de um objeto são denominadas métodos. A sintaxe moderna permite omitir a palavra-chave function e os dois pontos.
let assistant = {
// Sintaxe tradicional
wakeUp: function() {
console.log("System ready.");
},
// Sintaxe abreviada (ES6)
sleep() {
console.log("Shutting down...");
},
// Arrow function (comportamento de 'this' diferente)
ping: () => console.log("Pong!")
};
Para acessar outras propriedades do mesmo objeto dentro de um método, utiliza-se a palavra-chave this. O valor de this não é definido no momento da criação da função, mas sim no momento de sua execução, dependendo de qual objeto realizou a chamada (contexto de invocação).
let agentA = { codename: "Alpha" };
let agentB = { codename: "Beta" };
function broadcastIdentity() {
console.log(`Transmission from ${this.codename}`);
}
// Atribuição da função como método para diferentes objetos
agentA.transmit = broadcastIdentity;
agentB.transmit = broadcastIdentity;
// broadcastIdentity(); // Erro ou 'undefined' (dependendo do modo estrito), pois não há objeto chamador
agentA.transmit(); // "Transmission from Alpha"
agentB.transmit(); // "Transmission from Beta"
Armadilhas Comuns com Referências de Contexto
Um erro frequente ocore ao tentar armazenar a referência de this diretamente como uma propriedade de valor (ao invés de um método) dantro de funções de fábrica (factory functions). Como this é avaliado no momento da execução da função externa, ele não apontará para o objeto que está sendo construído.
// Implementação problemática
function buildConsole() {
return {
id: "Terminal-9",
// 'this' aqui refere-se ao contexto global ou é undefined (em strict mode)
reference: this
};
}
let myConsole = buildConsole();
// console.log(myConsole.reference.id); // TypeError: Cannot read properties of undefined
// Solução adequada: envolver a referência em um método
function buildConsoleFixed() {
return {
id: "Terminal-9",
getReference() {
// 'this' agora é avaliado quando o método é chamado através do objeto
return this;
}
};
}
let fixedConsole = buildConsoleFixed();
console.log(fixedConsole.getReference().id); // "Terminal-9"