¿Cómo funciona el condicional if (!+[]+!+[] == 2) en JavaScript?

49

14

¿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');
}

Eduardo Sebastian

Posted 2017-04-28T22:59:26.877

Reputation: 2 074

Aunque ya tienes varias y excelentes respuestas olvidaron mencionar que deberías evitar el uso de document.write() en producción.Gerardo 2017-05-07T03:05:49.797

Porque debo evitar su uso?Eduardo Sebastian 2017-05-08T12:29:25.843

Answers

45

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>

Jorge Arturo Juarez

Posted 2017-04-28T22:59:26.877

Reputation: 2 310

1Muchísimas gracias , me podrias decir donde aprender javascript practicamente y de buena forma? porfavorEduardo Sebastian 2017-04-28T23:20:26.193

3Herramientas para desarrolladores -&gt; Console y trastea con todos los sitios...Jorge Arturo Juarez 2017-04-28T23:25:04.677

console.log([]) devuelve false, console.log(! []) debería dar como resultado true , pero no es así , ¿por qué?Dev. Joel 2017-04-28T23:33:57.407

@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 2017-04-28T23:50:45.140

1Leyendo, todo valor en javascript es true, menos null, undefined, number zero y boolean false, que dan falsevaavDev 2017-04-29T00:38:57.583

soy de php y he entendido esta explicaciónLukas 2017-06-19T15:13:01.463

1

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)) para convertir los arrays vacíos en números (ceros) dando lugar al resultado esperado 2

Leon Plata 2017-06-19T21:20:05.657

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). Este y el anterior comentario son las razones por las que estoy dando puntos negativos a esta respuesta

Leon Plata 2017-06-19T21:23:52.073

1Recién acabo de leer la respuesta de @Dev.Joel y de Rubén :P, es la respuesta correctaLeon Plata 2017-06-19T21:27:20.697

No se hace mención del primer + ni de su uso como operador unitario.Rubén 2017-06-24T15:46:14.450

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 2017-07-13T18:23:11.383

@ElChiniNet, erróneamente he escrito lo contrario de lo que quería expresar y por ello mi comentario es invalido, gracias por la aclaraciónLeon Plata 2017-07-14T19:16:33.890

@LeonPlata, eso imaginé. Un saludo ;)ElChiniNet 2017-07-15T00:35:04.957

44

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

Dev. Joel

Posted 2017-04-28T22:59:26.877

Reputation: 14 962

2excelente explicación +1 ;))fwBasic 2017-04-29T00:36:08.240

Gracias por aclarar dudas :)Eduardo Sebastian 2017-04-29T01:00:17.820

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.?vaavDev 2017-04-29T01:48:32.403

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 2017-04-29T02:02:58.810

2¿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 2017-05-03T17:36:45.973

2brother, ver el funcionamiento de JSFuck me ha dejado lelo, buen aporte :D ¡muchas gracias!fredyfx 2017-05-03T18:00:28.290

2Esta debería ser la respuesta! Excelente explicación!sioesi 2017-05-04T11:03:55.773

¿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.erknrio 2017-05-04T14:12:42.763

@erknrio no es una buena idea , solo plantee que existe tal estilo de programación , por qué se obtiene el resultado planteado.Dev. Joel 2017-05-04T17:36:43.710

1@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 :-).erknrio 2017-05-04T17:39:33.653

Hola @toledano, lee mi respuesta más abajo. Creo que aclara tus dudas. Un saludoElChiniNet 2017-07-13T19:42:14.197

@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 2017-07-14T07:57:47.830

22

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

Posted 2017-04-28T22:59:26.877

Reputation: 4 965

3FYI, [] 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.Emanuel Ve 2017-05-03T18:17:05.877

@EmanuelVe en ciertos casos los objetos se convierten a valores primitivos ¿sabes cuál es el valor primitivo de un []?

Rubén 2017-05-03T18:18:43.823

[] es un syntax-sugar para new Array() que si haces typeof [] veras es q es un object. el tipo primitivoEmanuel Ve 2017-05-03T18:20:13.983

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 2017-05-03T18:21:48.523

"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 2017-05-03T18:26:31.927

@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 stringvaavDev 2017-05-08T02:23:50.330

1@Victor-Random: He agregado la referencia sobre [].Rubén 2017-06-22T15:20:37.123

^^^^ @EmanuelVeRubén 2017-06-22T15:21:18.703

1@Rubén lo revisare.... :) ya entiendo que entonces el array no entraria en esa validacion del toprimitive, sino usando el joinvaavDev 2017-06-22T17:54:19.563

15

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

vaavDev

Posted 2017-04-28T22:59:26.877

Reputation: 2 314

Que bien , me podrías pasar documentación acerca de los elementos considerados true y todo ese ámbito? porfavor.Eduardo Sebastian 2017-04-29T01:00:52.973

@EduardoSebastian dejame buscarte, que solo se específica todo como lo dije, es una sola linea jejejevaavDev 2017-04-29T01:25:23.627

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

vaavDev 2017-04-29T01:41:33.427

Gracias !! yay!Eduardo Sebastian 2017-04-29T01:51:26.783

@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 2017-04-29T17:24:10.923

@Victor-Random si agregaras el link de la documentación en la respuesta seria muy útil para futuras referencias.Jorge Arturo Juarez 2017-04-29T17:25:12.950

@JorgeArturoJuarez gracias por lo que dices que mi respuesta esta mas completa, ahorita agrego la documentacionvaavDev 2017-04-29T22:45:40.950

Agregare la documentacionvaavDev 2017-05-04T12:07:29.380

Excelente respuesta.dwarandae 2017-05-04T13:38:40.123

@dwarandae muchas gracias (y)vaavDev 2017-05-05T02:45:03.207

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 2017-05-10T09:10:26.567

0

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 preguntándose por qué [] vale true y me parece que es debido a 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

Posted 2017-04-28T22:59:26.877

Reputation: 1 345