Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 24 de mayo de 2022
Esoterismo

A veces no es necesario usar lenguajes esotéricos para crear un código que nadie sea capaz de entender a simple vista... de hecho, lo hacemos muy frecuentemente en nuestro día a día 😉. Basta nombrar inapropiadamente unas cuantas variables, acoplar y desacoplar sin criterio o usar una mala indentación para que nuestro código ya venga "ofuscado" de serie, sin usar ninguna herramienta externa.

Sin embargo hay otro nivel de maldad, que consiste en el abuso de la flexibilidad de sintaxis en algunos lenguajes para construir expresiones diabólicamente enrevesadas. Hace poco me topé por casualidad con un buen ejemplo de ello en JavaScript, un código que, a simple vista, es imposible de entender:

// ¿Qué retorna esta expresión?
(_$=($,_=[]+[])=>$?_$($>>+!![],($&+!![])+_):_)(255)

Obviamente, podemos copiarla y pegarla en la consola de nuestro navegador, y rápidamente veremos de qué se trata. Sin embargo, me pareció interesante dedicar unos minutos a intentar comprender el código, así que vamos a ir troceando y refactorizando esta expresión ilegible hasta convertirla en algo que, al menos, podamos digerir.

Para empezar, vamos a fijarnos en la estructura general de la expresión. Se trata claramente de la definición de una función, que invocamos sobre la marcha pasándole el valor 255 como argumento:

(_$=($,_=[]+[])=>$?_$($>>+!![],($&+!![])+_):_)(255)
(       ...definición de la función...       )(255)

Por tanto, ya podemos centrar el foco en la función que vamos a desenmarañar, de nombre _$, que renombramos a func para facilitar la lectura:

func=($,_=[]+[])=>$?func($>>+!![],($&+!![])+_):_

Si observamos un poco, en medio de la aberración podemos encontrar algunas expresiones constantes, cuya codificación llevan al límite la flexibilidad del sistema de tipos de JavaScript con la clara intención de hacer el código menos legible:

  • []+[]. Esta expresión retorna una cadena vacía "", pues está intentando concatenar el contenido textual de dos arrays sin elementos.
  • +!![]. Vayamos por partes: !![] es una forma demoníaca de escribir la constante lógica true. Dado que el array vacío se considera "truthy", la doble negación simplemente obra la conversión a booleano. Finalmente, al anteponerle un signo de suma, convertimos el valor en numérico, por lo que pasa a ser 1.

Reescribamos de nuevo la función sustituyendo estos valores, a ver si empezamos a vislumbrar algo:

func=($,_=[]+[])=>$?func($>>+!![],($&+!![])+_):_
func=($,_= ""  )=>$?func($>>1    ,($&  1  )+_):_

Bueno, no es que haya mejorado de forma espectacular, pero al menos podemos ver algunos árboles en medio del bosque. Se trata de una lambda o arrow function a la que se asigna el nombre _$.

Los dos parámetros de la función, llamados $ y _ (el segundo de ellos tiene el valor por defecto "") están nombrados también de forma diabólica con objeto de contribuir al despiste, pero podemos renombrarlos para intentar mejorar la legibilidad: dado que el primero de ellos se usa en una expresión $>>1, podemos inferir que es un número, por lo que le llamaremos num; el segundo, dado que su valor por defecto es la cadena vacía, podemos llamarlo str sin temor a equivocarnos. Aprovecharemos también para introducir algunos espacios que contribuyan a simplificar su lectura

func=($,_=  "" )=>$?func($>>1    ,($&  1  )+_):_
func = (num, str="") => num? func(num >> 1, (num & 1) + str): str

¡Esto ya es otra cosa! Claramente, func es una función recursiva (se llama a sí misma) que finalizará cuando num sea cero, retornando la cadena almacenada en str. En cada iteración, insertamos en str el valor del bit menos significativo de num, y volvemos a llamar a la función desplazando los bits a la derecha, lo que equivale a una división entre dos).

Ahora sí, podemos responder a la pregunta que nos hacíamos algo más arriba:

// ¿Qué retorna esta expresión?
(_$=($,_=[]+[])=>$?_$($>>+!![],($&+!![])+_):_)(255)

¡Se trata de un conversor de decimal a binario!

(dec2bin = (num, str="") => num? dec2bin(num >> 1, (num & 1) + str): str)(255)
> '11111111'

(dec2bin = (num, str="") => num? dec2bin(num >> 1, (num & 1) + str): str)(170)
> '10101010'

(dec2bin = (num, str="") => num? dec2bin(num >> 1, (num & 1) + str): str)(3)
> '11'

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!