Variables, tipos de dato, scope.
Tags: javascript, beginners, programming
Published: 2023-06-07 03:44:54

Hace casi dos años [escribía un post sobre el mismo tema](https://dev.to/voidrizoma/javascript-variables-tipos-de-dato-scope-y-el-hoisting-1fhb), pero en estos meses, he descubierto que además de la importancia de escribir en español. Que siempre es un mar de información en el que te tienes que andar metiendo para entender cosas, sobre todo con JS, que es raro, pero así lo queremos <3. 

## Tipo de datos

Los tipos de datos son importantes en cualquier lenguaje de programación. En el caso de JS, tenemos dos clases de datos: los primitivos y los de tipo objeto.

### Datos primitivos
* Booleano (Boolean): Pueden representar dos valores lógicos: verdadero (true) y falso (false).
* Null: Representa un valor nulo. 
* Undefined: Representa una variable que no ha sido asignada por un valor. 
* Número (Number): Como su nombre lo indica, podemos representar cualquier número.
* BigInt: En JS tenemos dos tipos de números, el BigInt representa un valor exacto como los exponentes, algo así como: 2n ** 53n.
* Cadena de texto (String): Representa datos textuales, por ejemplo: "Hola olla".
* Symbol

### Datos tipo objeto
* Objetos
* Arreglos
* Funciones

Los objetos y arreglos son los que conocemos de siempre.
* Tuplas
* Records

Éstos tipos de dato son nuevos y son muy similares al objeto y al array, pero cambia en unas cosas. Son inmutables, se trabaja con ellos por su valor y no por referencia... y tienen otras cosas medio chulas de las que iré escribiendo en otros posts.

## Variables

Se pueden definir de dos maneras: 1) una variable es un espacio almacenado en la memoria, 2) una variable es un contenedor para algún dato o valor. En JS tenemos tres formas de declarar estos valores: 

* var
* let
* const

#### var 

Antes de EcmaScript 6 teníamos una manera de declarar nuestras variables y era con la palabra reservada "var". Pero eso nos daba un par de problemas; el primero era que podíamos
duplicar las declaraciones y podríamos reasignar los valores. Suena a que no es un problema, pero si no teníamos el cuidado suficiente al trabajar nuestras variables, éstas podrían cambiar sin darnos cuenta. Ya que "var" nos permite la reasignación y la redeclaración. 

```JS
//var

var sayHello = "Hola olla"

console.log(sayHello) // "Hola olla"

var sayHello= "Hi!"

console.log(sayHello) // "Hi"
```

#### let 

Con la llegada de ES6, el problema de reasignación de variables con var, termina. Especialmente cuando usamos la palabra reservada let dentro de las llaves { }. Así que además de darnos una keyword nueva, nos introduce a un nuevo scope; llamado: Block scope o alcance de bloque. 

```JS
//let 

let sayHello = "Hola olla"

console.log(sayHello) // "Hola olla"

sayHello = "Hi!"
console.log(sayHello) // "Hi"

let sayHello= "Hi!"

console.log(sayHello) // error: Identifier 'sayHello' has already been declared
```

#### const

La palabra reservada "const" nos ayuda a guardar valores que nunca tendrán que reasignarse. 

```JS
// const

const sayHello = "Hola olla"

console.log(sayHello) // "Hola olla"

sayHello = "Hi!" // error: Assignment to constant variable. 

const sayHello= "Hi!" // error: Identifier 'sayHello' has already been declared
```

"const" es interesante, porque mientras lo relacionamos con variables constantes o que no cambian, en realidad no es tan así. De hecho se comporta diferente dependiendo el tipo de dato. En los datos primitivos tiene mucho sentido que con esta forma de declarar la variable, nos sea imposible reasignarla. Por lo que pareciera que "const" es de constante o para declarar variables inmutables, pero no es así. 

En los datos primitivos, las variables con "const" funcionan como de sólo lectura. Pero en los datos de tipo objeto, podemos cambiar propiedades del valor asignado a la variable. ¿khá? 

```JS

const gato = { name: "Simón" }

//Si queremos cambiar el nombre 
//NO tenemos que hacer lo siguiente

gato = { name: "Tito" }

// De la forma anterior, no estamos cambiando 
//una propiedad del valor asignado, estamos 
//reasignando el valor de const. Entonces, 
//para poder cambiar una propiedad, 
//lo tenemos que hacer de la siguiente manera: 

gato.name = "Tito"
console.log(gato.name) // "Tito"

// Ejemplo con arreglos

const frutas = ["Manzana", "Pera", "Fresa", "Melón"]
console.log(frutas[2]) // "Fresa"
frutas[2] = "Uvas"
console.log(frutas[2]) // "Uvas"

```

## Scope

Ya llega la hora de hablar del scope. Entender el scope nos ayudará mucho a la hora  no solo de declarar nuestras variables, sino a entender muchas cosas a la hora de programar y pensar cómo solucionar nuestros problemas técnicos. El scope determina el alcance  de nuestras variables. Con JS tenemos tres tipos de scope: 1) global, 2) scope de función y 3) scope de bloque. Aunque en realidad podríamos agregar otros dos: local y de módulo.

* Alcance global: Cuando declaramos variables fuera de cualquier función, automáticamente se convierte en una variable de alcance global.

* Alcance de función: Cada vez que creamos funciones, se crea automáticamente un nuevo scope dentro de la nueva función. Eso significa que cada vez que declaramos variables dentro de una función, esa variable tendrá alcance o visibilidad dentro de esa función y no se podrá acceder a ella fuera de la función.

* Alcance de bloque: el alcance de bloque llega a JS en 2015 con ES6, cuando se introducen let y const. Esto significa que cualquier variable declarada dentro de las llaves { } sólo pueden ser visibles dentro de ellas. 

* Alcance de Módulo: Cuando creamos un nuevo módulo, todas las variables que estén dentro del archivo aunque se encuentren fuera de funciones o llaves, en teoría podrían ser consideradas como variables globales; sin embargo, esas variables sólo tienen alcance a nivel de módulo, excepto cuando importamos explícitamente el módulo en otro archivo. 

## Extra || Valor o referencia en JS

Cuando declaramos una variable, ésta se almacena en algún lugar de la memoria. Pero, funciona diferente para los datos primitivos y los de tipo objeto. Así que vamos por un ejemplo: 

```JS
let a = 1
console.log(a) // 1
let b = b
console.log(b) // 1
a = 2 
console.log(a) // 2
console.log(b) // 1

```
Acá pasa algo bien raro, bueno no raro, pero sí interesante. Cuando declaramos una variable y le asignamos un valor, éste queda almacenado en un lugar, pero cuando le asignamos la primera variable a otra variable, se crea una copia del valor y al mismo tiempo se vuelven independientes una de la otra; por lo tanto, por más que modifiquemos "a", el valor de "b" seguirá siendo 1.

Pero en el caso de los objetos pasa algo curioso:
 
```JS
const fruta = { name: "Fresa", color: "red" }
```
Ahí ya tenemos una variable que guarda un objeto. Que como ya sabemos, podemos modificar las propiedades de ese valor de fruta. Pero, a diferencia de los valores de los datos primitivos, en los objetos no se crean copias, lo que hace JS es apuntar al espacio de memoria donde guarda ese objeto y modifica sus propiedades. Entonces, si modificamos muchas veces las propiedades del objeto fruta, modificaremos los valores del objeto original, porque los cambios de hacen por referencia a ese espacio de memoria. 

```JS
const fruta = { name: "Fresa", color: "red" }
const fruta2 = fruta
console.log(fruta) // { name: "Fresa", color: "red" }
console.log(fruta2) // { name: "Fresa", color: "red" }

fruta.name = "Manzana"
console.log(fruta) // { name: "Manzana", color: "red" }
console.log(fruta2) // { name: "Manzana", color: "red" }
```
Así que cuando trabajemos con datos primitivos podemos estar seguros que manejamos valores, mientras que cuando usemos objetos, estaremos trabajando con referencias. Es por ello que es importante conocer conceptos como inmutabilidad o la importancia de la inclusión de Records y Tuplas como nuevos tipos de dato.