Home >Web Front-end >JS Tutorial >Don't Fear the Evil Twins - SitePoint

Don't Fear the Evil Twins - SitePoint

Jennifer Aniston
Jennifer AnistonOriginal
2025-02-22 08:58:11896browse

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 !=

JavaScript language contains two sets of equality 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:

Because there are only two possible boolean values, you might think that one of the expressions should be calculated as
<code class="language-javascript">"this_is_true" == false // false
"this_is_true" == true  // false</code>
. However, they are all calculated as

. 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

<code class="language-javascript">"this_is_true" == false // false
"this_is_true" == true  // false</code>

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.

How does

== and != work?

The best way to learn how

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

<code class="language-javascript">'' == 0   // true
0 == '0' // true
'' == '0' // false</code>
According to this generation, the equal expression is either a relational expression, or an equal expression that is equal to a relational expression, or an equal expression that is not equal to a relational expression, etc. . (I overlooked

and ==, which are not related to this article.) Section 11.9.1 provides the following information on how != works: === !== ==The production formula

is calculated as follows:

EqualityExpression : EqualityExpression == RelationalExpressionLet

be the result of calculating
    .
  1. lrefLet EqualityExpression be
  2. .
  3. lvalLet GetValue(lref) be the result of calculating
  4. .
  5. rrefLet RelationalExpression be
  6. .
  7. rvalReturns the result of performing an abstract equality comparisonGetValue(rref). (See 11.9.3.)
  8. rval == lval
  9. Section 11.9.2 provides similar information on how
works:

!=The production formula

is calculated as follows:
  1. Let lref be the result of calculating EqualityExpression.
  2. Let lval be GetValue(lref).
  3. Let rref be the result of calculating RelationalExpression.
  4. Let rval be GetValue(rref).
  5. Let r be the result of performing abstract equality comparisonrval != lval. (See 11.9.3.)
  6. If r is true, return false. Otherwise, return true.

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:

Comparex == y, where x and y are values, resulting in true or false. This comparison is carried out as follows:

  1. If Type(x) is the same as Type(y), then
    1. If Type(x) is Undefined, return true.
    2. If Type(x) is Null, return true.
    3. If Type(x) is Number, then
      1. If x is NaN, return false.
      2. If y is NaN, return false.
      3. If x and y are the same numerical value, return true.
      4. If x is 0 and y is -0, return true.
      5. If x is -0 and y is 0, then true will be returned.
      6. Return false.
    4. If Type(x) is String, if x and y are exactly the same character sequence (the same length and the same characters in the corresponding position), then true is returned. Otherwise, return false.
    5. If Type(x) is Boolean, then if x and y are both true or are both false, then return true. Otherwise, return false.
    6. If x and y refer to the same object, return true. Otherwise, return false.
  2. If x is null and y is undefined, then return true.
  3. If x is undefined and y is null, then return true.
  4. If Type(x) is Number and Type(y) is String, the result of comparison x == ToNumber(y) is returned.
  5. If Type(x) is String and Type(y) is Number, the result of comparison ToNumber(x) == y is returned.
  6. If Type(x) is Boolean, the result of comparison ToNumber(x) == y is returned.
  7. If Type(y) is Boolean, the result of comparison x == ToNumber(y) is returned.
  8. If Type(x) is String or Number and Type(y) is Object, the result of comparison x == ToPrimitive(y) is returned.
  9. If Type(x) is Object and Type(y) is String or Number, the result of comparison ToPrimitive(x) == y is returned.
  10. 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, return NaN.
  • If the parameter is Null, return 0.
  • If the parameter is a Boolean value true, return 1. If the parameter is a Boolean value false, 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, return NaN. For example, the parameter "xyz" causes the return NaN. Furthermore, parameter "29" results in a return of 29.
  • If the type of parameter is Object, apply the following steps:
    1. Let primValue be ToPrimitive(输入参数, 提示Number).
    2. Return ToNumber(primValue).
The

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:

  1. If the input parameter is Undefined, the input parameter (Undefined) is returned - no conversion.
  2. If the input parameter is Null, the input parameter (Null) is returned - no conversion.
  3. If the type of input parameter is Boolean, return the input parameter - no conversion.
  4. If the type of input parameter is Number, return the input parameter - no conversion.
  5. If the type of input parameter is String, return the input parameter - no conversion.
  6. 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 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:

<code class="language-javascript">"this_is_true" == false // false
"this_is_true" == true  // false</code>

Follow the abstract equality comparison algorithm to evaluate these expressions according to the following steps:

  1. Skip step 1 because the types are different: typeof "this_is_true" returns "string", while typeof false or typeof true returns "boolean".
  2. 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).
  3. 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.
  4. 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 is Number. The expression is converted to ToNumber("this_is_true") == 0 and ToNumber("this_is_true") == 1.
  5. ToNumber("this_is_true") Returns NaN, which simplifies the expressions to NaN == 0 and NaN == 1 respectively. At this time the algorithm recursively.
  6. Go to step 1, because the types of 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:

<code class="language-javascript">"this_is_true" == false // false
"this_is_true" == true  // false</code>

The following steps show how JavaScript uses the abstract equality comparison algorithm to get true as the value of the expression:

  1. 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 is Number. The expression is converted to ToPrimitive(lifeAnswer) == 42.
  2. ToPrimitive()Call the lifeAnswerInternal 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.
  3. 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 is Number. The expression is converted to ToNumber("42") == 42.
  4. 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:

<code class="language-javascript">"this_is_true" == false // false
"this_is_true" == true  // false</code>

The following steps show how JavaScript uses the abstract equality comparison algorithm to get true as the value of '' == 0.

  1. Execute step 5, resulting in 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.)
  2. 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':

  1. Execute step 4, resulting in 0 == ToNumber('0'), which is converted to 0 == 0, and the algorithm recursively.
  2. 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: ===

<code class="language-javascript">"this_is_true" == false // false
"this_is_true" == true  // false</code>
The

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 !==:

<code class="language-javascript">'' == 0   // true
0 == '0' // true
'' == '0' // false</code>

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.

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, 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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:Foundation 5Next article:Foundation 5