JavaScript developer Douglas Crockford once referred to the JavaScript ==
and !=
operators as "evil twins" that should be avoided. However, once you understand them, these operators aren't that bad and can actually be useful. This article will explore ==
and !=
, explain how they work, and help you understand them better.
Key Points
==
operators in JavaScript are not inherently evil; they perform type casts when comparing different types of values, which is both useful and tricky. !=
===
for direct type and value comparisons without casting, which is clearer and it is usually recommended to avoid unexpected results. Use !==
and ==
when you need to cast or compare values whose types may change dynamically. !=
==
comparisons, so as to predict results more accurately and avoid common pitfalls. !=
==
run in various scenarios, such as comparing strings to numbers or objects to original values, to consolidate understanding . !=
==
are not scary, they require a good understanding of JavaScript's type cast rules to be effective and safe in code Use it locally. !=
Problematic and ==
operators !=
and ===
, and !==
and ==
. Understanding why there are two sets of equality operators and in which situations to use which operator has been the source of confusion for many people. The !=
and ===
operators are not difficult to understand. When the two operand types are the same and the values are the same, !==
returns ===
and true
returns !==
. However, when the value or type is different, false
returns ===
, false
returns !==
. The true
and ==
operators behave the same when the two operand types are the same. However, when the types are different, JavaScript casts one operand !=
to another type to make the operand compatible before comparison. The results are often confusing, as follows:
"this_is_true" == false // false "this_is_true" == true // false
. Additional confusion occurs when you assume that the pass relationship (if a is equal to b and b is equal to c) should apply: true
"this_is_true" == false // false "this_is_true" == true // false
This example shows that ==
lacks transitiveness. If the empty string is equal to the number 0, and if the number 0 is equal to the string composed of character 0, the empty string should be equal to the string composed of 0. But that's not the case. When an incompatible type is encountered when comparing operands through ==
or !=
, JavaScript casts one type to another to make it comparable. Conversely, when using ===
and !==
, it never performs type casts (which leads to a slight improvement in performance). Due to different types, ===
always returns false
in the second example. Understanding rules that control how JavaScript casts operands to different types so that the two operand types are compatible before applying ==
and !=
can help you determine when it is better to use ==
and !=
, and Have confidence in using these operators. In the next section, we will explore the cast rules used with the ==
and !=
operators.
==
and !=
work?
and ==
work is to study the ECMAScript language specifications. This section focuses on ECMAScript 262. Section 11.9 of the specification introduces the equality operator. The !=
and ==
operators appear in the syntax production !=
and EqualityExpression
. (Unlike the first generation, the second generation avoids the EqualityExpressionNoIn
operator.) Let's check the in
generation shown below. EqualityExpression
'' == 0 // true 0 == '0' // true '' == '0' // false
and ==
, which are not related to this article.) Section 11.9.1 provides the following information on how !=
works: ===
!==
==
The production formula
be the result of calculating
EqualityExpression : EqualityExpression == RelationalExpression
Let.
works:- .
lref
LetEqualityExpression
be- .
lval
LetGetValue(lref)
be the result of calculating- .
rref
LetRelationalExpression
berval
Returns the result of performing an abstract equality comparisonGetValue(rref)
. (See 11.9.3.)- Section 11.9.2 provides similar information on how
rval == lval
!=
The production formula
- Let
lref
be the result of calculatingEqualityExpression
.- Let
lval
beGetValue(lref)
.- Let
rref
be the result of calculatingRelationalExpression
.- Let
rval
beGetValue(rref)
.- Let
r
be the result of performing abstract equality comparisonrval != lval
. (See 11.9.3.)- If
r
istrue
, returnfalse
. Otherwise, returntrue
.
lref
and rref
are references on the left and right sides of the ==
and !=
operators. Each reference is passed to the GetValue()
internal function to return the corresponding value. The core of how ==
and !=
work is specified by the abstract equality comparison algorithm, given in Section 11.9.3:
Compare
x == y
, wherex
andy
are values, resulting intrue
orfalse
. This comparison is carried out as follows:
- If
Type(x)
is the same asType(y)
, then
- If
Type(x)
isUndefined
, returntrue
.- If
Type(x)
isNull
, returntrue
.- If
Type(x)
isNumber
, then
- If
x
isNaN
, returnfalse
.- If
y
isNaN
, returnfalse
.- If
x
andy
are the same numerical value, returntrue
.- If
x
is 0 andy
is -0, returntrue
.- If
x
is -0 andy
is 0, thentrue
will be returned.- Return
false
.- If
Type(x)
isString
, ifx
andy
are exactly the same character sequence (the same length and the same characters in the corresponding position), thentrue
is returned. Otherwise, returnfalse
.- If
Type(x)
isBoolean
, then ifx
andy
are bothtrue
or are bothfalse
, then returntrue
. Otherwise, returnfalse
.- If
x
andy
refer to the same object, returntrue
. Otherwise, returnfalse
.- If
x
isnull
andy
isundefined
, then returntrue
.- If
x
isundefined
andy
isnull
, then returntrue
.- If
Type(x)
isNumber
andType(y)
isString
, the result of comparisonx == ToNumber(y)
is returned.- If
Type(x)
isString
andType(y)
isNumber
, the result of comparisonToNumber(x) == y
is returned.- If
Type(x)
isBoolean
, the result of comparisonToNumber(x) == y
is returned.- If
Type(y)
isBoolean
, the result of comparisonx == ToNumber(y)
is returned.- If
Type(x)
isString
orNumber
andType(y)
isObject
, the result of comparisonx == ToPrimitive(y)
is returned.- If
Type(x)
isObject
andType(y)
isString
orNumber
, the result of comparisonToPrimitive(x) == y
is returned.- Return
false
.
Step 1 The operand type is the same when executed in this algorithm. It shows that undefined
is equal to undefined
, and null
is equal to null
.It also shows that nothing equals NaN
(non-number), two identical values are equal, 0 equals -0, two strings with the same length and sequence of characters are equal, true
equals true
, false
is equal to false
, and two references to the same object are equal. Steps 2 and 3 show why null != undefined
returns false
. JavaScript considers these values to be the same. Starting from step 4, the algorithm becomes interesting. This step focuses on the equality between the Number
and String
values. When the first operand is Number
and the second operand is String
, the second operand is converted to ToNumber()
through the internal function of Number
. The expression x == ToNumber(y)
means recursion; the algorithm starting from Section 11.9.1 is reapplied. Step 5 is equivalent to Step 4, but the first operand has a type String
and must be converted to a type Number
. Steps 6 and 7 convert the Boolean operand to a Number
type and recursively. If the other operand is a boolean, it will be converted to Number
the next time this algorithm is executed, which will recurse again. From a performance point of view, you may want to make sure both operands are boolean types to avoid two recursive steps. Step 9 shows that if the type of any operand is Object
, the operand is converted to the original value through the ToPrimitive()
internal function, and the algorithm recursively. Finally, the algorithm considers that the two operands are not equal and returns false
in step 10. Although detailed, the abstract equality comparison algorithm is quite easy to understand. However, it references a pair of internal functions ToNumber()
and ToPrimitive()
, whose internal work needs to be exposed to fully understand the algorithm. The ToNumber()
function converts its parameters to Number
and is described in Section 9.3. The following list summarizes possible non-numeric parameters and equivalent return values:
Undefined
, return NaN
. Null
, return 0. true
, return 1. If the parameter is a Boolean value false
, return 0. Number
, the input parameter is returned - no conversion. String
, then Section 9.3.1 “ToNumber of string type” is applied. Returns the value corresponding to the string parameter indicated by the syntax. If the parameter does not match the indicated syntax, return NaN
. For example, the parameter "xyz" causes the return NaN
. Furthermore, parameter "29" results in a return of 29. Object
, apply the following steps: primValue
be ToPrimitive(输入参数, 提示Number)
. ToNumber(primValue)
. ToPrimitive()
function accepts an input parameter and an optional PreferredType
parameter. The input parameters are converted to non-object type. If the object can be converted to multiple primitive types, ToPrimitive()
use the optional PreferredType
prompt to bias the preferred type. The conversion is carried out as follows:
Undefined
, the input parameter (Undefined
) is returned - no conversion. Null
, the input parameter (Null
) is returned - no conversion. Boolean
, return the input parameter - no conversion. Number
, return the input parameter - no conversion. String
, return the input parameter - no conversion. Object
, the default value corresponding to the input parameter is returned. Retrieve the default value of the object by calling the object's [[DefaultValue]]
internal method and passing an optional PreferredType
prompt. The behavior of [[DefaultValue]]
is defined in Section 8.12.8 for all native ECMAScript objects. This section introduces quite a lot of theories. In the next section, we will turn to practice by providing various expressions involving ==
and !=
and gradually completing algorithmic steps.
Understand the Evil Twins
Now that we have understood how ==
and !=
work according to the ECMAScript specification, let us take advantage of this knowledge by exploring the various expressions involving these operators. We will walk through how to evaluate these expressions and find out why they are true
or false
. For my first example, consider the following expression pairs introduced near the beginning of the article:
"this_is_true" == false // false "this_is_true" == true // false
Follow the abstract equality comparison algorithm to evaluate these expressions according to the following steps:
typeof "this_is_true"
returns "string", while typeof false
or typeof true
returns "boolean". Boolean
. The expression is converted to "this_is_true" == ToNumber(false)
and "this_is_true" == ToNumber(true)
. ToNumber(false)
returns 0, ToNumber(true)
returns 1, which simplifies the expressions to "this_is_true" == 0
and "this_is_true" == 1
respectively. At this time the algorithm recursively. String
and the type of the right operand is Number
. The expression is converted to ToNumber("this_is_true") == 0
and ToNumber("this_is_true") == 1
. ToNumber("this_is_true")
Returns NaN
, which simplifies the expressions to NaN == 0
and NaN == 1
respectively. At this time the algorithm recursively. NaN
, 0 and 1 are all Number
. Skip steps 1.a and 1.b that are not applicable. However, step 1.c.i applies because the left operand is NaN
. The algorithm now returns false
(NaN
is not equal to anything, including itself) as the value of each original expression and backtracks the stack to exit recursion completely. My second example (based on the explanation of the meaning of life in the "Galaxy Wandering Guide") compares an object with a number by ==
and returns true
:
"this_is_true" == false // false "this_is_true" == true // false
The following steps show how JavaScript uses the abstract equality comparison algorithm to get true
as the value of the expression:
Object
and the type of the right operand is Number
. The expression is converted to ToPrimitive(lifeAnswer) == 42
. ToPrimitive()
Call the lifeAnswer
Internal method of [[DefaultValue]]
, without prompt. According to Section 8.12.8 of the ECMAScript 262 specification, the [[DefaultValue]]
calls the toString()
method, which returns "42". The expression is converted to "42" == 42
, and the algorithm is recursive. String
and the type of the right operand is Number
. The expression is converted to ToNumber("42") == 42
. ToNumber("42")
Returns 42, and the expression is converted to 42 == 42
. The algorithm recurses and executes step 1.c.iii. Because the numbers are the same, true
is returned and expanded recursively. For my last example, let's find out why the following sequence does not show transitiveness, where the third comparison will return true
instead of false
:
"this_is_true" == false // false "this_is_true" == true // false
The following steps show how JavaScript uses the abstract equality comparison algorithm to get true
as the value of '' == 0
.
ToNumber('') == 0
, which is converted to 0 == 0
, and the algorithm recursively. (Section 9.3.1 of the specification states that StringNumericLiteral::: [empty]'s MV [mathematical value] is 0. In other words, the value of an empty string is 0.) true
(and expands recursion). The following steps show how JavaScript uses the abstract equality comparison algorithm to get true
as the value of 0 == '0'
:
0 == ToNumber('0')
, which is converted to 0 == 0
, and the algorithm recursively. true
(and expands recursion). Finally, JavaScript performs step 1.d in the abstract equality comparison algorithm to obtain true
as the value of '' == '0'
. Because the two strings have different lengths (0 and 1), return false
.
Conclusion
You may be wondering why you should bother using ==
and !=
. After all, previous examples have shown that these operators may be slower than the ===
and !==
operators due to type casting and recursion. You may want to use ==
and !=
because in some cases there are no advantages. Consider the following example: ===
"this_is_true" == false // false "this_is_true" == true // false
typeof
operator returns a String
value. Because the String
value is compared to another String
value ("object"), no type casting occurs, and ==
is as efficient as ===
. Maybe a JavaScript newbie who has never encountered ===
will find such code clearer. Similarly, the following code snippet does not require type casting (the types of both operands are Number
), so !=
is as efficient as !==
:
'' == 0 // true 0 == '0' // true '' == '0' // false
These examples show that ==
and !=
are suitable for comparisons that do not require casting. When operand types are different, ===
and !==
are the best choices because they return false
rather than unexpected values (e.g. false == ""
returns true
). If the operand type is the same, there is no reason not to use ==
and !=
. Maybe it's time to stop being afraid of evil twins, and once you understand them, they're less evil.
FAQs for JavaScript Equality and Comparison Operators (FAQs)
What is the difference between==
and ===
in JavaScript? In JavaScript, ==
and ===
are comparison operators. However, they differ in the way they compare values. The ==
operator (also known as the loose equality operator) performs type casting before comparison. This means that if you compare two different types of values, JavaScript will try to convert one type to another before performing the comparison. On the other hand, the ===
operator (called the strict equality operator) does not perform type casting. It compares values and types at the same time, which means that if the two value types are different, JavaScript will consider them to be unequal.
===
instead of ==
in JavaScript? is generally recommended to use ===
instead of ==
in JavaScript, because it provides stricter comparisons, which means it does not perform type casts and checks for values and types. This can help avoid unexpected results when comparing different types of values. For example, when using ==
, JavaScript considers the number 0 and the empty string "" equal, because it converts the type before comparison. However, using ===
, they will be considered unequal because they are of different types.
Type cast in JavaScript refers to automatically or implicitly converting values from one data type to another. This happens when operators are used for different types of operands or when some type is required. For example, when using the loose equality operator (==
), JavaScript will try to convert operands to general types before making comparisons.
In JavaScript, objects are compared by reference, not by value. This means that even if two objects have exactly the same properties and values, they are not considered equal because they refer to different objects in memory. The only case where objects are considered equal is that they refer to exactly the same object.
What is the difference between==
and !=
in JavaScript? ==
and !=
are comparison operators in JavaScript. The ==
operator checks whether the values of the two operands are equal, and performs type casts if necessary. On the other hand, the !=
operator checks whether the values of the two operands are not equal, and performs type casts if necessary.
===
and !==
in JavaScript? ===
and !==
are comparison operators in JavaScript. The ===
operator checks whether the values of the two operands are equal, taking into account both the values and types. On the other hand, the !==
operator checks whether the values of the two operands are not equal, taking into account both the values and types.
In JavaScript, arrays are objects, compared by reference, not by value. This means that even if two arrays contain the same elements in the same order, they are not considered equal because they refer to different objects in memory. To compare two arrays by their content, you need to compare each element separately.
null
and undefined
? In JavaScript, null
and undefined
are considered loosely equal (==
) because they both represent missing values. However, they are not strictly equal (===
) because they are of different types.
In JavaScript, comparison operators have the same priority level. They are calculated from left to right. However, it is important to note that they have lower priority than arithmetic and bitwise operators, but higher than logical operators.
Yes, you can use comparison operators with strings in JavaScript. JavaScript uses lexical (dictionary) order when comparing strings. However, it is important to note that capital letters are considered "small" than lowercase letters because they have smaller ASCII values.
The above is the detailed content of Don't Fear the Evil Twins - SitePoint. For more information, please follow other related articles on the PHP Chinese website!