Don't Fear the Evil Twins - SitePoint
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
-
Understanding the basics: and
==
operators in JavaScript are not inherently evil; they perform type casts when comparing different types of values, which is both useful and tricky.!=
- Learn when to use which one: Use and
===
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.!=
- Learn the cast rules: Familiar with how JavaScript casts types during and
==
comparisons, so as to predict results more accurately and avoid common pitfalls.!=
- Explore practical examples: Dig into the examples to see how and
==
run in various scenarios, such as comparing strings to numbers or objects to original values, to consolidate understanding .!=
- Don't be afraid, but be cautious: Although and
==
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:
- If the parameter is
Undefined
, returnNaN
. - If the parameter is
Null
, return 0. - If the parameter is a Boolean value
true
, return 1. If the parameter is a Boolean valuefalse
, return 0. - If the parameter type is
Number
, the input parameter is returned - no conversion. - If the type of the parameter is
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, returnNaN
. For example, the parameter "xyz" causes the returnNaN
. Furthermore, parameter "29" results in a return of 29. - If the type of parameter is
Object
, apply the following steps:- Let
primValue
beToPrimitive(输入参数, 提示Number)
. - Return
ToNumber(primValue)
.
- Let
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:
- If the input parameter is
Undefined
, the input parameter (Undefined
) is returned - no conversion. - If the input parameter is
Null
, the input parameter (Null
) is returned - no conversion. - If the type of input parameter is
Boolean
, return the input parameter - no conversion. - If the type of input parameter is
Number
, return the input parameter - no conversion. - If the type of input parameter is
String
, return the input parameter - no conversion. - If the type of the input parameter is
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 optionalPreferredType
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:
- Skip step 1 because the types are different:
typeof "this_is_true"
returns "string", whiletypeof false
ortypeof true
returns "boolean". - Skip steps 2 to 6 that are not applicable because they do not match the operand type. However, step 7 applies because the right parameter has a type
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.- Skip steps 1 to 4 that are not applicable because they do not match the operand type. However, step 5 applies because the type of the left operand is
String
and the type of the right operand isNumber
. The expression is converted toToNumber("this_is_true") == 0
andToNumber("this_is_true") == 1
. ToNumber("this_is_true")
ReturnsNaN
, which simplifies the expressions toNaN == 0
andNaN == 1
respectively. At this time the algorithm recursively.- Go to step 1, because the types of
NaN
, 0 and 1 are allNumber
. Skip steps 1.a and 1.b that are not applicable. However, step 1.c.i applies because the left operand isNaN
. The algorithm now returnsfalse
(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:
- Skip steps 1 to 8 that are not applicable because they do not match the operand type. However, step 9 applies because the type of the left operand is
Object
and the type of the right operand isNumber
. The expression is converted toToPrimitive(lifeAnswer) == 42
. ToPrimitive()
Call thelifeAnswer
Internal method of[[DefaultValue]]
, without prompt. According to Section 8.12.8 of the ECMAScript 262 specification, the[[DefaultValue]]
calls thetoString()
method, which returns "42". The expression is converted to"42" == 42
, and the algorithm is recursive.- Skip steps 1 to 4 that are not applicable because they do not match the operand type. However, step 5 applies because the type of the left operand is
String
and the type of the right operand isNumber
. The expression is converted toToNumber("42") == 42
. ToNumber("42")
Returns 42, and the expression is converted to42 == 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
.
- Execute step 5, resulting in
ToNumber('') == 0
, which is converted to0 == 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.) - Execute step 1.c.iii, which compares 0 with 0 and returns
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'
:
- Execute step 4, resulting in
0 == ToNumber('0')
, which is converted to0 == 0
, and the algorithm recursively. - Execute step 1.c.iii, which compares 0 with 0 and returns
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.
Why should I use ===
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.
What is type cast in JavaScript?
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.
How does JavaScript handle object comparison?
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.
How to compare two arrays in JavaScript?
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.
How does JavaScript handle the comparison between 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.
What is the priority order of comparison operators in JavaScript?
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.
Can I use comparison operators with strings in JavaScript?
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!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Frequently Asked Questions and Solutions for Front-end Thermal Paper Ticket Printing In Front-end Development, Ticket Printing is a common requirement. However, many developers are implementing...

There is no absolute salary for Python and JavaScript developers, depending on skills and industry needs. 1. Python may be paid more in data science and machine learning. 2. JavaScript has great demand in front-end and full-stack development, and its salary is also considerable. 3. Influencing factors include experience, geographical location, company size and specific skills.

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.

How to merge array elements with the same ID into one object in JavaScript? When processing data, we often encounter the need to have the same ID...

Learning JavaScript is not difficult, but it is challenging. 1) Understand basic concepts such as variables, data types, functions, etc. 2) Master asynchronous programming and implement it through event loops. 3) Use DOM operations and Promise to handle asynchronous requests. 4) Avoid common mistakes and use debugging techniques. 5) Optimize performance and follow best practices.

Discussion on the realization of parallax scrolling and element animation effects in this article will explore how to achieve similar to Shiseido official website (https://www.shiseido.co.jp/sb/wonderland/)...

In-depth discussion of the root causes of the difference in console.log output. This article will analyze the differences in the output results of console.log function in a piece of code and explain the reasons behind it. �...

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.
