Atribuição por valor e referência

16 de julho, 2021 — 3 min read

Atribuição por valor e referência

Tipos de dados

Quando atribuímos um valor a uma variável ele pode ser um valor primitivo ou uma estrutura de dados.

Valores primitivos

Os valores primitivos no JavaScript são: String, Number, Boolean, Null (primitivo especial), undefined e Symbol. Esses valores são imutáveis, então quando alteramos alguma variável com algum desses dados é criado uma nova referência em memória, o mesmo acontece quando atribuímos uma variável a outra.

let value = 'Zagatti'
let newValue = value

No exemplo acima as duas variáveis são iguais, pois ambas contém um tipo primitivo que pode ser comparado dentro do JavaScript, mas se alterarmos alguma delas essa alteração não será refletida na outra.

Isso é o que podemos chamar de atribuição por valor, pois as variáveis não compartilham a mesma referência, sendo independentes.

Valores não primitivos

No JavaScript podemos dizer que os valores não primitivos englobam o tipo Object e Function, que no caso do JavaScript arrays, objetos literais, Map e etc, são do tipo Object.

const reference = { name: 'Zagatti' }
const newReference = { name: 'Zagatti' }

console.log(reference === newReference) // false

Se utilizarmos o operador de comparação entre objetos iguais temos um false como resultado, pois são objetos diferentes, contém as mesmas informações mas tem referências diferentes.

const reference = { name: 'Zagatti' }
const newReference = reference

console.log(reference === newReference) // true

Como podemos ver o operador de comparação retornou true, isso por que a comparação é entre o mesmo objeto, isso é o que podemos chamar de atribuição por referência.

Esse é o tipo de atribuição que devemos ter mais atenção e cuidado, pois se alterarmos o objeto por qualquer uma das variáveis, será refletido na outra.

const reference = { name: 'Zagatti' }
const newReference = reference
newReference.name = 'Andre'

console.log(reference) // { name: 'Andre' }

Existem muitas formas de criar uma nova referência para o objeto, a mais utilizada é com o spread que foi introduzido no ES6.

const reference = { name: 'Zagatti' }
const newReference = { ...reference }
newReference.name = 'Andre'

console.log(reference) // { name: 'Zagatti' }

No dia a dia utilizamos muito arrays de objetos e nesse caso é onde começa a ficar mais complexo em tomar cuidado com a referência, pois conseguimos criar um novo array com o spread, mas ainda teremos as mesmas referências de objetos internos e se torna mais complexo quando temos ainda mais níveis no objeto com outros objetos.

const reference = [{ name: 'Zagatti' }]
const newReference = [...reference]
newReference[0].name = 'Andre'

console.log(reference) // [{ name: 'Andre' }]

Existem várias formas de clonar um array ou objeto no JavaScript mas deixo para você ler no link ou pesquisar mais sobre.

Cuidado com a mutabilidade

Em diversos casos criamos funções para manipular arrays dentro do JavaScript, com isso devemos tomar cuidado com alguns métodos que alteram diretamente o array, ao invés disso na maioria dos casos é preferível os métodos que geram e retornam um novo array ou dado.

const array = [1, 2, 3]

function removeLastItem(arr) {
  arr.splice(-1, 1)
  return arr
}

const newArray = removeLastItem(array)

console.log(array) // [1, 2]
console.log(newArray) // [1, 2]

Em muitos casos queremos preservar o valor original então o exemplo com splice não funcionaria bem, para a mesma operação temos o método slice.

const array = [1, 2, 3]

function removeLastItem(arr) {
  return arr.slice(null, -1)
}

const newArray = removeLastItem(array)

console.log(array) // [1, 2, 3]
console.log(newArray) // [1, 2]

Os métodos que alteram o array são: copyWithin, fill, flat, pop, push, reverse, shift, sort, splice e unshift. Todos os outros criam um novo dado mantendo o array original intacto.

Conclusão

Entender os diferentes tipos de dados de uma linguagem é essencial para se trabalhar em alto nível, sem entender a diferença de atribuir um valor ou uma referência a uma variável fica complexo de entender o fluxo de dados da aplicação e prever dados sendo mudados em contextos diferentes tendo a mesma referência espalhada pela aplicação.

Não é um conceito difícil de se entender e espero que no fim desse post você tenha entendido como criar vantagens em diferentes contextos, alterando ou não suas estruturas de dados.

Referências


Se inscreva para novidades:

Para mais informações, acesse:Política de Privacidade

Enviar
A. Zagatti Logo

André Zagatti

Engenheiro de software frontend que gosta de compartilhar conhecimento.

© 2023, André Zagatti