Abstract equality and inequality

The abstract equality (==) and inequality (!=) operators rely on the same algorithm internally, which is responsible for determining whether two values can be considered equal. In this section, our examples will only explore ==, but rest assured that != will always simply be the opposite of whatever == is.

In the vast majority of cases, it is not advisable to rely on abstract equality because its mechanism can create unexpected results. Most of the time, you'll want to opt for strict equality (that is, === or !==). 

Where both operands, the left-side and the right-side, are of the same type, then the mechanism is quite simple—the operator will check whether the two operands are identical values:

100 == 100;     // => true
null == null; // => true
'abc' == 'abc'; // => true
123n == 123n; // => true
When both operands are of the same type, abstract equality (==) is exactly identical to strict equality (===).

Since all non-primitives in JavaScript are of the same type (Object), abstract equality ( ==will always return false if you try to compare two non-primitives (two objects) that don't refer to the exact same object:

[123] == [123]; // => false
/123/ == /123/; // => false
({}) == ({}); // => false

However, where both operands are of different types, for example, where you are comparing a Number type to a String type or an Object type to a Boolean type, the exact behavior of abstract equality will depend on the operands themselves.

If either operand is Number, and the other is String, then the a == b operation is equivalent to the following:

Number(a) === Number(b)

Here are some examples of this in action:

123 == '123';  // => true
'123' == 123; // => true
'1e3' == 1000; // => true
Note how, as discussed in the Conversion to a number section in the last chapter, the "1e3" string will be internally converted to the number 1000.

Continuing down the rabbit holeif only one operand to the == operator is Boolean, then the operation is, once again, equivalent to Number(a) === Number(b):

false == ''; // => true
// Explanation: Number(false) is `0` and Number('') is `0`

true == '1'; // => true
// Explanation: Number(true) is `1` and Number('1') is `1`

true == 'hello'; // => false
// Explanation: Number(true) is `1` and Number('hello') is `NaN`

false == 'hello'; // => false
// Explanation: Number(false) is `0` and Number('hello') is `NaN`

Finally, if previous conditions are not met, and if either operand is Object (not a primitive), then it will compare the primitive representation of that object to the other operand. As discussed in the last chapter, in the Conversion to a primitive section, this will attempt to call the [Symbol.toPrimitive](), valueOf(), and then toString() methods to establish the primitive. We can see this in action here:

new Number(1) == 1; // => true
new Number().valueOf(); // => 1
({ valueOf() { return 555; }) == 555; // => true

Due to their complicated coercive behaviors, the abstract equality and inequality operators are best avoided. Anyone reading code littered with these operators won't be able to have a good level of confidence in the conditions and control flow of the program because there are simply too many odd edge cases where abstract equality can bite.

If you find yourself wanting to use abstract equality, for example, when one operand is a number and another is a string, consider whether it might be clearer and less error-prone to use a combination of stricter checks or to explicitly cast your values for clarity; for example, instead of aNumber == aNumericStringyou could do aNumber === Number(aNumericString).
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.150.123