De volta ao básico: Tipos Primitivos e Objetos em Javascript
Tags: javascript, webdev, beginners, codenewbie
Published: 2021-04-01 00:03:54

E aí, galera 👋

Eu estava assistindo um vídeo de Programação Orientada a Objetos em JavaScript e achei que seria bom compartilhar o que estou aprendendo / revisando. É por isso que estou planejando uma série de postagens para cobrir alguns conceitos como uma forma de aprender mais sobre os tópicos e espero que também ajude algumas pessoas.

Neste artigo, abordarei os tipos de Javascript e suas diferenças.

## Tipos do Javascript

Existem oito tipos de dados em Javascript:

1. string
2. number
3. bigint
4. boolean
5. undefined
6. null
7. symbol
8. Object

Os primeiros 7 deles são comumente chamados de **Tipos primitivos** e todo o resto são **Tipo objeto**.

## Tipos primitivos

Eles só podem armazenar um único dado, não têm métodos e são **imutáveis**.

_Espera ae, como assim? Eles são mutáveis ..._ Na verdade, eles não são. Normalmente confundimos o próprio valor primitivo com a variável que atribuímos ao valor primitivo. Dá uma olhada:

```javascript
// A gente nao pode modificar a string
let car = "car"
console.log(car) // car
car.toUpperCase()
console.log(car) // car
car[0] = "b"
console.log(car) // car


// Mas podemos atribuir um novo valor à mesma variável
car = car.toUpperCase()
console.log(car) // CAR
```

A variável pode ser reatribuída a um novo valor, mas o valor primitivo existente não pode ser alterado como fazemos com arrays ou objetos.

Portanto, esta é uma das principais diferenças entre os dois tipos:
**Tipos primitivos são imutáveis e tipos de objetos são mutáveis**.

_Aah, beleza. Entendi! Mas como é que eles não têm métodos, se você acabou de usar um? _

Esse é outro ponto interessante! `Tipos primitivos` não têm métodos, mas, exceto pelo `null` e `undefined`, todos eles têm objetos equivalentes que envolvem os valores primitivos, assim podemos usar métodos.

Para o tipo primitivo `string` existe o objeto `String`, para a primitiva `number` existe `Number`, e portanto existem `Boolean`, `BigInt` e `Symbol`.

Javascript converte automaticamente os primitivos em seus objetos correspondentes quando um método for invocado. Javascript envolve o primitivo e chama o método.

Veja abaixo como um objeto `String` é estruturado com seu valor primitivo e `__proto__` (que está além do nosso escopo aqui, mas está relacionado ao prototype do seu objeto) com os métodos associados:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gi0fw65yc3tpid5ioapj.JPG)

É assim que podemos acessar propriedades como `length` e métodos como `indexOf` e `substring` ao trabalhar com tipos primitivos `string`.

Quando o Javascript os envolve com seus objetos correspondentes, ele chama o método `valueOf` para converter o objeto de volta ao valor primitivo quando o Javascript encontra um objeto onde um valor primitivo é esperado.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wv9127ydlnuj4h5wuzgv.JPG)

## Tipos de objeto

Diferentemente dos tipos primitivos, os objetos podem armazenar coleções de dados, as suas propriedades, e são **mutáveis**.

```javascript
// Podemos modificar os objetos sem precisar reatribui-los à variáveis
let cars = ["bmw", "toyota"]
console.log(cars) // ["bmw", "toyota"]
cars.push("tesla")
console.log(cars) // ["bmw", "toyota", "tesla"]

let car = { brand: "tesla" }
car.year = 2021
console.log(car) // { brand: "tesla", year: "2021" };
```

Exemplos de tipos de `Objeto` são Array e o próprio Object. Diferente dos `Tipos primitivos`, eles possuem métodos embutidos. Dá para ver abaixo como um array e um objeto são mostrados aqui no navegador com alguns de seus métodos:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5agoeju671l6h0mx3xd.jpeg)

Por mais estranho que pareça, `funções` são na verdade objetos também, são objetos `Function`, que podem ser chamados.

Só para ilustrar isso e por curiosidade, veja como as funções também podem ser criadas:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pt8nz01421zuy7nryrbo.png)

Isso é apenas para fins educacionais, uma vez que não é recomendado usá-lo dessa forma e há problemas com closures, conforme mostrado [aqui](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function).

Beleza, aprendemos um pouco mais sobre esses tipos, então vamos ver algumas das diferenças ao trabalhar com eles.

# Diferenças entre os tipos

### 1. Atribuindo a uma variável e copiando o valor

A diferença na forma como os valores são armazenados nas variáveis é o que faz as pessoas geralmente chamarem `Tipos de objeto` como `Tipos de referência`.

#### Tipos primitivos

Quando atribuímos um tipo primitivo a uma variável, podemos pensar nessa variável como _contendo_ aquele valor primitivo.

```javascript
let car = "tesla"
let year = 2021

// Variável - Valor
// car      - "tesla"
// year     - 2021
```

Portanto, quando atribuímos essa variável a outra variável, estamos copiando esse valor para a nova variável. Assim, **tipos primitivos são "copiados por valor"**.

```javascript
let car = "tesla"
let newCar = car

// Variável - Valor
// car      - "tesla"
// newCar   - "tesla"
```

Uma vez que copiamos os valores primitivos diretamente, **ambas as variáveis são separadas e se mudarmos uma não afetamos a outra**.

```javascript
let car = "tesla"
let newCar = car

car = "audi"

// Variável - Valor
// car      - "audi"
// newCar   - "tesla"
```

#### Tipos de objeto

Com `Tipos de Objeto` as coisas são diferentes. Quando atribuímos um objeto a uma variável, a variável recebe uma **referência** para esse valor. Esta referência armazena o `endereço` para a localização desse valor na memória (tecnicamente mais do que isso, mas vamos simplificar). Portanto, a variável não tem o valor em si.

Vamos imaginar a variável, o valor que ela armazena, o endereço na memória e o objeto nos trechos abaixo:

```javascript
let cars = ["tesla"]

// Variável  - Valor                 - Endereço - Objeto
// cars      - <#001> (A referência) - #001      - ["tesla"]
```

Desta forma, ao atribuirmos esta variável a outra estamos dando a ela a referência do objeto e não copiando o próprio objeto como acontece com o valor primitivo. Assim, **tipos de objetos são "copiados por referência"**.

```javascript
let cars = ["tesla"]
let newCars = cars

// Variável  - Valor                 - Endereço - Objeto
// cars      - <#001> (A referência) - #001     - ["tesla"]
// newCars   - <#001> (A referência tem o mesmo endereço)

cars = ["tesla", "audi"]

// Variable  - Valor                  - Endereço - Objeto
// cars      - <#001> (A referência) - #001     - ["tesla", "audi"]
// newCars   - <#001> (A referência tem o mesmo endereço)

console.log(cars) // ["tesla", "audi"]
console.log(newCars) // ["tesla", "audi"]
```

Ambos têm referências ao mesmo objeto array. Então **quando modificamos o objeto de uma das variáveis a outra também terá essa mudança**.

## 2. Comparação

Entender as diferenças do que é armazenado nas variáveis ao lidar com tipos primitivos e de objeto é crucial para entender como podemos compará-los.

#### Tipos primitivos

Usando o operador de comparação estrita `===`, se compararmos duas variáveis que armazenam valores primitivos **elas serão iguais se tiverem o mesmo valor**.

```javascript
let year = 2021
let newYear = 2021

console.log(year === 2021) // True
console.log(year === newYear) // True
```

No entanto, se compararmos duas variáveis que foram definidas como `Tipos de objeto`, estaremos na verdade comparando duas referências em vez de seus objetos. Portanto, **eles são iguais apenas se referirem exatamente ao mesmo objeto**.

```javascript
let cars = ["tesla"]
let newCars = ["tesla"]

console.log(cars === newCars) // False
console.log(cars === ["tesla"]) // False

// Agora copiamos a referência de cars para newCars
newCars = cars
console.log(cars === newCars) // True
```

Mesmo que, no início do trecho de código estivéssemos trabalhando com o mesmo conteúdo nos arrays, as variáveis não tinham as mesmas referências, elas tinham referências a diferentes objetos de array na memória. Porém, após copiarmos a referência para `newCars`, já que agora eles estão "apontando" para o mesmo objeto, a avaliação é `True`.

Portanto, para comparar objetos, não podemos simplesmente usar o operador `===` porque, embora eles possam ter as mesmas propriedades, eles podem não fazer referência ao mesmo objeto. Existem algumas maneiras de fazer isso e, por isso, recomendo a leitura deste [artigo](https://dmitripavlutin.com/how-to-compare-objects-in-javascript/) (que está em inglês).

## 3. Passando para funções

Quando passamos tipos primitivos ou de objetos para funções, é como se estivéssemos copiando seus valores/referências para os parâmetros das funções, da mesma maneira que estivéssemos atribuindo a eles com `=`.

Como vimos que quando os atribuímos a novas variáveis estamos ou copiando seu valor (para tipos primitivos) ou referenciando (para tipos de objeto), é mais fácil entender o que acontece com funções e seu escopo externo.

#### Tipos primitivos

Quando estamos passando `Tipos primitivos` para funções, estamos copiando seus valores para os parâmetros das funções, assim isso não afeta a variável inicial no escopo externo.

```javascript
let year = 2021
function getYearWithoutCovid (freeYear) {
    freeYear = 2022
    return freeYear
}

const newYear = getYearWithoutCovid(year)
console.log(year) // 2021
console.log(newYear) // 2022
```

Passando `ano` para a função, estamos copiando seu valor para o parâmetro da função (`freeYear` será 2021), assim a variável original não seja afetada.

#### Tipos de objeto

Com `Tipos de objeto`, estamos copiando suas referências ao passá-los como parâmetros de funções. Portanto, se alterarmos o objeto dentro da função isso também será sentido no escopo externo.

```javascript
let person = { name: "Paul", status: "unemployeed" }
function getAJob (person) {
    person.status = "employeed"
    return person
}

const newPerson = getAJob(person)
console.log(person) // { name: "Paul", status: "employeed" }
console.log(newPerson) // { name: "Paul", status: "employeed" }
```

Quando passamos `pessoa` para a função, estamos copiando sua referência para o parâmetro da função, não seu valor de objeto. Alterá-lo dentro da função afetará o objeto inicial no escopo externo, uma vez que ambas as variáveis têm referências ao mesmo objeto.

É por isso que é recomendado usar `Funçoes puras` neste caso (que não estão no escopo deste artigo, mas eu encorajo você a pesquisar sobre isso <3). Para isso, criamos uma cópia local dessa `pessoa` dentro da função e a modificamos ao invés do objeto passado.

# Conclusão

Espero que com este artigo você possa entender um pouco mais sobre os tipos de dados em Javascript e também aprender as principais diferenças entre eles.

Eu apenas tentei compartilhar o que aprendi ao revisar esses conceitos, então há mais coisas a acrescentar, mas achei que essa era uma forma didática de explicar. Se você tem coisas a acrescentar e discutir, deixe um comentário :) Se isso te ajudou de alguma forma, deixe um coração <3

Além disso, me segue no [Twitter] (https://twitter.com/CR_Rafael22) se quiser, talvez compartilhe coisas legais lá também :)

# Referências

https://262.ecma-international.org/11.0/#sec-ecmascript-data-types-and-values
https://flaviocopes.com/difference-primitive-types-objects/
https://dmitripavlutin.com/value-vs-reference-javascript
https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
https://codeburst.io/javascript-essentials-types-data-structures-3ac039f9877b#01e0
https://mattgreer.dev/blog/javascript-is-a-pass-by-value-language/
https://developer.mozilla.org/en-US/docs/Glossary/Primitive
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf