Estoy familiarizado con NaN siendo "raro" en JavaScript, es decir, NaN === NaN siempre devuelve false , como se describe aquí . Por lo tanto, no se deben hacer comparaciones === para verificar NaN , sino usar isNaN(..) en su lugar.
Así que me sorprendió descubrir que
> [NaN].includes(NaN) trueEsto parece inconsistente. ¿Por qué tener este comportamiento?
¿Cómo funciona? ¿El método includes comprueba específicamente isNaN ?
Como puede ver al leer la documentación de inclusión, utiliza el algoritmo sameValueZero para funcionar, por lo que, como dice su documentación , proporciona un valor True al comparar NaN y cito:
Podemos ver en la siguiente tabla de comparaciones de igualdad que esto se debe a la forma en que
Object.ismaneja NaN. Tenga en cuenta que si Object.is(NaN, NaN) se evaluó como falso, podríamos decir que encaja en el espectro flexible/estricto como una forma aún más estricta de triple igual, una que distingue entre -0 y +0. Sin embargo, el manejo de NaN significa que esto no es cierto. Desafortunadamente,Object.istiene que ser pensado en términos de sus características específicas, en lugar de su laxitud o rigurosidad con respecto a los operadores de igualdad.
El método .includes() usa el algoritmo SameValueZero para verificar la igualdad de dos valores y considera que el valor de NaN es igual a sí mismo.
El algoritmo SameValueZero es similar a SameValue , pero la única diferencia es que el algoritmo SameValueZero considera que +0 y -0 son iguales.
El método Object.is() usa SameValue y devuelve verdadero para NaN .
console.log(Object.is(NaN, NaN)); El comportamiento del método .includes() es ligeramente diferente del método .indexOf() ; el método .indexOf() usa una comparación de igualdad estricta para comparar valores y la comparación de igualdad estricta no considera que NaN sea igual a sí mismo.
console.log([NaN].indexOf(NaN));Puede encontrar información sobre diferentes algoritmos de verificación de igualdad en MDN:
En 7.2.16 Comparación de igualdad estricta , hay la siguiente nota:
NOTA
Este algoritmo difiere del algoritmo SameValue en su tratamiento de ceros con signo y NaN.
Esto significa que Array#includes una función de comparación diferente que para una comparación estricta:
22.1.3.13 Array.prototype.incluye bajo
NOTA 3
El método include difiere intencionalmente del método similar indexOf de dos maneras. En primer lugar, utiliza el algoritmo SameValueZero , en lugar de Strict Equality Comparison , lo que le permite detectar elementos de matriz NaN . En segundo lugar, no omite los elementos de matriz faltantes, en lugar de tratarlos como indefinidos .
Esto parece ser parte de la operación abstracta Number::sameValueZero :
6.1.6.1.15 Número::mismoValorCero ( x , y )
- Si x es NaN e y es NaN , devuelve verdadero .
[...]
Se requiere que esta operación sea parte de la verificación Array#includes() que hace lo siguiente:
22.1.3.13 Array.prototype.includes ( searchElement [ , fromIndex ] )
[...]
- Repetir, mientras k < len
un. Sea elementK el resultado de ? Get( O , ! ToString( k )).
B. Si SameValueZero( searchElement , elementK ) es verdadero , devuelve verdadero .
C. Establezca k en k + 1.- Devuelve falso .
[...]
Donde la operación SameValueZero se delegará a la de números en el paso 2:
7.2.12 MismoValorCero ( x , y )
[...]
- Si Type( x ) es diferente de Type( y ), devuelve false .
- Si Type( x ) es Number o BigInt, entonces
un. Regreso ! Tipo( x )::mismoValorCero( x , y ).- Regreso ! MismoValorNoNumérico( x , y ).
Para la comparación Array#indexOf() utilizará la comparación de igualdad estricta , por lo que se comporta de manera diferente:
const arr = [NaN]; console.log(arr.includes(NaN)); // true console.log(arr.indexOf(NaN)); // -1 Otras operaciones que usan SameValueZero para comparar están en conjuntos y mapas:
const s = new Set(); s.add(NaN); s.add(NaN); console.log(s.size); // 1 console.log(s.has(NaN)); // true s.delete(NaN); console.log(s.size); // 0 console.log(s.has(NaN)); // false const m = new Map(); m.set(NaN, "hello world"); m.set(NaN, "hello world"); console.log(m.size); // 1 console.log(m.has(NaN)); // true m.delete(NaN); console.log(m.size); // 0 console.log(m.has(NaN)); // false El algoritmo SameValueZero aparece por primera vez en las especificaciones de ECMAScript 6, pero es más detallado. Todavía tiene el mismo significado y todavía tiene un explícito:
7.2.10 MismoValorCero( x , y )
[...]
- Si Tipo( x ) es Número, entonces a. Si x es NaN e y es NaN , devuelve verdadero . [...]
ECMAScript 5.1 solo tiene un algoritmo SameValue que aún trata a NaN igual a NaN . La única diferencia con SameValueZero es cómo se tratan +0 y -0 : SameValue devuelve false para ellos, mientras que SameValueZero devuelve true .
SameValue se usa principalmente para la operación de objetos internos, por lo que es casi intrascendente para escribir código JavaScript. Muchos de los usos de SameValue son cuando se trabaja con claves de objetos y no hay valores numéricos.
La operación SameValue se expone directamente en ECMAScript 6, ya que eso es lo que usa Object.is() :
console.log(Object.is(NaN, NaN)); // true console.log(Object.is(+0, -0)); // false De leve interés es que WeakMap y WeakSet también usan SameValue en lugar de SameValueZero que Map y Set usan para comparar. Sin embargo, WeakMap y WeakSet solo permiten objetos como miembros únicos, por lo que intentar agregar un NaN o +0 o -0 u otras primitivas genera un error.
Según el documento de MDN dice que
Nota: Técnicamente hablando, include
includes()usa el algoritmosameValueZeropara determinar si se encuentra el elemento dado.
const x = NaN, y = NaN; console.log(x == y); // false -> using 'loose' equality console.log(x === y); // false -> using 'strict' equality console.log([x].indexOf(y)); // -1 (false) -> using 'strict' equality console.log(Object.is(x, y)); // true -> using 'Same-value' equality console.log([x].includes(y)); // true -> using 'Same-value-zero' equalityObject.is() y === está en su tratamiento de los ceros con signo y los NaN.