Existen cuatro algoritmos de igualdad en ES2015:
==
)===
): utilizada por Array.prototype.indexOf
, Array.prototype.lastIndexOf
, y case
-matching%TypedArray%
y ArrayBuffer
, asà como por las operaciones Map
y Set
, y también por String.prototype.includes
y Array.prototype.includes
desde ES2016JavaScript proporciona tres operaciones distintas para comparar la igualdad de dos elementos:
Object.is
proporciona SameValue (nuevo en ES2015).Elegir una u otra operación depende del tipo de comparación que se esté buscando realizar.
Explicándolo de manera concisa, un doble igual lleva a cabo una conversión de tipo (cast) cuando se comparan dos cosas; el triple igual hace lo mismo pero sin realizar ninguna conversión de tipo (cast) (simplemente devuelve falso si los tipos de los elementos a comparar son diferentes); Y Object.is funciona de la misma manera que el triple igual pero hace una gestión especial de NaN, -0 y +0 de tal manera que los dos últimos no son iguales mientras que Object.is(NaN, NaN) nos devolverá true
. (Si comparamos NaN con NaN de manera ordinaria , por ejemplo usando el doble igual o el triple igual, nos devolverá false
, ya que la especificación IEEE 754 asà lo dice). Hay que darse cuenta de que la distinción entre todas estas posibilidades tiene que ver con como se manejan los tipos primitivos; ninguna de ellas compara si los parámetros son similares en relación a su estructura. Para comparar dos objectos no primitivos , x e y, que tengan la misma estructura pero que sean objetos diferentes entre ellos, todos los casos anteriormente descritos devolverán false.
===
El operador igualdad estricta compara la igualdad de dos valores. Ninguno de estos valores se convierte de manera implÃcita antes de ser comparado. Si los valores tienen tipos diferentes son considerados diferentes. Por el contrario, si los valores tienen el mismo tipo y no son números, son considerados iguales si tienen el mismo valor. Finalmente, si ambos valores son números, son considerados iguales si ambos no son NaN y tienen el mismo valor, o si uno es +0 y otro -0.
var num = 0;
var obj = new String("0");
var str = "0";
var b = false;
console.log(num === num); // true
console.log(obj === obj); // true
console.log(str === str); // true
console.log(num === obj); // false
console.log(num === str); // false
console.log(obj === str); // false
console.log(null === undefined); // false
console.log(obj === null); // false
console.log(obj === undefined); // false
La igualdad estricta es casi siempre el operador igualdad más adecuado. Para todos los valores, excepto para los números, utiliza la semántica obvia: un valor sólo es igual asà mismo. Para número usa una semántica ligeramente diferente para paliar dos casos lÃmites diferentes. El primero es que usando número en coma flotante el cero puede ser positivo o negativo. Esto es útil para representar ciertas soluciones matemáticas, pero en la mayorÃa de las situaciones no nos importa esa diferencia entre +0 y -0. La igualdad estricta los trata como un únicomvalor. El segundo caso tiene que ver con que los número en coma flotante incluyen el concepto NaN (Not a Number) como un posible valor para representar la solución a ciertos problemas matemáticos mal definidos, por ejemplo la adición de un infinito negativo a un infinito positivo. La igualdad estricta trata NaN como desigual con cualquier otro valore â incluyendo a sà mismo. (El único caso en el que x !== x es verdades en cuando x is NaN).
Igualdad débil usando ==El operador igualdad débil compara la igualdad de dos valores después de convertir ambos valores a un tipo de datos común. Tras la conversión , la comparación que se lleva a cabo funciona exactamente como ===. La igual débil es una igualdad simétrica: A == B tiene una semática idéntica a B == A para cualquier valor que tengan A y B ( excepto por el orden de las .conversiones de tipo aplicadas)
Para los operandos de varios tipos funciona de la siguiente manera:
Operando B Undefined Null Number String Boolean Object Operando A Undefinedtrue
true
false
false
false
false
Null true
true
false
false
false
false
Number false
false
A === B
A === ToNumber(B)
A === ToNumber(B)
A == ToPrimitive(B)
String false
false
ToNumber(A) === B
A === B
ToNumber(A) === ToNumber(B)
A == ToPrimitive(B)
Boolean false
false
ToNumber(A) === B
ToNumber(A) === ToNumber(B)
A === B
ToNumber(A) == ToPrimitive(B)
Object false
false
ToPrimitive(A) == B
ToPrimitive(A) == B
ToPrimitive(A) == ToNumber(B)
A === B
En la tabla previa, ToNumber(A)
intenta convertir su argumento a número antes de realizar la compración. Su comportamiento es equivalente a +A
(el operador unario +). ToPrimitive(A)
intenta convertir su objeto argumento a un valor de tipo primitivo realizando varias secuencias de invocaciones A.toString
y A.valueOf
en A
.
Tradicionalmente, y de acuerdo con la especificación ECMAScript, todos los objetos son débilmente desiguales comparándolos con undefined
y null
. Pero algunos nevegadores permiten que una cantidad muy limitada de clases
de objetos (especifÃcamente , el objeto documento.all
para todas las páginas), en algunos contextos, puedan actuar como si emularan el valor undefined
. En ese contexto se evalúa como verdadero las igualdades débiles null == A
y undefined == A
, sà y sólo sÃ, A es un objecto que emula undefined
. En cualquier otro caso la igual débil no será verdadera con undefined
o null
.
var num = 0;
var obj = new String("0");
var str = "0";
var b = false;
console.log(num == num); // true
console.log(obj == obj); // true
console.log(str == str); // true
console.log(num == obj); // true
console.log(num == str); // true
console.log(obj == str); // true
console.log(null == undefined); // true
// both false, except in rare cases
console.log(obj == null);
console.log(obj == undefined);
Algunos desarrolladores consideran que nunca es una buena idea usar este tipo de igualdad, la igualdad débil. El resultado cuando se usa la igualdad estricta es más fácil de predecir y , como no hay coerción de tipos durante la evaluación, es con casi total seguridad más rápida.
Igualdad Same-valueLa igualdad Same-value se encarga de un último caso de uso: determinar si dos valores son funcionalmente idénticos en todos los contextos. (Este caso de uso es un caso de ejemplo del Liskov substitution principle.) Un ejemplo de esto ocurre cuando se intenta hacer mutable una propiedad inmutable.
// Add an immutable NEGATIVE_ZERO property to the Number constructor.
Object.defineProperty(Number, "NEGATIVE_ZERO", {
value: -0,
writable: false,
configurable: false,
enumerable: false,
});
function attemptMutation(v) {
Object.defineProperty(Number, "NEGATIVE_ZERO", { value: v });
}
Object.defineProperty
que lanzará una excepción cuando se intente cambiar una propiedad inmutable la cambiará, pero no hará nada si al solicitar el cambio actual . Si v es -0, no ha sido solicitado ningún cambio y no se lanzará ningún error. Pero si v es +0, Number
. NEGATIVE_ZERO no tendrá más su valor inmutalbe. Internamente, al redefinir una propiedad inmutbale, el nuevo valor se compara con el valor actual usando la igualdad same-value.
El método Object.is
nos proporciona la igualdad same-value.
Similar a la igualdad same-value, pero +0 y -0 son considerados iguales.
Igualdad abstracta, igualdad estricta e igualdad same value en la especificaciónEn la especificación ES5, la comparación ==
queda descrita en Section 11.9.3, The Abstract Equality Algorithm. La comparación ===
en 11.9.6, The Strict Equality Algorithm. (Búscala y leela, son breves y fáciles de leer. Nota: lee el algoritmo de la igualdad estricta primero.) ES5 también describe, en Section 9.12, The SameValue Algorithm para uso interno del motor JS. Es, en su mayorÃa igual que el algoritmo de igualdad estricto, excepto porque 11.9..6.4 y 9.12.4 difieren en cómo tratar los Numbers
. ES2015 simplemente propone exponer este algoritmo mediante el uso deObject.is
.
Podemos ver con el igual doble y el triple que, con la excepción de hacer con antelación una comprobación de tipo en 11.9.6.1, el algorimto de igualdad estricta es un subconjunto del algorimot de igualdad abstracta porque 11.9.6.2-7 se corresponde con 11.9.3.1a-f.
¿ Un modelo para comprender las comparacions de igualdad?Antes de ES2015, podÃamos haber dicho sobre el igual doble y el igual triple que uno es una versión mejoradad del otro. Por ejemplo, alguien podrÃa decir que el igual doble es una versión extendida del igual triple ya que el primero hace todo lo que hace el segundo pero añadiendo la conversión de operadores. Por ejemplo 6 == "6". (De manera alternativa alguien podrÃa decir que el igual doble es la base y que el igual triple es una versión mejorada, ya que requiere que los dos operadores sean del mismo tipo y, por lo tanto, añade una restricción adicional. Qué afirmación es mejor para entender el modelo depende en tu punto de vista).
Sin embargo. esta manera de pensar sobre los operadores de igualdad proporcionados de manera nativa no es un modelo en el que podamos encuadrar la versión ES2015 de Object.is
no es simplemente más débil que el doble igual o más estricto que el triple igual, ni tampoco ocupa un lugar intermedio (por ejemplo siendo a la vez más estricto que el igual doble y más débil que el igual triple). Podemos ver en la tabla inferior que esto se debe a la manera en la que Object.is
maneja NaN
. FÃjate que si Object.is(Nan, Nan)
evaluara a falso podrÃamos decir que que se encuadra dentro de la escala débil /estricta como algo más estricto que el triple igual, como un operador que distigue entre +0 y -0. Sin embargo en el manejo de NaN
esto no es cierto. Simplemente debemos considerar Object.is
en términos de sus caracterÃsticas especÃficas y no en relación a su debilidad o rigidez dentro del especto de los operadores de igualdad.
==
===
Object.is
undefined
undefined
true
true
true
null
null
true
true
true
true
true
true
true
true
false
false
true
true
true
"foo"
"foo"
true
true
true
{ foo: "bar" }
x
true
true
true
0
0
true
true
true
+0
-0
true
true
false
0
false
true
false
false
""
false
true
false
false
""
0
true
false
false
"0"
0
true
false
false
"17"
17
true
false
false
[1,2]
"1,2"
true
false
false
new String("foo")
"foo"
true
false
false
null
undefined
true
false
false
null
false
false
false
false
undefined
false
false
false
false
{ foo: "bar" }
{ foo: "bar" }
false
false
false
new String("foo")
new String("foo")
false
false
false
0
null
false
false
false
0
NaN
false
false
false
"foo"
NaN
false
false
false
NaN
NaN
false
false
true
Cuando usar Object.is
o el igual triple
Además de como trata NaN
, generalmente, la única vez en la que Object.is
posee un comportamiento especial hacia los ceros puede resultar de interés para usar ciertos esquemas de meta-programación, sobre todo en relación a los descriptores de porpiedades cuando es deseable que nuestro trabajo replique algunas de las caracterÃsticas de Object.defineProperty
. Si en tu situación no requiere de esto, lo mejor es evitar Object.is
y usar ===.
Incluso si entre tus requisitos está poseer que la comparación entre dos valores NaN
sea verdadera, generalmente es más fácil hacer un caso especial para ello (usando el método isNaN
que está disponible en versiones previas de ECMAScript) que calcular cómo la operaciones van afectar a los posibles signos de los valores cero en tu comparación.
Aquà podemos ver una lista exhaustiva de los método y operadores nativos que pueden distinguir entre -0 y +0 en tu código:
- (unary negation)
Obviamente negar 0 genera -0. Pero al abstracción de una expresión puede causar que un valor -0 se cuele sin darte cuenta
Consideremos el siguiente ejemplo:
let stoppingForce = obj.mass * -obj.velocity;
Si obj.velocity
is 0
(o se calcula como 0
), se inserta -0
en ese lugar y este valor se propaga a stoppingForce
.
Math.atan2
Empty
Math.ceil
Empty
Math.pow
Empty
Math.round
Se puede introducir un -0 dentro de una expresión como valor de retorno en estos método, incluso cuando -0 no sea uno de los parámetros. Por ejemplo usando Math.pow
para elevar -Infinity
a cualquier potencia negativa, los exponentes impares se evaluarán como -0. Consulta la documentación para más detalles sobre cada uno de los métodos.
Math.floor
Empty
Math.max
Empty
Math.min
Empty
Math.sin
Empty
Math.sqrt
Empty
Math.tan
Es posible obtener, en algunos casos, -0 como valor de retorno de estos métodos cuando -0 sea uno de los parámetros Por ejemplo Math.min(-0, +0)
devuelve -0. Consulta la documentación para más detalles sobre cada uno de los métodos.
~
Empty
<<
Empty
>>
Cada uno de estos operadores usa el algoritmo ToInt32 de manera interna. Como sólo hay un representacion de 0 para el intero de 32-bit interno. -0 no sobrevivirá a la operación inversa. Por ejemplo Object.is(~~(-0), -0) y Object.is(-0 << 2 >> 2, -0) devolverán false.
Confiar en Object.is
cuando no hay que tener en cuenta el signo de los ceros puede ser peligroso. Por supuesto para el caso contrario hará exactamente lo deseado.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4