5

¿Por qué sucede esto en javascript?

[] == false
// true

!![] == false
// false

cuando hago la comparación abstracta de un array vacío con un booleano, al forzar los valores para compararlos, resuelve que un dicho array vacío es "igual" a un booleano false.

Ahora, cuando le paso el operador !(not) al array vacío y fuerza su valor a booleano lo resuelve como false también, y al aplicar el !!(not not) por "lógica" es true. De hecho la siguiente expresión da true:

[] == ![]
// true 

Al menos que haya algún escollo en la conversión implícita que maneja JavaScript, esta última expresión no debería tener sentido alguno. Sobretodo con este tipo de valor como un array vacío, cualquiera puede cometer el error de creer que un array por mas vacío que esté sea verdadero y que al pasarlo implicitamente a booleano con !(not) va a cambiar su valor, pero no, siempre va a dar falso.

Shaz
  • 28,742
  • 18
  • 37
  • 61
  • La respuesta está en SO en inglés, tal vez alguien pueda hacer una traducción: https://stackoverflow.com/questions/4226101/conflicting-boolean-values-of-an-empty-javascript-array – Shaz Jan 31 '19 at 19:09
  • esta respuesta es todavia mas detallada https://stackoverflow.com/a/10556035/73749 – gbianchi Jan 31 '19 at 19:14
  • @gbianchi es verdad, pero tal vez muy técnica para el promedio de los visitantes de SO. Podrías tal vez hacer una traducción de lo mejor de ambas respuestas. – Shaz Jan 31 '19 at 19:18
  • temo comenter algun error porque js no es mi fuerte (y menos cuando hace estas cosas extravagantes).. pero lo agendo y si mañana nadie lo hizo, lo intento... @Shaz – gbianchi Jan 31 '19 at 19:19
  • esta pregunta es un duplicado, tambien puedes ver [Cómo funciona el condicional if en JavaScript?](https://es.stackoverflow.com/questions/66292/c%C3%B3mo-funciona-el-condicional-if-2-en-javascript) – JackNavaRow Jan 31 '19 at 19:46
  • 2
    leer, analizar e imprimir: https://dorey.github.io/JavaScript-Equality-Table/ – fredyfx Jan 31 '19 at 19:55
  • esta pregunta no es un duplicado de https://es.stackoverflow.com/questions/143847/javascript-por-qu%c3%a9-y-son-falso, alli compara dos array de diferentes instancias, lo cual tiene sentido que de falso. Aqui lo que sucede es que la conversión implicita de un [] a un boolean en una comparación lógica es diferente a la conversión implicita cuando utilizas el operando NOT NOT. Es muy fina la diferencia. – Darío Gómez Fisicaro Mar 28 '19 at 18:03

2 Answers2

5

Tu pregunta me parece interesante. Por lo pronto trato de responder basándome en lo que ocurriría en una comparación de igualdad...

Revisando el algoritmo que se desarrolla en la Comparación de igualdad abstracta explicada en el apartado 7.2.14 Abstract Equality Comparison de ECMA Script podemos revisar caso por caso.

Imprimiremos primero el tipo de cada uno para saber qué parte del algoritmo funcionará... si es que funciona...

  • [] == false da como resultado true

Se debería cumplir la etapa 7 del algoritmo, puesto que y es booleano:

  1. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).

Lo que debería ocurrir según ECMA Script es que y sea convertido a número y luego hacer una comparación del tipo: x == ! y.

Dado que y es booleano igual a false, dicha conversión retorna +0, como dice la especificación sobre ToNumber:

If argument is true, return 1. If argument is false, return +0.

Hagamos una prueba aplicando lo que dice la etapa 7 y otra prueba con lo que ocurriría en realidad.

console.log(typeof []);
console.log(typeof false);
console.log([] == ! (+0));   /*Siguiendo lo que indica ECMA Script (paso 7) */
console.log([] == false);    /*Lo que ocurre realmente... Esto pinta muy feo*/

Bueno esto empieza a ser preocupante. Según la especificación debería dar false, pero da true.

Vamos a seguir con los otros casos, a ver si llegamos a alguna conclusión.


  • !![] == false da como resultado false

Se cumple el caso 1: una comparación estricta x === y, dado que ambos valores son del mismo tipo:

  1. If Type(x) is the same as Type(y), then Return the result of performing Strict Equality Comparison x === y.

console.log(typeof !![]);
console.log(typeof false);
console.log(!![] === false);

¡Comparación estricta!, un verdadero alivio leer eso. Seguimos...


  • [] == ![] da como resultado true

Aquí no parece cumplirse nada de lo que indica ECMA Script, porque x es del tipo object y y es boolean. Lo que más se parecería sería la etapa 9.

  1. If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.

Pero no, exactamente. x es del tipo object, pero y es booleano. Podemos decir que estamos ante un caso que escapa al alcance mismo de la especificación ECMA Script (ver Conclusión).

console.log(typeof []);
console.log(typeof ![]);
console.log(([]) == ![]);

CONCLUSIÓN

Estaba preocupado por no poderte explicar con fundamento lo que pasa en el primer y tercer caso, pero investigando encontré una tabla que me quitó la preocupación. Si empezamos a tomar casos inspirados en esta tabla, se pueden escribir bibliotecas enteras intentando comprender lo que ocurre en cada caso.

Igualdad en Javascript Tabla de igualdad en Javascript, publicada por Dorey en repositorio de Github

En conclusión, cuando comparamos ciertos valores null, false, true, 1, 0, NaN, undefined ... con valores de otro tipo mediante igualdad abstracta (==) los resultados pueden ser inesperados. Hay una sola solución: utilizar la igualdad estricta, más aún, utilizar la igualdad estricta siempre, a no ser en casos muy concretos en los que por un motivo justificado no nos interese que se cumpla la igualdad de tipos.


Enlaces

Para más documentación se pueden consultar los siguientes enlaces ... y muchísimos más que se pueden encontrar en la red:

A. Cedano
  • 86,578
  • 19
  • 122
  • 221
  • `[] == ''` [esto sera true](https://es.stackoverflow.com/a/67342/28035) – JackNavaRow Jan 31 '19 at 20:04
  • 1
    @JackNavaRow revisa la edición y la tabla al final. Hay casos para escribir bibliotecas enteras o resolverlo muy simplemente: *usar comparación estricta siempre*, a no ser casos muy específicos en los que no importe la igualdad de tipos. – A. Cedano Jan 31 '19 at 23:16
1

Revisa de este modo:

Cuando haces un typeof() de [], obtienes que es de tipo object

Cuando haces un typeof() de !![], obtienes que es de tipo boolean

Ahora, si revisamos el uso de los operadores de comparación, sabemos que

== solo verifica que sean del mismo valor

=== además verifica que sean del mismo tipo

Entonces para ambos escenarios es mejor compararlos así:

console.log([] === false)

Da como resultado false porque un object no es lo mismo que un valor booleano.

Y para el caso de

console.log(!![] === false)

También dará false.

Si por el contrario quitar el operador NOT NOT con el símbolo !! y solo lo dejas así

console.log(![] === false)

Dará true

En el caso específico de este ejemplo

console.log(!![] === false)

Da como resultado false porque al usar el operador !! se vuelve a true el valor, pero si comparamos true === false , eso no es cierto y por ende retorna false

Incluso si el ejemplo anterior lo escribes así

console.log(!![] == false)

Te va a retornar false por que quedaría de manera interna así

true == false y eso retorna false

Shaz
  • 28,742
  • 18
  • 37
  • 61
  • Si yo se explicitamente que estoy comparando un object con un booleano de forma estricta, asumo que siempre me va a dar false por el simple hecho de que son diferentes tipos. es decir, ([] === false) y ([] === true) me devuelven false por que estoy comparando diferentes tipos a priori. – Darío Gómez Fisicaro Feb 27 '19 at 14:09