• No todo es un objeto en JavaScript

    22 de julio de 2022

    - 4 min read

  • Decir que en JavaScript “todo es un objeto” es incorrecto… No “todo hereda de Object” en el lenguaje, aunque lo parezca. No sé de dónde salió esa idea pero… es un error pensar esto.

    Tipos de datos en JavaScript

    Según la especificación de ECMAScript, los tipos de datos disponibles en JavaScript se separan en dos grupos: primitivos y objetos.

    Los primitivos son: undefined, null , Boolean, Number, String, BigInt y Symbol.

    Los primitivos representan un dato en la implementación más baja posible.

    Todo lo que no es un primitivo, es un objeto. No sólo el propio Object. También los Array, Set, Map, Date, Regex, Promise y todo lo que se te ocurra.

    Las Function también son objetos. Con la particularidad que pueden ser invocados (ejecutados).

    typeof para salir casi de dudas

    Para saber el tipo de dato en JavaScript podemos usar el operador typeof. De hecho, este operador es el que justamente viene a desmentir que todo sea un objeto aunque tiene algunas particularidades que luego comentaremos.

    typeof 2; // number
    typeof true; // boolean
    typeof "midu"; // string
    typeof undefined; // undefined
    typeof Symbol(); // symbol
    typeof 2n; // bigint
    
    typeof null; //object * (caso especial)
    
    typeof {}; // object
    typeof []; // object
    typeof new Map(); // object
    typeof new Set(); // object
    typeof /abc/; // object

    Como podemos ver, los tipos primitivos indican su tipo esperado… excepto null. En este caso, el tipo es object. Esto es por un bug histórico en el lenguaje que no se ha solucionado para no romper la retro compatibilidad con código ya existente. Lo correcto sería que el typeof diese null, ya que se trata de un tipo primitivo, tal y como indica la especificación..

    Existe otro caso especial. Las funciones. Tienen su propio typeof, ya que son un caso especial de object ya que son llamables (ejecutables).

    typeof function () {}; // function

    null por un error histórico tiene como typeof object. Pero no es que sea un object ni que herede de él. No se arregla el bug para evitar problemas de retrocompatibilidad. Es lo que tiene la web.

    Boxing de tipos primitivos

    ¿Dónde viene entonces la confusión de que todo es un objeto en JavaScript? La confusión viene porque los primitivos parecen tener propiedades y métodos.

    Por ejemplo, puedes acceder a la longitud de un objeto o transformar un número en un string con decimales:

    "Esto es un primitivo".length; // 20
    "Esto también"
      .toUpperCase()(
        // 'ESTO TAMBIÉN'
    
        2
      )
      .toString()(
        // '2'
        2
      )
      .toFixed(2); // '2.00'
    
    true.toString(); // 'true'
    true.valueOf(); // true

    Lo que ocurre es que JavaScript envuelve el tipo primitivo automáticamente. Aunque en la especificación no habla de un término en concreto, se le conoce como boxing y sería, según la especificación, una forma de coerción implicita para transformar el dato primitivo en objeto al vuelo.

    Aunque creas que la coerción sólo es para transformar de un tipo a otro, grandes autores como Kyle Simpson consideran que este “boxing” también sería una coerción al ser una transformación automáticamente del lenguaje.

    Así que la confusión viene por pensar que el primitivo de string es lo mismo que la instancia de usar el objeto String que nos da automáticamente JavaScript para facilitarnos la vida.

    const n = 2;
    typeof n; // number
    n instanceof Number; // false
    
    const n2 = new Number(2);
    typeof n2; // number
    n2 instanceof Number; // true
    
    n === n2; // false

    Y no lo es. No son iguales, ni son del mismo tipo. Y, claro, el primero no hereda de object .

    "hola" === new String("hola"); // false
    2 === new Number(2); // false
    true === new Boolean(true); // false

    Ojo. No confundas usar el new Boolean(true) con simplemente usar Boolean(true). El primero crea una instancia del objeto Boolean y el segundo sólo hace una conversión del parámetro al tipo primitivo boolean.

    true === Boolean(true); // true
    true === Boolean(1); // true
    true === Boolean(true); // true

    Los objetos son una parte MUY importante en JavaScript pero eso no significa que TODO sea un objeto.

    Conclusiones

    Por la coerción de datos, o el boxing que hemos visto, puede parecer todo es un objeto, ya que simula que podemos acceder a propiedades y métodos de un primitivo.

    En realidad es JavaScript simplificando su sintaxis para que no tengas que hacerlo tu mismo. Es obvio que los tipos primitivos no son objetos, ya que, si lo fuesen, sus valores serían mutables también (y no lo son).

    Esto es en JavaScript… hay que tener en cuenta que en otros lenguajes de programación esto puede ser diferente. ¡Tenlo en cuenta!