Entendendo a Diferença entre Tipos de Valor e Tipos de Referência no JavaScript

No ecossistema JavaScript, compreender como os dados são armazenados e manipulados na memória é fundamental para evitar bugs de mutabilidade indesejada. As variáveis podem ser categorizadas em dois grandes grupos baseados em como armazenam suas informações: Tipos de Valor (primitivos) e Tipos de Referência (objetos).

  1. Definições Fundamentais

Tipos de Valor: Também conhecidos como primitivos, esses tipos armazenam o dado real diretamente na posição de memória da variável. Quando você atribui uma variável de valor a outra, o JavaScript cria uma cópia física e independente do conteúdo.

  • Number
  • String
  • Boolean
  • null e undefined
  • Symbol e BigInt

Tipos de Referência: Estes tipos não armazenam o valor diretamente. Em vez disso, a variável guarda um endereço de memória (um ponteiro) que indica onde o objeto real está alocado na "Heap". Ao atribuir um tipo de referência a uma nova variável, você está apenas copiando o endereço, não o objeto em si.

  • Object
  • Array
  • Function
  1. Comportamento em Atribuições

A principal diferença prática manifesta-se quando tentamos copiar dados. Veja como os tipos de valor se comportam:


// Exemplo com Tipos de Valor
let notaMatematica = 85;
let notaPortugues = notaMatematica; // Cópia do valor real

notaPortugues = 95; // Altera apenas notaPortugues

console.log(notaMatematica); // Saída: 85
console.log(notaPortugues);  // Saída: 95
console.log(notaMatematica === notaPortugues); // false

No exemplo acima, notaMatematica permanece intacta porque a atribuição criou uma entidade completamente nova na memória.

Agora, observe o comportamento com tipos de referência:


// Exemplo com Tipos de Referência
const perfilUsuario = { nome: "Carlos", idade: 30 };
const editorPerfil = perfilUsuario; // Cópia apenas da referência (endereço)

editorPerfil.idade = 31;

console.log(perfilUsuario.idade); // Saída: 31 (O original foi afetado!)
console.log(editorPerfil === perfilUsuario); // true (Apontam para o mesmo local)

// Comparação entre objetos diferentes com conteúdo idêntico
const outroPerfil = { nome: "Carlos", idade: 31 };
console.log(perfilUsuario === outroPerfil); // false (Referências de memória distintas)

Neste caso, perfilUsuario e editorPerfil são como dois controles remotos para a mesma televisão. Mudar o canal em um afetará o que é visto através do outro.

  1. Passagem de Parâmetros em Funções

Quando passamos variáveis para funções, o comportamento segue a mesma lógica de atribuição mencionada antreiormente.

Passagem por Valor

Se passarmos um primitivo, qualquer alteração dentro da função é local ao escopo da função.


function atualizarSaldo(valor) {
    valor = valor + 100;
    return valor;
}

let saldoAtual = 500;
atualizarSaldo(saldoAtual);
console.log(saldoAtual); // Saída: 500 (O valor externo não muda)

Passagem por Referência

Se passarmos um objeto ou array, a função recebe o endereço de memória, permitindo que o estado interno do objeto original seja modificado.


function adicionarItem(carrinho) {
    carrinho.push("Teclado Mecânico");
}

const meuCarrinho = ["Mouse"];
adicionarItem(meuCarrinho);

console.log(meuCarrinho); // Saída: ["Mouse", "Teclado Mecânico"]

  1. Estruturas Complexas

É comum termos objetos que contêm outros objetos. A referência funciona em cadeia, mantendo a ligação com os dados originais enquanto o caminho de memória for preservado.


const motor = { cavalos: 150, tipo: "Flex" };

const carro = {
    modelo: "Sedan",
    fabricante: "Honda",
    especificacoes: motor
};

const oficina = carro;
oficina.especificacoes.cavalos = 200;

console.log(motor.cavalos); // Saída: 200 (Alterado via referência encadeada)

Tags: javascript ecmascript memory-management data-types

Publicado em 8-11 19:49