Como vimos na página Primitives, a linguagem Javascript tem dois tipos de valores:
- Primitivos
- Variáveis do tipo primitivo são passadas por valor
- Não primitivos
- Variáveis do tipo não primitivo são passadas por referência
Stack e Heap
Quando declaramos variáveis, o engine aloca memória em duas regiões diferentes: call stack e heap.
Dados estáticos são aqueles que tem tamanho fixo em tempo de compilação, e pelo fato de não sofrerem alterações, são armazenados na call stack:
- Valores primitivos
- Valores da referência para objetos
Primitivos
O exemplo abaixo declara e inicializa duas variáveis e seus valores são uma string e um número, ambos literais. Ambas são variáveis com valores primitivos, então são armazenadas na call stack.
const name = "John" const age = 20
Para variáveis do tipo primitivo podemos dizer que a variável contém o valor:
name
contémJohn
age
contém20
Quando atribuímos essas variáveis à outras variáveis, copiamos o valor para a nova variável
const name = "John" const anotherName = name
Tanto
name
quanto anotherName
contém o valor John
. O valor John
foi copiado para a variável anotherName
. Alterar uma não afeta a outra, pois ambas não tem relaçãolet name = "John" let anotherName = name name = "Mary" console.log(name, anotherName) // Output: Mary John
Não primitivos
Variáveis atribuídas com valores não primitivos recebem uma referência para aquele valor. Essa referência aponta para um objeto na memória. A variável não contem o valor do qual foi atribuída.
Os objetos são alocados no heap de memória. Quando escrevemos
cont users = []
, esse array é criado no heap de memória. O que a variável users
recebe é o endereço de memória desse array.Diferente do que acontece para primitivos, a engine não aloca um tamanho fixo de memória para os objetos, mas sim incrementa mais espaço conforme necessário.
const name = "John" const age = 20 const person = { name, age, }
Internamente, a engine irá alocar e armazenar na call stack as variáveis
name
e age
. Já o objeto person
terá seu valor alocado em um endereço de memória no heap. Esse endereço de memória será atribuído à variável person
e armazenado no call stack.Por isso, dizemos que a variável
person
é uma referência para um objeto. Devido a esse motivo, quando atribuímos uma variável com valor não primitivo à uma outra, estamos apenas copiando o endereço de memória, e não o valor em si.
const person = { name: "John" } const anotherPerson = person console.log(person) // Output: { name: "John" } console.log(anotherPerson) // Output: { name: "John" } person.name = "Mary" console.log(person) // Output: { name: "Mary" } console.log(anotherPerson) // Output: { name: "Mary" }
No exemplo acima modificamos apenas a variável
person
, porém a variável anotherPerson
também refletiu a alteração, já que seu valor é o endereço de memória do objeto inicializado pela variável person
.Passando parâmetros para funções
Quando passamos uma variável com valor primitivo para uma função, esse valor é copiado para seus parâmetros. Alterar esse parâmetro não irá refletir na variável original.
const first = 1 const second = 2 function multiply(a, b) { a = 3 console.log(a * b) } multiply(first, second) // Output: 6 console.log(first, second) // Output: 1 2
Quando passamos uma variável com valor não primitivo, essa variável pode ser usada como valor ou referência dependendo das condições:
- Se alterarmos o valor desse parâmetro com um novo objeto, então o parâmetro será usado como valor, e o objeto original não será modificado, pois estamos atribuindo um novo valor para o parâmetro
const object = { name: "John" } function modify(param) { param = [1,2,3] console.log(param) } modify(object) // Output: [1,2,3] console.log(object) // Output: { name: "John" }
- Se alterarmos alguma propriedade desse parâmetro, estamos modificando o objeto referenciado pela variável que foi passada. Logo, usamos o parâmetro como referência e o objeto original será modificado.
const object = { name: "John" } function modify(param) { param.name = "Mary" console.log(param) } modify(object) // Output: { name: "Mary" } console.log(object) // Output: { name: "Mary" }