En JavaScript, The Good Parts , Douglas Crockford escribió:
JavaScript tiene dos conjuntos de operadores de igualdad:
===
y!==
, y sus gemelos malvados==
y!=
. Los buenos funcionan de la manera que cabría esperar. Si los dos operandos son del mismo tipo y tienen el mismo valor,===
producetrue
y!==
producefalse
. Los gemelos malvados hacen lo correcto cuando los operandos son del mismo tipo, pero si son de diferentes tipos, intentan forzar los valores. Las reglas por las cuales hacen eso son complicadas e inmemorables. Estos son algunos de los casos interesantes:'' == '0' // false 0 == '' // true 0 == '0' // true false == 'false' // false false == '0' // true false == undefined // false false == null // false null == undefined // true ' \t\r\n ' == 0 // true
La falta de transitividad es alarmante. Mi consejo es que nunca uses a los gemelos malvados. En cambio, siempre use
===
y!==
. Todas las comparaciones que acabamos de mostrar producenfalse
con el===
operador.
Dada esta observación inequívoca, ¿hay algún momento en que el uso ==
podría ser realmente apropiado?
fuente
==
por defecto, se===
destaca y me deja saber que algo importante está sucediendo.Respuestas:
Voy a argumentar por
==
Douglas Crockford, que citó, es conocido por sus muchas y, a menudo, muy útiles opiniones. Mientras estoy con Crockford en este caso particular, vale la pena mencionar que no es la única opinión. Hay otros, como el creador de idiomas Brendan Eich, que no ven el gran problema
==
. El argumento es un poco como el siguiente:JavaScript es un lenguaje de tipo conductual *. Las cosas se tratan en función de lo que pueden hacer y no de su tipo real. Es por eso que puede llamar al
.map
método de una matriz en una NodeList o en un conjunto de selección jQuery. También es la razón por la que puede hacer3 - "5"
y recuperar algo significativo, porque "5" puede actuar como un número.Cuando realiza una
==
igualdad, está comparando el contenido de una variable en lugar de su tipo . Aquí hay algunos casos en los que esto es útil:.value
de un elemento de entrada en el DOM? ¡No hay problema! No tiene que comenzar a lanzarlo o preocuparse por su tipo: puede==
hacerlo de inmediato en números y recuperar algo significativo.== null
hacerlo ya que el comportamientonull
representa que no hay nada allí y que indefinido tampoco tiene nada allí.==
argumento, tratará los casos en que el usuario no haya ingresado nada o solo un espacio en blanco para usted, que probablemente sea lo que necesita.Veamos los ejemplos de Crockford y explíquelos conductualmente:
Básicamente,
==
está diseñado para funcionar en función de cómo se comportan las primitivas en JavaScript, no en función de lo que son . Si bien no estoy personalmente de acuerdo con este punto de vista, definitivamente vale la pena hacerlo, especialmente si tomas este paradigma de tratar tipos basados en el comportamiento en todo el lenguaje.* algunos podrían preferir el nombre de tipificación estructural, que es más común pero hay una diferencia, no estoy realmente interesado en discutir la diferencia aquí.
fuente
==
no es que ninguna de sus comparaciones sea útil , es que las reglas son imposibles de recordar, por lo que es casi seguro que cometerás errores. Por ejemplo: "¿Necesita verificar si recibió una entrada significativa de un usuario?", Pero '0' es una entrada significativa y'0'==false
es verdadera. Si lo hubiera usado===
, habría tenido que pensar explícitamente en eso y no habría cometido el error.3 - "5"
ejemplo plantea un buen punto en sí mismo: incluso si lo usa exclusivamente===
para comparación, esa es la forma en que las variables funcionan en Javascript. No hay forma de escapar por completo.==
en mi propio código, me parece un antipatrón y estoy completamente de acuerdo en que el algoritmo de igualdad abstracta es difícil de recordar. Lo que estoy haciendo aquí es argumentar a favor de la gente.Resulta que jQuery usa la construcción
ampliamente, como una abreviatura para el código equivalente:
Esto es una consecuencia de la Especificación del lenguaje ECMAScript § 11.9.3, El algoritmo de comparación de igualdad abstracta , que establece, entre otras cosas, que
y
Esta técnica en particular es lo suficientemente común como para que JSHint tenga una bandera específicamente diseñada para ello.
fuente
== null or undefined
es el único lugar donde no uso===
o!==
==
y solo se usa===
en los casos en que es necesario: github.com/madrobby/zepto/blob/master/src/event.js__proto__
y, a su vez, lo obliga casi sin ayuda a la especificación del lenguaje para evitar romper sitios web móviles.Comprobar valores para
null
oundefined
es una cosa, como se ha explicado abundantemente.Hay otra cosa, donde
==
brilla:Puede definir la comparación de la
>=
misma manera (la gente suele comenzar,>
pero esto me parece más elegante):a > b
<=>a >= b && !(b >= a)
a == b
<=>a >= b && b >= a
a < b
ya <= b
se dejan como ejercicio para el lector.Como sabemos, en JavaScript
"3" >= 3
y"3" <= 3
, de donde obtienes3 == "3"
. Puede señalar que es una idea horrible permitir la implementación de la comparación entre cadenas y números analizando la cadena. Pero dado que esta es la forma en que funciona,==
es absolutamente la forma correcta de implementar ese operador de relación.Entonces, lo realmente bueno
==
es que es consistente con todas las demás relaciones. Para decirlo de otra manera, si escribes esto:Ya lo estás usando implícitamente
==
.Ahora a la pregunta bastante relacionada de: ¿Fue una mala elección implementar la comparación de números y cadenas de la forma en que se implementa? Visto de forma aislada, parece una cosa bastante estúpida. Pero en el contexto de otras partes de JavaScript y DOM, es relativamente pragmático, considerando que:
Object
para tener un mapa disperso de ints a valores)input[type=number]
)Por una gran cantidad de razones, tenía sentido hacer que las cadenas se comporten como números cuando sea necesario. Y suponiendo que la comparación de cadenas y la concatenación de cadenas tengan diferentes operadores (por ejemplo,
::
para concatenar y un método para comparar (donde puede usar todo tipo de parámetros con respecto a mayúsculas y minúsculas), esto sería en realidad menos problemático. Pero esta sobrecarga del operador es, de hecho, de donde proviene el "Java" en "JavaScript";)fuente
>=
no es realmente transitivo. Es bastante posible en JS que nia > b
nia < b
nib == a
(por ejemplo:)NaN
.+
que no es realmente conmutativo, porqueNaN + 5 == NaN + 5
no se cumple. El punto es que>=
funciona con valores numéricos para los cuales==
funciona de manera consistente. No debe sorprender que "no es un número" es, por su propia naturaleza no-ish número;)==
es consistente con el mal comportamiento de>=
? Genial, ahora desearía que hubiera un>==
...>=
es bastante consistente con el resto del lenguaje / API estándar. En su totalidad, JavaScript logra ser más que la suma de sus partes extravagantes. Si desea un>==
, ¿también querría un estricto+
? En la práctica, muchas de estas decisiones hacen que muchas cosas sean mucho más fáciles. Entonces no me apresuraría a juzgarlos como pobres.>=
es significativo,==
es igualmente significativo, eso es todo. Nadie dice que deberías comparar[[]]
confalse
. En lenguajes como C, el resultado de este nivel de tonterías es un comportamiento indefinido. Solo trátalo de la misma manera: no lo hagas. Y estarás bien. Tampoco necesitarás recordar ninguna regla mágica. Y luego, en realidad, es bastante sencillo.Como matemático profesional, veo en el operador de igualdad de Javscript
==
(también llamado "comparación abstracta", "igualdad suelta" ) un intento de construir una relación de equivalencia entre entidades, que incluye ser reflexivo , simétrico y transitivo . Desafortunadamente, dos de estas tres propiedades fundamentales fallan:==
no es reflexivo :A == A
puede ser falso, por ejemplo==
no es transitivo :A == B
yB == C
juntos no implicanA == C
, por ejemploSolo la propiedad simétrica sobrevive:
A == B
implicaB == A
, qué violación es probablemente impensable en cualquier caso y conduciría a una rebelión grave;)¿Por qué importan las relaciones de equivalencia?
Porque ese es el tipo de relación más importante y frecuente, respaldado por numerosos ejemplos y aplicaciones. La aplicación más importante es la descomposición de entidades en clases de equivalencia , que es en sí misma una forma muy conveniente e intuitiva de entender las relaciones. Y no ser equivalencia conduce a la falta de clases de equivalencia, lo que a su vez conduce a la falta de intuición y complejidad innecesaria que es bien conocida.
¿Por qué es una idea tan terrible escribir
==
para una relación de no equivalencia?Porque rompe nuestra familiaridad e intuición, ya que literalmente cualquier relación interesante de similitud, igualdad, congruencia, isomorfismo, identidad, etc. es una equivalencia.
Conversión de tipo
En lugar de confiar en una equivalencia intuitiva, JavaScript introduce la conversión de tipos:
Pero, ¿cómo se define la conversión de tipo? ¿A través de un conjunto de reglas complicadas con numerosas excepciones?
Intento de construir una relación de equivalencia
Booleanos. Claramente
true
yfalse
no son lo mismo y deben estar en diferentes clases.Números. Afortunadamente, la igualdad de números ya está bien definida, en la que dos números diferentes nunca están en la misma clase de equivalencia. En matemáticas, eso es. En JavaScript, la noción de número está algo deformada por la presencia de los más exóticos
-0
,Infinity
y-Infinity
. Nuestra intuición matemática dicta que0
y-0
debe estar en la misma clase (de hecho lo-0 === 0
estrue
), mientras que cada uno de los infinitos es una clase separada.Números y booleanos. Dadas las clases de números, ¿dónde ponemos los booleanos?
false
se vuelve similar a0
, mientras que setrue
vuelve similar a,1
pero no a otro número:¿Hay alguna lógica aquí para
true
armar1
? Es cierto que1
se distingue, pero también lo es-1
. Yo personalmente no veo ninguna razón para convertirtrue
a1
.Y se pone aún peor:
Por
true
lo tanto, de hecho se convierte1
entre todos los números! ¿Es lógico? ¿Es intuitivo? La respuesta se deja como ejercicio;)Pero qué hay de esto:
El único booleano
x
conx && true
sertrue
esx = true
. Lo que demuestra que tanto1
y2
(y cualquier otro número que0
) se convierten atrue
! Lo que muestra es que nuestra conversión falla otra propiedad importante: ser biyección . Lo que significa que dos entidades diferentes pueden convertir a la misma. Lo cual, por sí mismo, no tiene que ser un gran problema. El gran problema surge cuando usamos esta conversión para describir una relación de "similitud" o "igualdad suelta" de lo que queramos llamarlo. Pero una cosa está clara: no será una relación de equivalencia y no se describirá intuitivamente a través de clases de equivalencia.¿Pero podemos hacerlo mejor?
Al menos matemáticamente, ¡definitivamente sí! Se podría construir una relación de equivalencia simple entre booleanos y números con solo
false
y0
estar en la misma clase. Entoncesfalse == 0
sería la única igualdad suelta no trivial.¿Qué pasa con las cuerdas?
Podemos recortar cadenas de espacios en blanco al principio y al final para convertirlas en números, también podemos ignorar ceros al frente:
Entonces obtenemos una regla simple para una cadena: recortar los espacios en blanco y los ceros al frente. O obtenemos un número o una cadena vacía, en cuyo caso la convertimos a ese número o cero. O no obtenemos un número, en cuyo caso no convertimos y, por lo tanto, no obtenemos una nueva relación.
¡De esta forma podríamos obtener una relación de equivalencia perfecta en el conjunto total de booleanos, números y cadenas! Excepto que ... los diseñadores de JavaScript obviamente tienen otra opinión:
¡Entonces las dos cadenas a las que ambos se convierten de
0
repente no son similares! ¿Por qué o por qué? De acuerdo con la regla, las cadenas son más o menos iguales precisamente cuando son estrictamente iguales. ¡No solo esta regla rompe la transitividad como vemos, sino que también es redundante! ¿Cuál es el punto de crear otro operador==
para que sea estrictamente idéntico al otro===
?Conclusión
El operador de igualdad flexible
==
podría haber sido muy útil si respetara algunas leyes matemáticas fundamentales. Pero como lamentablemente no, su utilidad sufre.fuente
NaN
? Además, a menos que se aplique un formato de número específico para la comparación con cadenas, debe resultar una comparación de cadenas no intuitiva o no transitividad.NaN
actúa como mal ciudadano :-). La transitividad puede y debe mantenerse para cualquier comparación de equivalencia, intuitiva o no.Sí, me he encontrado con un caso de uso, es decir, cuando se compara una clave con un valor numérico:
Creo que es mucho más natural realizar la comparación en
key == some_number
lugar de comoNumber(key) === some_number
o comokey === String(some_number)
.fuente
Me encontré con una aplicación bastante útil hoy. Si desea comparar números rellenos, como
01
los enteros normales,==
funciona bien. Por ejemplo:Le ahorra eliminar el 0 y convertirlo en un entero.
fuente
'04'-0 === 4
, o posiblementeparseInt('04', 10) === 4
+'01' === 1
'011' == 011 // false
en modo no estricto y SyntaxError en modo estricto. :)Sé que esta es una respuesta tardía, pero parece que hay alguna posible confusión acerca de qué,
null
y enundefined
mi humilde opinión, es lo que hace el==
mal, más que la falta de transitividad, que es lo suficientemente malo. Considerar:¿Qué significan estos?
p1
tiene un supervisor cuyo nombre es "Alicia".p2
tiene un supervisor cuyo nombre es "Ninguno".p3
explícitamente, inequívocamente, no tiene un supervisor .p4
puede o puede tener un supervisor. No sabemos, no nos importa, se supone que no debemos saber (¿problema de privacidad?), Ya que no es asunto nuestro.Cuando lo usas,
==
estás combinandonull
yundefined
eso es totalmente inapropiado. ¡Los dos términos significan cosas completamente diferentes! ¡Decir que no tengo un supervisor simplemente porque me negué a decirte quién es mi supervisor está equivocado!Entiendo que hay programadores a los que no les importa esta diferencia
null
yundefined
eligen usar estos términos de manera diferente. Y si su mundo no usanull
yundefined
correctamente, o si desea dar su propia interpretación de estos términos, que así sea. Sin embargo, no creo que sea una buena idea.Ahora, por cierto, no tengo ningún problema
null
yundefined
ambos son falsos. Está perfectamente bien deciry luego
null
yundefined
haría que el código que procesa el supervisor a ser omitido. Eso es correcto, porque no sabemos o no tenemos un supervisor. Todo bien. Pero las dos situaciones no son iguales . Por eso==
está mal. Una vez más, las cosas pueden ser falsas y usarse en un sentido de tipeo de pato, lo cual es ideal para lenguajes dinámicos. Es adecuado JavaScript, Pythonic, Rubyish, etc. Pero nuevamente, estas cosas NO son iguales.Y no me refiero a no transitividad:
"0x16" == 10
,10 == "10"
pero no"10" == "0x16"
. Sí, JavaScript es débilmente tipos. Sí, es coercitivo. Pero la coercitividad nunca debería aplicarse a la igualdad.Por cierto, Crockford tiene opiniones fuertes. ¿Pero sabes que? Él tiene razón aquí!
FWIW ¡Entiendo que existen, y personalmente me he encontrado con situaciones en las que
==
es conveniente! Como tomar una entrada de cadena para números y, por ejemplo, compararla con 0. Sin embargo, esto es pirateo. Usted tiene la conveniencia de una compensación por un modelo incorrecto del mundo.TL; DR: la falsedad es un gran concepto. No debe extenderse a la igualdad.
fuente
p5
... la única situacióntypeof(p5.supervisor) === typeof(undefined)
en la que el supervisor ni siquiera existe como concepto: D