# Tipos y objetos en JavaScript

En JavaScript tenemos varios tipos de datos; *booleanos*, numéricos, los de texto o *string*, el null que indica que una variable no tiene un valor, *undefined* que indica que una variable está declarada en memoria, pero su valor aún no ha sido definido y el *symbol*, el cual fue introducido en la definición de EcmaScript 6. A partir de aquí todo lo demás son objetos, como son los arreglos o *arrays*, las fechas o incluso objetos que tú mismo declares. 
## Diferencias entre valores primitivos y objetos

1. Los valores primitivos son inmutables.
2. No se pueden declarar propiedades a valores primitivos.
3. Los objetos se manejan por referencias por lo que dos objetos diferentes que tengan los mismos valores no serán iguales.

Para entender esto veamos el siguiente ejemplo:

```javascript
let str = "yay";
console.log(str[0]); // "y"
console.log(str.length); // 3

str[0] = "p";
console.log(str);
```

Analizando el código anterior ¿Cuál será el segundo resultado de la consola? De una el resultado sigue siendo `"yay"`, pero ¿Por qué pasa esto?

Primero tenemos que entender que los *strings* son valores primitivos y como se ha mencionado los valores primitivos son inmutables, o sea que no se pueden modificar. Cuando se intenta acceder a una propiedad de un valor primitivo como en el caso de `str[0]`, **JavaScript envuelve el valor en un objeto**.

Lo anterior puede resultar un poco confuso, pero veamos cómo sería el código enfocándonos cuando JavaScript envuelve los valores primitivos en un nuevo objeto para que tenga sentido la acción que le damos a realizar.

```javascript
let str = "yay";
console.log(new String(str)[0]); // "y"
console.log(new String(str).length); // 3

new String(str)[0] = "p";
console.log(str);
```

1. Se define una variable `str` de valor primitivo *string*.
2. Se intenta acceder al *key* 0 de la variable `str`, como no es posible declarar propiedades a valores primitivos JavaScript lo envuelve en un objeto de tipo *string*, se accede al valor y lo imprime dando resultado `y`.
3. Hace lo mismo del paso anterior, pero accede a la propiedad `length`.
4. Se crea otro objeto de la variable `str` donde la key 0 es igual a `p`. Es como escribir 1+1 en medio de una ejecución.
5. Imprime la variable str que es igual a `yay`.

Cuando comparamos objetos, estos se comparan por referencia. ¿Esto qué significa? Cada objeto, a pesar de que puedan lucir similares o iguales, ellos son diferentes, cada uno tiene su propia identidad. A la hora de comparar dos objetos vamos a obtener un valor negativo, ya que ellos no son iguales. A diferencia los valores primitivos, los cuales ellos no tienen su propia identidad, vamos a obtener un resultado positivo.

Una forma simple de ver que los objetos tienen identificador propio está en palabra *new*, lo veo como algo que cada vez que se invoca es algo nuevo distinto a lo demás y algo simple de comprobar de una manera más visual es comparando los datos.

```javascript
10 === 10; // true
"ave" === "ave"; // true
// new Object() es equivalente a {}
{} === {}; // false
[] === []; // false
```

¿Qué significa que los objetos se comparan por referencia? Tomemos el siguiente ejemplo:

```javascript
let obj = {};
let dos = obj;
let obj2 = {}
obj === dos // true
obj === obj2 // false
```

El resultado al comparar `obj` y dos es *true* porque en este caso sí es el mismo objeto porque apunta a la misma dirección de la memoria y `obj` con `obj2` es *false* porque ya no son el mismo objeto.

En caso de que queramos comparar el contenido de *arrays* u objetos es posible que lo que queramos usar sean los valores primitivos de *tuples* o *records*, valores que son primitivos que se añaden recientemente al lenguaje.

```javascript
#[] === #[] // true
#{} === #{} // true
```

## Coerción de datos

La coerción sucede cuando tenemos que **convertir un valor de un tipo de dato a otro tipo de dato**. La coerción puede suceder en ciertos escenarios automáticamente debido a que JavaScript es un lenguaje débilmente tipado, por ejemplo:

```javascript
true + 5 // 6
```

Aquí estamos sumando el valor booleano true con el número 5 y estamos recibiendo un resultado de 6. ¿Por qué sucede esto? Bueno, sucede porque JavaScript está convirtiendo el valor `true` a 1 para darle sentido a esta operación. En este caso, `true` nos retorna un 1. Al sumarse recibimos un 6.

```javascript
["abc"] + "abc" // abcabc
```

Aquí estamos sumando un arreglo el cual tiene un elemento de una cadena de texto que es `abc` con una cadena de texto sin el arreglo, de esta suma recibimos un `abcabc`. JavaScript convierte el arreglo a un *string* automáticamente y al sumar ambas cadenas de texto se concatenan.

La **coerción numérica**, generalmente sucede cuando tú intentas hacer alguna operación matemática, por ejemplo, en este caso:

```javascript
50 / "5" // 10
```

Aquí estamos dividiendo un número 50 entre un *string* que tiene un valor de 5. JavaScript convierte el *string* 5 a un número. Por lo tanto, recibimos un 10.

La **coerción de *strings*** generalmente sucede cuando se utiliza el operador de suma y alguno de los dos valores es un *string*. JavaScript asume se está intentando concatenar *strings*, entonces trata de convertir el otro elemento en *string* y los une, como puedes ver aquí.

```javascript
54 + "abc" // "54abc"
54 + "" // "54"
```

La **coerción de booleanos** sucede cuando se intenta, comparar o hacer alguna operación lógica.

```javascript
0 || 5 // 5
```

Aquí tenemos un operador *OR*, básicamente está diciendo o 0 o 5, y estamos recibiendo 5. El 0 está siendo convertido a un valor `false` y 5 está siendo convertido a un valor `true`. El operador *OR* siempre se va a inclinar por el valor `true` y por eso recibimos un 5.

## Igualdad de valores

En JavaScript existen los siguientes tipos de igualdad:

|  Igualdad    |      Representación      |
|--------------|--------------------------|
|  Abstracta   | a == b (doble igual)   |
|  Estricta    | a === b (triple iguales) |
|  Mismo valor | `Object.is(a, b)`          |

La **igualdad abstracta** es confusa porque al igual que lo vimos en la [coerción de datos](#coercion-de-datos "Coerción de datos") JavaScript convierte los valores a un tipo que tenga sentido.

```javascript
["abc"] == "abc" // true
```

Para tener una idea más clara de todos los resultados dependiendo de la operación puedes ver la [Tabla de igualdad de JavaScript](https://dorey.github.io/JavaScript-Equality-Table/):

![Tabla de igualdad de JavaScript](https://res.cloudinary.com/marcomadera/image/upload/f_auto,c_scale,w_500,h_481,dpr_auto/v1611100839/Blog/tipos-y-objetos-en-javascript/Coercion_dzxmpj.png "Tabla de igualdad de JavaScript")

Es difícil aprenderse la tabla anterior por lo que hacer este tipo de igualdades puede causar algunos errores en nuestro programa o algunos comportamientos inesperados si olvidamos un dato. ¿Cómo la evitamos?. Lo recomendable es que se use el operador de **igualdad estricta**. Este operador evita que los valores se conviertan al compararlo uno con el otro.

```javascript
["abc"] === "abc" // false
```

Puedes ver que ya obtenemos un valor `false`, ya que este *array* no está siendo convertido en un *string*.

La **igualdad del mismo valor con** `Object.is()` es muy similar a la igualdad estricta, con dos casos especiales, *Not a Number* y la igualdad de cero y cero con signo negativo.

```javascript
NaN === NaN // false
Object.is(NaN, NaN) // true

-0 === 0 // true
Object.is(-0, 0) // false
0 === -0 // true
Object.is(0, -0) // false
```

## Prototipos

En JavaScript todos los objetos tienen un prototipo. Lo podemos ver en la consola como `__proto__`, aquí podemos ver todas las funciones y argumentos que se pueden usar y al final de la lista puede que veamos de nuevo a `__proto__` hasta llegar al `__proto__: Object` porque todos los objetos de JavaScript heredan de este objeto creando la cadena de prototipos.

![Prototipo](https://res.cloudinary.com/marcomadera/image/upload/f_auto,dpr_auto/v1611104815/Blog/tipos-y-objetos-en-javascript/proto_uvgdvt.png)

Ventajas de los prototipos:

- Todas las funciones o propiedades que declaremos dentro del prototipo van a ser heredadas por todas las instancias de esta clase.
- Todas las instancias de esta clase van a apuntar al mismo prototipo por lo que podemos tener un sinfín de números de instancias, pero solamente un prototipo. Entonces no vamos a sobrecargar la memoria de la computadora.

Para crear una clase se utiliza un objeto función donde se pueden agregan las propiedades y se declaran los métodos utilizando `prototype` en lugar de como se vio en la consola `__proto__`. Con esto ya se pueden llamar instancias de la clase.

```javascript
function Persona(edad){
}
//Declarar métodos
Persona.prototype = {
  permisos: function permisos(){ 
      console.log(`tengo ${this.edad} y ${this.edad < 18 ? "no" : "sí"} puedo votar`);
  }
}
Persona.prototype.constructor = Persona;

function Estudiante(nombre, edad){
  Persona.call(this);
  this.nombre = nombre;
  this.edad = edad;
}
//Declarar métodos
Estudiante.prototype = Object.create(Persona.prototype, {
  decirNombre: {
    value:  function decirNombre(){console.log("Mi nombre es",this.nombre)}
  }
});
Estudiante.prototype.constructor = Estudiante;

// Crear instacias de la clase
let estudiante = new Estudiante("Juanito",17);
estudiante.decirNombre();
estudiante.permisos();
```

Desde la especificación de ECMAScript 6 se introdujo la sintaxis de las clases. Es una transformación de la sintaxis de prototipos para hacer más cómoda la declaración de clases, *syntactic sugar* para prototipos.

```javascript
class Persona {
  //Declarar métodos
  permisos(){ 
    console.log(`tengo ${this.edad} y ${this.edad < 18 ? "no" : "sí"} puedo votar`);
  }
}

class Estudiante extends Persona {
  constructor(nombre, edad) {
    super();
    this.nombre = nombre;
    this.edad = edad;
  }
  //Declarar métodos
  decirNombre(){
    console.log("Mi nombre es:", this.nombre);
  }
}

// Crear instacias de la clase
let estudiante = new Estudiante("Marco", 17);
estudiante.decirNombre();
estudiante.permisos();
```

Se puede observar las similitudes a los prototipos, se declara la clase, dentro tiene el método constructor donde recibe las propiedades y se declaran los métodos. Ahora al ver la estructura del objeto vemos lo siguiente donde la izquierda es creada en forma de prototipo y la derecha en forma de clase.

![Prototipos vs Classes](https://res.cloudinary.com/marcomadera/image/upload/f_auto,dpr_auto/v1611633447/Blog/tipos-y-objetos-en-javascript/Proto-vs-Classes_xdvwjy.png)

Se puede observar que la diferencia es que en el constructor una es de tipo función y la otra dice `class`, pero al ver el tipo se puede ver que por detrás la clase es una función.

## Conclusión

Hemos visto el comportamiento de los tipos primitivos y las características de los objetos de JavaScript. Los objetos están por todas partes y los revisamos por encima sin tocar métodos y otros detalles. También vimos la forma tradicional de crear clases y la que fue implementada con ES6.

