74

¿Cómo funciona este código? Quisiera una explicación detallada si es posible y saber dónde hay documentación al respecto.

if (!+[]+!+[] == 2) {
  document.write('Somos iguales');
}
else {
  document.write('Somos distintos');
}
Rubén
  • 10,857
  • 6
  • 35
  • 79
Eduardo Sebastian
  • 4,908
  • 7
  • 30
  • 70

5 Answers5

58

Un Array vacio es igual a false

[] == false

el contrario de false es true

!false == true

true es igual a 1, false es igual a 0

true == 1
false == 0

entonces

true + true == 2

function probar(){
    console.log([] == false);
    console.log(!false == true);
    console.log(true == 1)
    console.log(false == 0)
    console.log(true + true == 2)
}
<button onClick="probar()">probar</button>
ElChiniNet
  • 3,215
  • 9
  • 25
Jorge Arturo Juarez
  • 2,813
  • 1
  • 11
  • 24
  • 2
    Muchísimas gracias , me podrias decir donde aprender javascript practicamente y de buena forma? porfavor – Eduardo Sebastian Apr 28 '17 at 23:20
  • 4
    `Herramientas para desarrolladores -> Console` y trastea con todos los sitios... – Jorge Arturo Juarez Apr 28 '17 at 23:25
  • Sí `console.log([])` devuelve `false`, `console.log(! [])` debería dar como resultado `true` , **pero no es así , ¿por qué?** – Dev. Joel Apr 28 '17 at 23:33
  • @dev.joel al parecer []==false, se refiere a que esta vacio, pero Boolean([])==true indica que el elemento existe, por que Boolean(null) da false, el operador ! es equivalente a !Boolean([]) – Jorge Arturo Juarez Apr 28 '17 at 23:50
  • 2
    Leyendo, todo valor en javascript es true, menos null, undefined, number zero y boolean false, que dan false – Dev 200 Apr 29 '17 at 00:38
  • soy de php y he entendido esta explicación – Lukas Jun 19 '17 at 15:13
  • 2
    Con la explicación actual entonces `![]+![]` también debería evaluar a `2` pero evalúa a `0`, se ha obviado por completo el hecho de que se está usando el operador suma ([unary plus](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus_())) para convertir los arrays vacíos en números (ceros) dando lugar al resultado esperado `2` – Leon Plata Jun 19 '17 at 21:20
  • La explicación dada a @Dev.Joel acerca de `Boolean([])` es incorrecta, `Boolean` usado como función convierte en `false` a cualquier valor pasado como parámetro exceptuando `0, -0, null, false, NaN, undefined` ([documentación](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)). Este y el anterior comentario son las razones por las que estoy dando puntos negativos a esta respuesta – Leon Plata Jun 19 '17 at 21:23
  • 2
    Recién acabo de leer la respuesta de @Dev.Joel y de Rubén :P, es la respuesta correcta – Leon Plata Jun 19 '17 at 21:27
  • No se hace mención del primer `+` ni de su uso como operador unitario. – Rubén Jun 24 '17 at 15:46
  • 1
    @LeonPlata, creo que has mal interpretado el texto que has leído o has escrito lo contrario de lo que querías poner. Al utilizar `Boolean` cualquier cosa que le pases es evaluada a `true`, excptuando `0`, `-0`, `null`, `false`, `NaN`, `undefined`, `document.all` o un string vacío. La explicación de @Dev.Joel dice que `Boolean([])` da `true` y esto es cierto. – ElChiniNet Jul 13 '17 at 18:23
  • @ElChiniNet, erróneamente he escrito lo contrario de lo que quería expresar y por ello mi comentario es invalido, gracias por la aclaración – Leon Plata Jul 14 '17 at 19:16
  • @LeonPlata, eso imaginé. Un saludo ;) – ElChiniNet Jul 15 '17 at 00:35
56

JSFuck es un estilo de programación esotérico y educativo basado en las partes atómicas de JavaScript.

Algunas reglas para recordar:

  • Precediendo con ! convertimos a valor Booleano

    console.log(![])   // return false
    
  • Precediendo con + convertimos a número

    console.log(+[])  // return 0
    
  • Adición [] convertimos a Cadena

    console.log([]+[])  // return  ""
    

Por qué muestra el Mensaje son Iguales?

  • !+[] al anteponer el signo más se convierte en número , +[] = 0 la negación (!) de 0 es 1.

Luego al realizar la suma !+[] + !+[] es decir 1+1 = 2

Referencias

BetaM
  • 30,571
  • 7
  • 32
  • 50
Dev. Joel
  • 23,229
  • 3
  • 25
  • 44
  • Esta interesante tu respuesta :), por cierto no has leido sobre que la suma + se aplkca de defecha a izquierda jejeje no se donde lo lei, por eso pense que era algo asi []+ o da igual.? – Dev 200 Apr 29 '17 at 01:48
  • Está especificado en mi respuesta de la adición a la izquierda y derecha. **a la derecha espera de otro valor para efectuar la suma** por lo tanto `[]+` dará un error de sintaxis , y a la izquierda se convierte a número. – Dev. Joel Apr 29 '17 at 02:02
  • 3
    ¿Pero por qué `[]` vale `true`? Si creo una variable `a = 0` y luego hago `if (!a) console.log('Hola')` se imprime `Hola`, porque `0` es `false`... pero `+[]` devuelve `0` y es `true` ¿al mismo tiempo? Con razón se llama _JSFuck_ 0_o... – toledano May 03 '17 at 17:36
  • ¿No va un poco en contra de todo lo que defiende el código limpio (clean code)?. Se puede entender ese código pero siempre pienso que un código que no se lee fácil de un vistazo rápido no es buena idea :S. – mrroot5 May 04 '17 at 14:12
  • @erknrio no es una buena idea , solo plantee que existe tal estilo de programación , por qué se obtiene el resultado planteado. – Dev. Joel May 04 '17 at 17:36
  • 2
    @Dev.Joel, si si, entendí su postura. Simplemente era un comentario general. A veces lo que uno piensa no es siempre lo mejor y quería saber la opinión de otros :-). – mrroot5 May 04 '17 at 17:39
  • Hola @toledano, lee mi respuesta más abajo. Creo que aclara tus dudas. Un saludo – ElChiniNet Jul 13 '17 at 19:42
  • @Victor-Random, las sumas se aplican de izquierda a derecha a no ser que sitúes paréntesis para variar ese comportamiento. Es por eso que `2 + 3 + "0"` da `50` en vez de `230`. Me imagino que es eso lo que debes haber leído. Como bien dice Dev.Joel si sitúas el operador `+` a la derecha de un operando, debes situar un segundo operando o te dará error. – ElChiniNet Jul 14 '17 at 07:57
30

Los enlaces incluídos a continuación indicados con 1 corresponden a secciones de ECMAScript® 2016 Language Specification. La intención en primera instancia es proporcionar las referencias específicas al estándar referido, utilizando un enfoque de "jalar" (pull) más que el tradicional de "empujar" (push)

A continuación se listan las referencias correspondientes a cada una de las partes principales de

!+[]+!+[] == 2

Las cuales son

  • [] expresión para inicializar un objeto literal de tipo matriz, equivalente a new Array().
  • +[] matriz con operador unitario +.
  • !+[] matriz con operador unitario + y operador unitario no lógico.
  • !+[] + !+[] adición de dos !+[] .
  • !+[] + !+[] == 2 comparación abstracta de la adición de dos !+[].

Empecemos

Entonces

  • +[] es una operación unitaria que devuelve cero (0)

  • !0 es una operación unitaria que devuelve verdadero (true)

  • true + true es una operación que, primero convierte cada operando a 1 y luego devuelve su adición la cual es 2

  • 2 == 2 es una comparación de igualdad abstracta que devuelve verdadero (true)

A continuación un pequeño experimento para que el lector compruebe si stack-snippet y su navegador operan conforme a la especificación referida.

document.write(+[]);
document.write('<br/>'); //Separador
document.write(!+[]);
document.write('<br/>'); //Separador
document.write(!+[]+!+[]);
Rubén
  • 10,857
  • 6
  • 35
  • 79
  • 4
    FYI, `[]` es un array vacio. Nada que ver con el tipo `string`. Creo que podrias explicar la precedencia de operadores como para cerrar bien la respuesta. – rnrneverdies May 03 '17 at 18:17
  • @EmanuelVe en ciertos casos los objetos se convierten a [valores primitivos](https://www.ecma-international.org/ecma-262/7.0/index.html#sec-primitive-value) ¿sabes cuál es el valor primitivo de un `[]`? – Rubén May 03 '17 at 18:18
  • `[]` es un syntax-sugar para `new Array()` que si haces `typeof []` veras es q es un object. el tipo primitivo – rnrneverdies May 03 '17 at 18:20
  • Gracias pero según entiendo eso no responde mi pregunta. Un array es un objeto exótico el cual debe tener una regla para ser convertido a un tipo primitivo (Undefined, Null, Boolean, Number, Symbol, or String). – Rubén May 03 '17 at 18:21
  • 2
    es un object. https://www.ecma-international.org/ecma-262/7.0/index.html#sec-object-type – rnrneverdies May 03 '17 at 18:23
  • "An object is a collection of properties and has a single prototype object. The prototype may be the null value." pero me parece que no es el caso de `[]`. Bueno, no estoy buscando el prototipo, sino el valor primitivo. – Rubén May 03 '17 at 18:26
  • @Rubén , [ ] esto, es una version corta para crear un array, en vez de new Array(). Un array es un object, si revisas mi respuesta veras su conversion a primitivo. Pasa por number y luego to primitive y luego segun el contenido pasadonpor el argumento te devuelve string o un entero.nese array vacío se vuelve string – Dev 200 May 08 '17 at 02:23
  • 2
    @Victor-Random: He agregado la referencia sobre []. – Rubén Jun 22 '17 at 15:20
  • 2
    @Rubén lo revisare.... :) ya entiendo que entonces el array no entraria en esa validacion del toprimitive, sino usando el join – Dev 200 Jun 22 '17 at 17:54
18

Para complementar a las respuestas, consideremos la siguiente serie de elementos adicionales documentados en:

ECMA-262 7ᵗʰ Edition / June 2016 ECMAScript® 2016 Language Specification: https://www.ecma-international.org/ecma-262/7.0/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures.

JavaScript in Plain Language - Tony de Araujo: https://www.amazon.com/dp/B00NQERIEI/ref=rdr_kindle_ext_tmb

JavaScript Objects Functions and Arrays Explained - Tony de Araujo: https://www.amazon.com/JavaScript-Objects-Functions-Arrays-Explained-ebook/dp/B00GDEPBZQ

Nicholas C. Zakas Professional JavaScript for Web Developers. https://www.amazon.es/Professional-JavaScript-Developers-Wrox-Guides/dp/1118026691


1) JavaScript datos primitivos y complejos:

JavaScript basa sus tipos de datos de ECMAScript, divididos en primitivos y complejos o referenciados:

Datos primitivos:

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-primitive-value

  • String: unicode, ascii y caracteres alfanuméricos.

  • Number: integer, float.

  • Boolean: true y false.

  • Null: null.

  • Undefined: undefined.

Datos complejos:

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-terms-and-definitions-object

  • Object: Array, Unordered list (Key-value, Map), funciones, object.

2) Valores Booleanos de los datos:

https://www.ecma-international.org/ecma-262/7.0/index.html#table-10

Datos primitivos:

var string="Hola soy una string"; devolverá true.

var integer=123; devolverá true.

var trueVariable = true; devolverá true.

var float = 4.98; devolverá true.

Datos complejos:

var arraySinElementos = []; devolverá true.

var arrayConElementos = [0,1,2]; devolverá true.

Excepciones:

Basándonos en la tabla anteriormente citada podremos encontrar los valores Booleanos de los siguientes tipos de datos, al aplicar ToBoolean():

  • Boolean false: devolverá false.

  • Number zero: devolverá false.

  • Undefined: devolverá false.

  • Null: devolverá false.


2) Valor de los operadores lógicos:

Definimos los principales valores lógicos:

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-terms-and-definitions-boolean-value

true == 1;

false == 0;

Operador lógico not (!): aplica la inversa del valor recibido.

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-logical-not-operator

Su funcionamiento seria el siguiente :

resultado = !expresion;

!expresion == !(ToBoolean(expresion))

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-logical-not-operator-runtime-semantics-evaluation

La expresión sera evaluada con un ToBoolean(), que seguirá la siguiente tabla:

https://www.ecma-international.org/ecma-262/7.0/index.html#table-10

Aplicando la inversa tendremos que:

  • En el caso de ser la expresión true este devolverá false.

  • En el caso de ser false devolverá true.

  • Si es distinto de cero (!==0), el resultado es cero.

  • Si es 0 el resultado sera 1.

  • Las cadenas se convierten en números, si es posible.

  • Las cadenas vacías se convierten en true.

  • El valor lógico nulo o not realiza por determinado la acción de false, ya que no se sabe si la condición es verdadera.


3) Operador más unario (+expresion) y operador más de adición (expresión + expresión):

El operador más (+) lo podemos encontrar como operador unario y de adición:

  • Si se considera como adición:

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-addition-operator-plus

  • Si lo consideramos unario tendría la siguiente propiedad y funcionamiento:

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-unary-plus-operator-runtime-semantics-evaluation

expresión unaria: +expresión unaria
expresión unaria: ToNumber(expresión unaria)

Esta función aplicara ToNumber() a la expresión:

https://www.ecma-international.org/ecma-262/7.0/index.html#table-11

Si se usa sobre un valor no numérico este aplicara la funcion ToNumber(), en nuestra caso tenemos un array[] que entraria en los datos referenciados o Object.

Es decir que ToNumber() sera aplicada a un object, por lo tanto se establece el uso de ToPrimitive().

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-tonumber

4) Función ToPrimitive():

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-toprimitive

Esta función se encuentra en el apartado de conversión de tipos:

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-type-conversion

Y se usara con el fin de convertir el objeto Object a un valor primitivo para que pueda ser luego evaluado con:

`ToString()` y `valueOf()`.
`valueOf()` y `ToString()`.

Respectivamente.

En este caso al ser un Objeto Object: se aplica:

Type(input)

Siguiendo el siguiente orden de evaluación:

Tabla resumida:

Si PreferredType | no fue pasado | hint = "default".
Si PreferredType | fue pasado    | hint = "string".
Si PreferredType | fue pasado    | hint = "number".
Luego retornar   | OrdinaryToPrimitive(input, hint).

Se procede a la conversión:

Afirmacion: Type(O) es Object. // Se cumple pues es array[] es un Object.

Afirmacion: Type(hint) es String y su valor es "string" ó "number".

En este caso es un string vacio.

https://www.ecma-international.org/ecma-262/7.0/index.html#table-12

Cumpliéndose:

Si hint = string.

Aplicar "toString()" y luego "valueOf()"

  • ToString():

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-tostring

ToPrimitive(argument, hint String). // Establecido anteriormente

  • valueOf():

https://www.ecma-international.org/ecma-262/7.0/index.html#sec-object.prototype.valueof

Aplicando tenemos que:

ToString( array[] ) == ( "" ) // Paréntesis para mejor entendimiento

( "" ).valueOf() == ( "" )
  • Por lo tanto:

    +[] == ToNumber(array[]).

    ToNumber(array[]) == ( "" ).valueOf().

    ( "" ).valueOf == ( "" ).

Ahora usemos nuestro operador logico not (!) para obtener:

( "" ) == false.

5) Seguido tocaria sustituir:

if (!+[]+!+[]==2) {
document.write('Somos iguales');
}
else {
document.write('Somos distintos');
}

Resolviendo:

if (!("")+!("")==2) {
document.write('Somos iguales');
}
else {
document.write('Somos distintos');
}

Finalizando:

if (true+true==2) {
document.write('Somos iguales');
}
else {
document.write('Somos distintos');
}

HTML: Somos iguales
Dev 200
  • 5,246
  • 5
  • 39
  • 80
  • Que bien , me podrías pasar documentación acerca de los elementos considerados true y todo ese ámbito? porfavor. – Eduardo Sebastian Apr 29 '17 at 01:00
  • @EduardoSebastian aqui https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures – Dev 200 Apr 29 '17 at 01:41
  • @EduardoSebastian Deberías de poner esta como la respuesta correcta, ya que considero que es mucho mejor respuesta que la mía. – Jorge Arturo Juarez Apr 29 '17 at 17:24
  • 1
    Victor: el operador `==` en ciertos casos realiza conversión de los operandos. Para efectos de "comprobación" como las que mencionas en tu respuesta considero que se debería usar `===` o en su defecto mencionar cómo se debería considerar la conversión realizada por `==`. – Rubén May 10 '17 at 09:10
4

Veo que esta pregunta ha generado un grupo de respuestas muy valiosas y es este tipo de preguntas y respuestas las que más nutren a nuestra comunidad. Intentaré aportar mi granito de arena a este maravilloso hilo.

Voy a aclarar ciertos aspectos que no están aclarados. Quien lea las respuestas notará que hay algunas que se contradicen. He visto un comentario de @toledano en la respuesta de @Dev.Joel y otra de @Dev.Joel en la respuesta de @JorgeArturoJuarez básicamente preguntándose por qué [] == false es true y por otro lado ![] es false y me parece que es debido a varias de las respuestas que son algo ambiguas, como la respuesta marcada como correcta que mostraba que [] era igual a false.

Lo primero que hay que saber es que toda instancia de un objeto está definida de por sí y por lo tanto es verdadera:

var a = [];
var b = new String("");
var c = new Boolean(false);

if (a && b && c) {

  console.log("todos son verdaderos");
  console.log(!a, !b, !c);
  
}

No se deben confundir las instancias de un objeto con los valores primitivos:

var a = "";
var b = false;
var c;

if (a || b || c) {

  console.log("¿hay alguna verdadera?");
  
} else {

  console.log("ninguna es verdaera");
  console.log(!a, !b, !c);
  
}

Sin embargo al usar los operadores de comparación (exceptuando === y !==) y los operandos tener un tipado diferente, JavaScript intenta convertir ambos operandos a un tipo apropiado para realizar la comparación, y generalmente esta conversión es numérica. Es por eso que ocurre lo siguiente sin importar que sean instancias de objetos y sean verdaderas:

var a = [];
var b = new String("");
var c = new Boolean(false);

//---Estas comparaciones son verdaderas
console.log(a == false);
console.log(b == false);
console.log(c == false);

//---Esto se debe a que
console.log(+a, +b, +c, +false);

Teniendo esto en cuenta se podrá entender por qué [] == false da true y ![] da false.

En cuanto a la pregunta principal, ya todo está más que explicado en las anteriores respuestas, aquí lo resumo:

  1. Al aplicar el operador unario + a una instancia de Array vacía el resultado es 0 (ya que internamente le aplica la operación abstracta ToNumber).
  2. Al aplicar el operador lógico ! a 0 el resultado es true (ya que este operador devuelve false si la expresión puede ser evaluada a true y en caso contrario devuelve true)
  3. Al sumar dos valores booleanos con valor true el resultado será 2 (ya que al no tratarse de cadenas de caracteres, en vez de concatenar, el operador intentará convertir los operandos a número antes de sumarlos)
ElChiniNet
  • 3,215
  • 9
  • 25