Aguarde...

4 de abril de 2021

Valores primitivos, objetos e referências em JavaScript simplificado

Valores primitivos, objetos e referências em JavaScript simplificado

Tipos de dados primitivos, valores, objetos e referências estão entre os tópicos mais mal compreendidos em JavaScript. Eles podem causar muitas dores de cabeça. Neste tutorial, você aprenderá sobre tipos de dados primitivos, valores, objetos, referências, as diferenças entre eles e como funcionam.

Uma breve introdução

Em JavaScript, existem duas categorias de tipos de dados com os quais você pode trabalhar. A primeira categoria são tipos de dados primitivos. Neste momento, existem [sete tipos primitivos]. Esses tipos de dados primitivos são o número, corda, boolean, nullundefined, BigInt e Symbol. O BigInt e o símbolo são tipos de dados mais recentes.

O símbolo foi introduzido na especificação ES6 . O BigInt foi introduzido posteriormente, na especificação ES2020 . Quando algo não é um desses tipos de dados primitivos, é tecnicamente um objeto. Isso se aplica a objetos reais, bem como matrizes e até funções. Do ponto de vista do JavaScript, todos esses são objetos.

Essa distinção entre objetos e tipos de dados primitivos é importante porque o JavaScript trata cada um de maneira diferente.

// Primitive data types:
const numberVal = 5
const strVal = 'Hello!'
const boolVal = true
const nullVal = null
const undefinedVal = undefined
const bigIntVal = 9007123254550972n
const symbolVal = Symbol('label')

// Objects:
const myObjLiteral = {
  name: 'Toby'
}

const myArray = [9, 'book', true, null]

function myFunction(num1, num2) {
  return num1 / num2
}

Tipos de dados primitivos e valores primitivos

Vamos começar com a primeira categoria, tipos de dados primitivos. Os valores que contêm esses tipos de dados primitivos são chamados de dados estáticos. Como dados estáticos, o JavaScript os armazena na pilha . Uma coisa importante sobre esses valores primitivos é que seu tamanho é fixo. JavaScript sabe quanta memória esses tipos de dados precisam.

Digamos que você atribua a alguma variável um tipo de dado primitivo como um valor. A partir de agora, essa variável conterá esse valor. Se você manipular com essa variável, você manipula diretamente com o valor que atribuiu a ela. Uma maneira simples de testar isso e as consequências é atribuir a variável a outra variável.

Quando você atribui uma variável a outra, e o valor de first é um tipo de dados primitivo, o JavaScript copia esse valor. Ao fazer isso, você está copiando os valores “por valor”. Portanto, agora, se você alterar o valor da primeira variável, a segunda permanecerá a mesma. Isso ocorre porque, embora você tenha criado uma variável a partir de outra, ambas têm seus próprios valores separados.

Portanto, se você alterar o valor de uma variável, não mudará a segunda. A segunda variável é uma entidade separada. Vamos voltar à pilha. Quando você atribui a primeira variável, o JavaScript armazena seu valor na pilha. Quando você atribui a variável a outra variável, seu valor também é adicionado à pilha.

Neste momento, a pilha agora conterá dois valores, um para cada variável. Não importa que os dois valores sejam iguais. Também não importa que você tenha criado a segunda variável a partir da primeira. Para JavaScript, essas são duas entidades separadas. Essas duas variáveis ​​não têm relação entre si.

É também por isso que você pode trabalhar com segurança com cada uma dessas variáveis ​​conforme desejar. É por isso que você pode alterar uma variável sem alterar a outra.

// Create a variable and assign it
// a primitive value:
let x = 'Hello'

// Assign "x" to another variable:
let y = x

// Change the value of "x":
// NOTE: this will not change "y".
x = 'Bye'

// Log the value of "x":
console.log(x)
// Output:
// 'Bye'

// Log the value of "x":
console.log(y)
// Output:
// 'Hello'

// Assign "y" to another variable:
let z = y

// Assign "z" to another variable:
let w = z

// Change the value of "y":
// NOTE: this will not change "z" and "w".
y = 'Eloquent'

// Log the value of "x":
console.log(z)
// Output:
// 'Hello'

// Log the value of "x":
console.log(w)
// Output:
// 'Hello'

Objetos e referências

Os objetos são uma história diferente. Quando você atribui uma variável, um objeto JavaScript o trata de maneira diferente. O valor, o objeto, não será adicionado à pilha. Em vez disso, ele será adicionado ao heap de memória . Existe outra diferença muito importante. Essa variável não conterá o valor, o objeto, mas uma referência a esse objeto.

Pense nesta referência como um elo ou corrente. É um link que conecta uma variável específica a um objeto específico. Isso tem uma consequência importante. Se você manipula com aquela variável, você trabalha com a referência e, por meio desta referência, com o próprio objeto. E se você copiar esse objeto atribuindo essa variável a outra variável?

Você pode pensar que isso criará outro objeto, cópia do primeiro, assim como no caso de valor primitivo. Não é isso que vai acontecer. O que realmente acontecerá é que o JavaScript criará uma nova referência. JavaScript só criará uma nova referência ou link para o objeto original.

Haverá ainda apenas um objeto na pilha de memória, o original. Isso é chamado de cópia “por referência” e acontece toda vez que você copia um objeto. Copiar dessa forma, por referência, resulta na criação de cópias superficiais de objetos. Esse tipo de cópia também tem uma consequência séria.

Se você manipular com qualquer uma dessas variáveis, também manipulará com o mesmo objeto. Então, se você mudar o objeto mudando uma variável, você muda a outra variável também. Ambas são variáveis ​​diferentes, mas ambas fazem referência ou se vinculam ao mesmo objeto.

// Create a variable and assign it
// a simple object:
let a = { name: 'Stan' }

// Assign "a" to another variable:
let b = a

// Change the value of "a"
// by adding new property "age" to the object:
a.age = 44

// Log the value of "a":
// console.log(a)
// Output:
// { name: 'Stan', age: 44 }

// Log the value of "b":
// console.log(b)
// Output:
// { name: 'Stan', age: 44 }

// Assign "b" to another variable:
let c = b

// Assign "c" to another variable:
let d = c

// Change the value of "d"
// by adding another property
// "favoriteAnimal" to the object:
d.favoriteAnimal = 'elephant'

// Log the value of "a":
console.log(a)
// Output:
// {
//   name: 'Stan',
//   age: 44,
//   favoriteAnimal: 'elephant'
// }

// Log the value of "b":
console.log(b)
// Output:
// {
//   name: 'Stan',
//   age: 44,
//   favoriteAnimal: 'elephant'
// }

// Log the value of "c":
console.log(c)
// Output:
// {
//   name: 'Stan',
//   age: 44,
//   favoriteAnimal: 'elephant'
// }

// Log the value of "d":
console.log(c)
// Output:
// {
//   name: 'Stan',
//   age: 44,
//   favoriteAnimal: 'elephant'
// }

Nota: Uma maneira de entender como funciona a cópia por referência é pensar sobre chaves e casas. Quando você copia sua chave, não está criando uma nova casa. Ainda há apenas uma casa, mas agora existem duas chaves que podem destrancar essa casa. As variáveis ​​são essas chaves, o objeto é aquela casa.

Resumo rápido de tipos de dados primitivos, objetos, valores e referências

Agora você sabe a diferença entre valores primitivos e referências. Ao atribuir tipos de dados primitivos e depois copiá-los, você está copiando por valores. Cada uma dessas cópias (variáveis) é uma entidade separada que não tem relação com outra. Você pode mudar um sem mudar nenhum outro.

Quando você atribui e copia um objeto, está copiando por referência. Você está criando novas referências para cada cópia. Como resultado, existem várias referências (variáveis). No entanto, ainda há apenas um objeto. Se você alterar uma dessas variáveis, alterará o objeto original. Isso afetará todas as referências (variáveis).

Valores primitivos, referências e comparação

Saber a diferença entre valor e referência é importante quando você deseja comparar coisas. Vamos dar uma olhada em como a comparação funciona com valores primitivos e objetos.

Comparando valores primitivos

Comparar dois valores primitivos geralmente é simples. O único problema é saber a diferença entre igual e estritamente igual e qual usar (geralmente será estritamente igual). Comparar valores primitivos com igual estrito verificará o valor e o tipo. Se ambos forem iguais, você obterá true. Se não, você vai conseguir false.

// One primitive value:
// Create one variable and assign it primitive value:
const str1 = 'JavaScript'

// Create another variable and assign it "str1":
const str2 = str1

// Compare "str1" and "str2":
console.log(str1 === str2)
// Output:
// true


// Two identical primitive values:
// Create two variables and assign them
// the same primitive values:
const num1 = 15
const num2 = 15

// Compare "num1" and "num2":
console.log(num1 === num2)
// Output:
// true

Comparando objetos e referências

As referências funcionam de maneira diferente. Se você comparar dois objetos diferentes e o conteúdo for o mesmo, a comparação ainda resultará em false. A comparação resultará trueapenas se você comparar referências ao mesmo objeto.

// One object:
// Create a variable and assign it an object:
const a = { name: 'Jack' }

// Assign "a" to another variable:
const b = a

// Compare "a" and "b":
console.log(a === b)
// Output:
// true

// Two identical objects:
// Create a variable and assign it an object:
const a = { name: 'George' }

// Create another variable and assign it the same object:
const b = { name: 'George' }

// Compare "a" and "b":
console.log(a === b)
// Output:
// false

Lembre-se de que os arrays, e também as funções, são tecnicamente objetos. Isso significa que se você comparar variáveis ​​com matrizes idênticas, o resultado será sempre false. Essas variáveis ​​serão as mesmas apenas se ambas fizerem referência à mesma matriz.

// One array:
// Create a variable and assign it an array:
const x = [1, 2, 3, 4]

// Create another variable and assign it "x":
const y = x

// Compare "x" and "y":
console.log(x === y)
// Output:
// true


// Two identical arrays:
// Create a variable and assign it an array:
const x = [1, 2, 3, 4]

// Create another variable and assign it the same array:
const y = [1, 2, 3, 4]

// Compare "x" and "y":
console.log(x === y)
// Output:
// false

Funções e passagem por valor e por referência

Saber a diferença entre valor e referência também é útil quando você trabalha com funções. Quando você passa algum valor primitivo armazenado em uma variável para uma função como um argumento, você o está passando “por valor”. Basicamente, você está copiando o próprio valor para uma função. A consequência disso é a mesma de quando você copia “por valor”.

Se você tentar alterar o valor passado para a função, ele não terá efeito na própria variável. O valor dessa variável permanecerá o mesmo. A alteração criada pela função não terá efeito sobre ela. Bem, a menos que você acesse a própria variável e a altere diretamente, mas esse é um cenário diferente.

// Create a variable and assign it a primitive value:
let personName = 'Andrei'

// Create a function that will attempt to modify
// the value it receives as an argument:
function changeNameFunc(name) {
  name = 'Viktor'
}

// Call the "changeNameFunc" function:
changeNameFunc(personName)

// Log the value of "name" variable:
console.log(personName)
// Output:
// 'Andrei' // <= The name is the same.

Se você tentar fazer isso com um objeto, o resultado será diferente. Quando você passa um objeto, ele é passado “por referência”. Nesse caso, o JavaScript não está copiando o objeto para que a função possa usá-lo. Ele apenas fornece a referência ao objeto original. Se você tentar modificar o objeto, na verdade mudará o objeto original.

// Create a variable and assign it an object:
let person = { name: 'Andrei' }

// Create a function that will attempt to modify
// the value it receives as an argument:
function changeNameFunc(person) {
  person.name = 'Viktor'
}

// Call the "changeNameFunc" function:
changeNameFunc(person)

// Log the value of "name" variable:
console.log(person)
// Output:
// { name: 'Viktor' } // <= The name is different.
Postado em Blog
Escreva um comentário