ç¸çæ¯è¼
å¨ ES2015ï¼æååç¸çæ¯è¼æ¹æ³ï¼
- ä¸è¬ç¸ç (
==) - å´æ ¼ç¸ç (
===)ï¼è¢«ç¨æ¼Array.prototype.indexOfãArray.prototype.lastIndexOfåcase-matching - é¶å¼ç¸çï¼è¢«ç¨æ¼
%TypedArray%åArrayBuffer建æ§åï¼ä»¥åMapåSetéç®åï¼éæå°å¨ ES2016 æ°å¢çString.prototype.includesã - åå¼ç¸çï¼ ç¨å¨é¤ä¸é¢æåçæææ æ³ã
JavaScript æä¾ä¸ç¨®ä¸åç弿¯è¼éç®æä½ï¼
- å´æ ¼ç¸ç (æç¨± "ä¸çæ¼"ã"å ¨ç") ä½¿ç¨ ===
- ä¸è¬ç¸ç ("éçæ¼") ä½¿ç¨ ==
- éæ
Object.is(ECMAScript 2015 æ°å å ¥)
è¦ç¨åªåæä½å決æ¼ä½ è¦åªç¨®é¡åçæ¯è¼ã
ç°¡å®ä¾èªªï¼ä¸è¬ç¸çæå°åå¥ä¸è´å徿¯è¼ï¼å´æ ¼ç¸çå䏿ï¼ä¹å°±æ¯èªªè¥åå¥ä¸åï¼å°±æåå³ fasleï¼ï¼Object.is æåå´æ ¼ç¸çå忍£çäºï¼ä½æå° NaNã-0 å +0 ç¨ç«èçï¼å æ¤éä¸å䏿ç¸çï¼è Object.is(NaN, NaN) åæåå³ true ãï¼ç¨ä¸è¬ç¸çæå´æ ¼ç¸çæ¯è¼å
©å NaN ææåå³ false ï¼å çº IEEE 754 妿¤è¦ç¯ãï¼ åè¨ï¼éä¸ç¨®å¤æ·å¿
é èæ
®ååï¼å çºä»åå¨è¨è¨ä¸ä¸è¢«èæ
®çºç¸çãå°æ¼ä»»ä½éååç©ä»¶ xãyï¼å³ä½¿ä»åæèç¸åçµæ§ï¼ä½å¦ææ¯ä¸åç©ä»¶ï¼æ¯è¼å°±ææ¯ falseã
å´æ ¼ç¸çï¼===ï¼
å´æ ¼ç¸çæ¯è¼å ©åå¼ï¼è被æ¯è¼çå ©åå¼é½ä¸æè½ææå ¶ä»åå¥ã妿弿¯ä¸ååå¥ï¼å°±æè¢«è¦çºä¸ç¸çãå¦æå ©å¼åå¥ç¸åä½ä¸æ¯æ¸åï¼è¥å¼ç¸åï¼åçºç¸çãæ¤å¤ï¼å¦æå ©åå¼ççºæ¸åï¼åªè¦ä»åæ¯ NaN 以å¤çåä¸å¼ï¼æè +0 å -0ï¼åçºç¸çã
var num = 0;
var obj = new String("0");
var str = "0";
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
å´æ ¼æ¯è¼é©åå¨çµå¤§å¤æ¸æ
æ³ä¸ä½¿ç¨ãå°æ¼ææéæ¸åçå¼ï¼å´æ ¼æ¯è¼å°±å¦åé¢ï¼ä¸åå¼åªç¸çæ¼èªå·±ãèæ¸åå使ç¨ç¨å¾®ä¸åçæ¹å¼ï¼ç¬¬ä¸ç¨®æ
æ³æ¯æµ®é»æ¸ 0 åæçºæ£åè² ï¼å¨è§£æ±ºæäºæ¸å¸å顿ï¼+0 å -0 æ¯ä¸åçï¼ä½å¨å¤§é¨åæ
æ³ä¸æåä¸éè¦èæ
®é種æ
å¢ï¼å æ¤å´æ ¼æ¯è¼å°ä»åè¦çºç¸åçã第äºç¨®æ
æ³æ¯éæ¸åï¼NaNï¼ç¨ä¾è¡¨ç¤ºæäºå®ç¾©ä¸æç¢ºçæ¸å¸åé¡çè§£ï¼ ä¾å¦ï¼è² ç¡çª®å æ£ç¡çª®ï¼å´æ ¼æ¯è¼èªçº NaN ä¸çæ¼ä»»ä½å¼ï¼å
å«ä»æ¬èº«ãï¼(x !== x)åªæå¨ x æ¯ NaNæææ¯ trueãï¼
ä¸è¬ç¸çï¼==ï¼
ä¸è¬ç¸çæå
å°æ¯è¼å¼è½ææååå¥å¾æ¯è¼ãè½æå¾ï¼å¯è½ä¸åæå
©åé½è¢«è½æï¼ï¼æ¥èé²è¡çå¹¾ä¹åå´æ ¼æ¯è¼ï¼===ï¼ä¸æ¨£ã ä¸è¬ç¸çæå°ç¨±ï¼ A == B çå B == A ï¼ç¡è« A å B æ¯ä»éº¼ãï¼é¤äºåå¥è½æçé åºï¼
ä¸ååå¥çä¸è¬ç¸çéä½å¦ä¸è¡¨ï¼
| æ¯è¼å¼ B | |||||||
|---|---|---|---|---|---|---|---|
| Undefined | Null | Number | String | Boolean | Object | ||
| æ¯è¼å¼ A | Undefined | true |
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 |
|
æ ¹æä¸è¡¨ï¼ ToNumber(A) åè©¦å¨æ¯è¼åè½ææä¸åæ¸åã éçå +A ï¼å® + éç®åï¼ãToPrimitive(A) å試å¾ç©ä»¶è½ææåçå¼ï¼ééåè©¦å° A ä½¿ç¨ A.toString å A.valueOf æ¹æ³ã
ä¸è¬ä¾èªªï¼æ ¹æ ECMAScript è¦ç¯ï¼ææç©ä»¶æè©²ä¸çæ¼ undefined å nullãä½å¤§å¤æ¸ç覽å¨å
許å¾å°é¨åçç©ä»¶ï¼å°¤å
¶æ¯ææé é¢ç document.all ç©ä»¶ï¼å¨æäºæ
æ³ä¸ç¶æ undefinedãä¸è¬ç¸çæ¯å
¶ä¸ä¸ç¨®ï¼ç¶ A æ¯åè¢«æ¨¡æ¬ æ undefined çç©ä»¶ï¼null == A å undefined == A ææ¯ trueãèå¨å
¶ä»æ
æ³ä¸ç©ä»¶ä¸æçåæ¼ undefined æ nullã
var num = 0;
var obj = new String("0");
var str = "0";
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
// é¤äºå°æ¸æ
æ³ï¼éå
©åæè©²æ¯ falseã
console.log(obj == null);
console.log(obj == undefined);
é¨åéç¼è èªçºæå¥½å¥ç¨ä¸è¬ç¸çãå´æ ¼æ¯è¼æ´å®¹æé 測ï¼ä¸å çºä¸å¿ è½åï¼å æ¤æçæ´å¥½ã
åå¼ç¸ç
åå¼ç¸çè§£æ±ºäºæå¾ä¸åæ æ³ï¼æ¯è¼å ©å弿¯å¦åè½ç¸å ãï¼é裡ç¨äºéæ°æ¿æååçºä¾ï¼ç¶è©¦åä¿®æ¹ä¸åä¸å¯è®ç屬æ§ï¼
// æ°å¢ä¸åä¸å¯è® NEGATIVE_ZERO 屬æ§å° Number ååã
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 æåºç¾ä¾å¤ï¼ä½è¥æ²æççè¦æ±ä¿®æ¹ï¼å°±æ²äºã妿 v æ¯ -0ï¼å°±ä¸ææä¿®æ¹ï¼ä¹å°±ä¸ææé¯èª¤åºç¾ãä½è¥ v æ¯ +0ï¼Number.NEGATIVE_ZERO ä¸åææèªå·±çä¸å¯è®å±¬æ§ãå¨å
§é¨ï¼ç¶ä¸åä¸å¯è®å±¬æ§è¢«éæ°å®ç¾©ï¼æ°ç弿ç¨åå¼ç¸çåå弿¯è¼ã
Object.is æ¹æ³æä¾åå¼ç¸çæ¯è¼ã
é¶å¼ç¸ç
ååå¼ç¸ç䏿¨£ï¼ä½å° +0 å -0 è¦çºç¸åã
ä¸è¬ç¸çãå´æ ¼ç¸çååå¼ç¸ççè¦ç¯
å¨ ES5ï¼ä¸è¬ç¸ç == å¨ Section 11.9.3, The Abstract Equality Algorithm ä¸è¦ç¯ãå´æ ¼ç¸ç === å¨ 11.9.6, The Strict Equality Algorithmãï¼å¯ä»¥ççï¼éå¾ç°¡çä¸å¯è®ã註ï¼å
è®å´æ ¼ç¸çãï¼ES5 ä¹å¨ Section 9.12, The SameValue Algorithm è¦ç¯ JS 弿çè¡çºãä»å¹¾ä¹åå´æ ¼ç¸ç䏿¨£ï¼é¤äº 11.9.6.4 å 9.12.4 å¨èç Number æçä¸åãES2015 ç°¡ççæåºäº Object.isã
æåå¯ä»¥ç¼ç¾å¨ 11.9.6.1 ä¸ï¼é¤äº 11.9.6.1 è¦ç¯å奿ª¢æ¥ï¼å´æ ¼ç¸çè¦ç¯æ¯å¾å±¬æ¼ä¸è¬ç¸çè¦ç¯ï¼å çº 11.9.6.2â7 å 11.9.3.1.aâf ç¸æã
çè§£ç¸çæ¯è¼æ¨¡å
ES2015 以å¾ï¼ä½ æè¨±æå°éçæ¼åä¸çæ¼è§£è®ææ¯å½¼æ¤çãå å¼·çããæ¯å¦ï¼æäººæè¨±æèªªéçæ¼æ¯ä¸çæ¼çå»¶ä¼¸çæ¬ï¼å çºåè
åçäºæ
åå¾è
äºæ
䏿¨¡ä¸æ¨£ï¼åªå·®å¨éç®å
çåå¥è½æãèä¾ä¾èªªï¼6 == "6" ï¼åæè
èªªï¼æäººå¯è½æè¬èªªéçæ¼æ¯åºåºï¼èä¸çæ¼æ¯å å¼·çï¼å çºå®è¦æ±å
©åéç®å
æ¯ååå¥ï¼æä»¥å®å¤äºä¸åéå¶ãè³æ¼åªåæ¯è¼å¥½çç解模åï¼å決æ¼ä½ çè§é»ã
åç®¡å¦æ¤ï¼éåæèå
§å»ºç¸åéç®åçæ¹æ³ï¼ä¸¦éæ¯å»¶ä¼¸ ES2015 ä¸ç Object.is æ¹æ³ã Object.is 䏿¯å®ç´å°æ¯éçèãæ´å¯¬é¬ãææ¯ä¸çèãæ´å´è¬¹ãï¼ä¹ä¸é©åå°å
¶æ¾å¨å
©è
ä¹éï¼å³ï¼æ¯éçèå´è¬¹ï¼ä½è¼ä¸çè寬é¬ï¼ãæåå¯ä»¥å¾ä¸æ¹çæ¯è¼è¡¨æ ¼çå°ï¼ä¸åæ¯èµ·æºæ¼ Object.is å¯ä»¥èç NaN çæ¯è¼éç®ãè¦æ³¨æçæ¯ï¼å¦æ Object.is(NaN, NaN) çéç®çµææ¯ false ï¼æåå°±å¯ä»¥å çºå®åå -0 å +0 ççµæï¼ä½¿ç¨å¯¬é¬åå´è¬¹çç¯çä¾çå®å®æ¯æ¯ä¸çèæ´å´è¬¹çé£ä¸å段ãç¶èï¼åå¥ NaN çæ¹å¼ä¸¦ä¸ç¢ºå¯¦ãUnfortunately, Object.is simply has to be thought of in terms of its specific characteristics, rather than its looseness or strictness with regard to the equality operators.
| x | y | == |
=== |
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 |
When to use Object.is versus triple equals
Aside from the way it treats NaN, generally, the only time Object.is's special behavior towards zeros is likely to be of interest is in the pursuit of certain meta-programming schemes, especially regarding property descriptors when it is desirable for your work to mirror some of the characteristics of Object.defineProperty. If your use case does not require this, it is suggested to avoid Object.is and use === instead. Even if your requirements involve having comparisons between two NaN values evaluate to true, generally it is easier to special-case the NaN checks (using the isNaN method available from previous versions of ECMAScript) than it is to work out how surrounding computations might affect the sign of any zeros you encounter in your comparison.
Here's an in-exhaustive list of built-in methods and operators that might cause a distinction between -0 and +0 to manifest itself in your code:
- (unary negation)-
It's obvious that negating
0produces-0. But the abstraction of an expression can cause-0to creep in when you don't realize it. For example, consider:js let stoppingForce = obj.mass * -obj.velocityIfobj.velocityis0(or computes to0), a-0is introduced at that place and propogates out intostoppingForce. Math.atan2,Math.ceil,Math.pow,Math.round-
It's possible for a
-0to be introduced into an expression as a return value of these methods in some cases, even when no-0exists as one of the parameters. E.g., usingMath.powto raise-Infinityto the power of any negative, odd exponent evaluates to-0. Refer to the documentation for the individual methods. Math.floor,Math.max,Math.min,Math.sin,Math.sqrt,Math.tan-
It's possible to get a
-0return value out of these methods in some cases where a-0exists as one of the parameters. E.g.,Math.min(-0, +0)evalutes to-0. Refer to the documentation for the individual methods. ~,<<,>>-
Each of these operators uses the ToInt32 algorithm internally. Since there is only one representation for 0 in the internal 32-bit integer type,
-0will not survive a round trip after an inverse operation. E.g., bothObject.is(~~(-0), -0)andObject.is(-0 << 2 >> 2, -0)evaluate tofalse.
Relying on Object.is when the signedness of zeros is not taken into account can be hazardous. Of course, when the intent is to distinguish between -0 and +0, it does exactly what's desired.