(a==1 && a==2 && a==3) true? Magia de JavaScript

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: El problema que explotó Stack Overflow

\n\n

Imagina la situación: estás en una entrevista y el entrevistador, con una sonrisa astuta, pregunta: «¿Puede la expresión a == 1 && a == 2 && a == 3 devolver alguna vez true?». La primera reacción es «¡Claro que no! Una variable no puede ser igual a tres números diferentes al mismo tiempo».

\n\n

Sin embargo, si conoces JavaScript un poco más a fondo, sabes que en este lenguaje son posibles las cosas más inesperadas. Esta pregunta ha acumulado miles de visitas en Stack Overflow y se ha convertido en un clásico para evaluar la comprensión de la coerción de tipos, los getters y los wrappers de objetos en JS.

\n\n

En este artículo no solo demostraremos que es posible, sino que también analizaremos 5 formas diferentes de hacer que (a==1 && a==2 && a==3) devuelva true. Aprenderás cómo funciona la comparación no estricta (==), en qué se diferencia de la estricta (===), y cómo se puede controlar.

\n\n

El artículo será útil tanto para principiantes que quieran entender más a fondo JavaScript, como para desarrolladores experimentados que se preparan para una entrevista. ¡Allá vamos!

\n\n

Método n.º 1: La magia de valueOf (el más popular)

\n\n

La forma más elegante y citada con frecuencia es usar un objeto personalizado con el método valueOf redefinido. En JavaScript, cuando comparas un objeto con un primitivo mediante ==, el motor llama automáticamente a valueOf() (o toString()) para convertir el objeto en un primitivo.

\n\n

Crearemos un objeto contador que, cada vez que se llame a valueOf, incrementará su valor en 1.

\n\n
const a = {\n  value: 1,\n  valueOf() {\n    return this.value++;\n  }\n};\n\nconsole.log(a == 1 && a == 2 && a == 3); // true
\n\n

Cómo funciona:

\n
    \n
  • En la primera comparación a == 1, se llama a valueOf(), devuelve 1, y value se incrementa a 2.
  • \n
  • En la segunda comparación a == 2, se vuelve a llamar a valueOf(), devuelve 2, y value se convierte en 3.
  • \n
  • La tercera comparación a == 3 completa la cadena con éxito.
  • \n
\n\n

Este es un ejemplo perfecto de type coercion (coerción forzada de tipos) en acción. Este mecanismo es lo que hace que JavaScript sea un lenguaje a la vez flexible y peligroso.

\n\n

Método n.º 2: toString — el gemelo de valueOf

\n\n

Si redefines toString en lugar de valueOf, el resultado será el mismo. JavaScript en una comparación no estricta puede usar cualquiera de estos métodos para convertir el objeto.

\n\n
const a = {\n  value: 1,\n  toString() {\n    return this.value++;\n  }\n};\n\nconsole.log(a == 1 && a == 2 && a == 3); // true
\n\n

La diferencia es sutil: valueOf tiene prioridad para comparaciones numéricas, mientras que toString la tiene para las de cadenas. Pero en nuestro caso, ambos son perfectamente adecuados.

\n\n

Método n.º 3: Getters en el ámbito global

\n\n

Muchos ni siquiera sospechan que las variables globales en JavaScript son en realidad propiedades del objeto global (window en el navegador, global en Node.js). Y las propiedades se pueden definir mediante getters (getter).

\n\n

Podemos definir un getter para la variable a directamente en el objeto global:

\n\n
let value = 1;\nObject.defineProperty(window, 'a', {\n  get() {\n    return value++;\n  }\n});\n\nconsole.log(a == 1 && a == 2 && a == 3); // true
\n\n

Este método es especialmente interesante porque a se comporta como una variable normal, aunque en realidad es una propiedad con un getter. Cada vez que accedes a a, el getter devuelve un nuevo valor.

Blogs

Book Recommendations