¿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');
}
¿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');
}
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>
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
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
[]
inicializa un literal de tipo matriz con ningún elemento. La propiedad longitud (length
) de la matriz es cero. y el valor primitivo es un cadena vacía. Para más detalles véase la respuesta a ¿Cuál es el valor primitivo de [] con base en ECMAScript 2016 (versión 7)?.En Unary + operator1 se indica que +
convierte su operando a tipo número. En el caso de + []
, el operando es una cadena vacía (probando [].join() === ''
en la consola de Chrome devuelve true
) y al convertir este a número, se convierte en 0
.
En logical operator not1 se indica que !
convierte su operando a tipo booleano, y que si el valor booleano del operando es falso, devuelve verdadero y si el valor booleano del operando es verdadero, devuelve falso.
En ToBoolean1 se indica que cuando el valor es cero (-0
ó +0
), se devuelve falso (false
).
En The Addition Operator ( + )1 se indica que cuando los operandos son de tipo distinto a cadena de texto, se convierten a número.
En ToNumber1 se indica que cuando el valor es verdadero, se devuelve uno (1
) y cuando el valor es falso, se devuelve cero (0
).
En Applying the Additive Operators to Numbers1 se indica que cuando +
se aplica a dos operandos numéricos, realiza la adición de estos.
En Abstract equality comparison1 se indica que cuando los dos elementos a la izquierda y derecha son iguales, devuelve verdadero (true
)y en caso contrario devuelve falso (false
).
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(!+[]+!+[]);
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
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))
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:
https://www.ecma-international.org/ecma-262/7.0/index.html#sec-addition-operator-plus
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()
"
https://www.ecma-international.org/ecma-262/7.0/index.html#sec-tostring
ToPrimitive(argument, hint String)
. // Establecido anteriormente
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
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:
+
a una instancia de Array
vacía el resultado es 0
(ya que internamente le aplica la operación abstracta ToNumber
).!
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
)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)