As we all know, == in JavaScript is a relatively complex operation. Its operation rules are very strange and can easily make mistakes, making it one of the "worst features" in JavaScript.
Based on carefully reading the ECMAScript specification, I drew a picture. I thought that after you understand this picture, you will thoroughly understand everything about the == operation. At the same time, I tried to prove to everyone through this article that == is not that bad thing. It is easy to master and even looks reasonable and not that bad.
First, the picture:
==The exact description of the operation rules is here: The Abstract Equality Comparison Algorithm. However, with such a complicated description, are you sure you won’t feel dizzy after reading it? Can you use it to guide your practice immediately?
It certainly doesn't work. After all, the specification is for developers of JavaScript running environment (compared with developers of V8 engines), not for users of languages. The above picture translates the specification into a form that is convenient for everyone to see.
Before introducing each part in Figure 1 in detail, let’s review the knowledge about types in JS:
There are two types of values in JS: basic type and object type.
The basic types include: Undefined, Null, Boolean, Number and String.
Both the Undefined type and the Null type have only one value, namely undefined and null; the Boolean type has two values: true and false; there are many values of the Number type; and the String type has countless values (theoretically).
All objects have valueOf() and toString() methods, which are inherited from Object and may of course be rewritten by subclasses.
Now consider the expression:
x == y
where x and y are values of one of the six types.
When the types of x and y are the same, x == y can be converted into x === y, and the latter is very simple (the only thing to note is NaN), so below we only consider the cases where the types of x and y are different.
1. Have and None
In Figure 1, six types of JavaScript values are represented by rectangles with blue background. First they are divided into two groups:
String, Number, Boolean and Object (corresponding to the large rectangular box on the left)
Undefined and Null (corresponding to the rectangular box on the right)
What is the basis for grouping? Let's take a look. Undefined and Null on the right are used to indicate uncertainty, non- or empty, while the four types on the right are all determined, existing and non-empty. We can say this:
On the left is a world of existence, and on the right is an empty world.
Therefore, it is reasonable to compare any value in the two worlds with false. (i.e., the horizontal line connecting two rectangles in Figure 1 is marked false)
2. Empty and Empty
Undefined and null in JavaScript are another place that often crashes us. It is usually considered a design flaw, which we will not dig into. But I have heard that the author of JavaScript initially thought this:
If you plan to assign a variable to the value of the object type, but have not assigned a value yet, then you can use null to represent the state at this time (one of the evidence is that the result of typeof null is 'object'); on the contrary, if you plan to assign a variable to the value of the original type, but have not assigned a value yet, then you can use undefined to represent the state at this time.
Regardless of whether this rumor is credible or not, it is reasonable that the result of the comparison between the two is true. (i.e. true marked on the vertical line on the right in Figure 1)
Before moving on to the next step, let’s talk about the two symbols in Figure 1: capital letters N and P. These two symbols do not mean positive and negative in the PN section. Instead:
N represents the ToNumber operation, which means converts the operand into a number. It is an abstract operation in the ES specification, but we can use the Number() function in JS to replace it equivalently.
P represents the ToPrimitive operation, that is, converting the operand to the value of the original type. It is also an abstract operation in the ES specification, and it can also be translated into equivalent JS code. But it is a little more complicated. To put it simply, for an object obj:
ToPrimitive(obj) is equivalent to: first calculate obj.valueOf(), if the result is the original value, this result is returned; otherwise, obj.toString() is calculated, and if the result is the original value, this result is returned; otherwise, an exception is thrown.
Note: There is an exception here, that is, an object of type Date, which will first call the toString() method.
In Figure 1, a line marked N or P indicates that when the two types of data it is connected to perform the == operation, the operand on the side marked N or P must first perform the ToNumber or ToPrimitive transformation.
3. True and False
As can be seen from Figure 1, when a Boolean value is compared with other types of values, the Boolean value is converted into a number. Specifically
true -> 1
false -> 0
This doesn't require too much verbal abuse at all. Think about it, in C, there is no Boolean type at all. The integers 1 and 0 are usually used to represent the true or false logic.
IV. Sequence of characters
In Figure 1, we divide String and Number into a group. Why? Among the six types, String and Number are sequences of characters (at least literally). A string is a sequence of all legal characters, while a number can be regarded as a sequence of characters that meet certain conditions. Therefore, numbers can be regarded as a subset of strings.
According to Figure 1, when performing the == operation of strings and numbers, you need to use the ToNumber operation to convert the string into numbers. Assuming x is a string and y is a number, then:
x == y -> Number(x) == y
So what is the rule for converting strings into numbers? The specification is described in a very complicated way, but generally speaking, it is to remove the quotes on both sides of the string and see if it can form a legal number. If so, the conversion result is this number; otherwise, the result is NaN. For example:
Number('123') // Result 123
Number('1.2e3') // Result 1200
Number('123abc') // Result NaN
Of course, there are exceptions, such as the result of converting an empty string into a number is 0. Right now
Number('') // Result 0
V. Simple and complex
Primitive types are simple types, they are straightforward and easy to understand. However, the disadvantage is that the expression ability is limited and difficult to expand, so there are objects. An object is a collection of attributes, and the attribute itself can be an object. Therefore objects can be constructed arbitrarily complex enough to represent various things.
But sometimes things are complicated and not a good thing. For example, not everyone has the time, patience, or the need to read it from beginning to end. Usually it is enough to only understand its central thoughts. So the paper has keywords and overviews. The same is true for objects in JavaScript. We need to have a means to understand its main characteristics, so the objects have toString() and valueOf() methods.
The toString() method is used to obtain a text description of the object; and the valueOf() method is used to obtain the eigenvalue of the object.
Of course, this is just my own understanding. Also, as the name implies, the toString() method tends to return a string. What about the valueOf() method? According to the description in the specification, it tends to return a number - although in the built-in type, the valueOf() method returns only Number and Date.
According to Figure 1, when an object is compared with a non-object, the object needs to be converted into a primitive type (although when comparing with a Boolean type, the Boolean type needs to be converted into a numeric type first, but the object type needs to be converted into a primitive type next). This is also reasonable. After all, == is not strictly equal comparison. We only need to take out the main features of the object to participate in the operation, and put the secondary features aside.
Six. Everything is counted
Let's look back at Figure 1. The lines marked N or P inside have no direction. If we mark the arrows on these lines, the connection points from the end marked N or P to the other end, then we will get (not considering undefined and null):
Have you discovered anything? Yes, during the calculation process, all types of values have a tendency to convert to numeric types. After all, a celebrity once said:
Everything is counted.
7. Just give me a chestnut
There is too much nonsense in the past, so here is an example to prove that Figure 1 is indeed convenient and effective to guide practice.
Example, calculate the following:
[''] == false
First, the two operands are object type and boolean type respectively. According to Figure 1, it is necessary to convert the Boolean type to a numeric type, and the result of converting false to a numeric is 0, so the expression becomes:
[''] == 0
The two operands become object type and numeric type. According to Figure 1, the object type needs to be converted to the original type:
First, call [].valueOf(). Since the valueOf() method of the array returns itself, the result is not the original type. Continue to call [].toString().
For arrays, the algorithm of the toString() method is to convert each element into a string type and then concatenate it in turn with ',', so the final result is an empty string, which is a value of the original type.
At this point, the expression becomes:
'' == 0
The two operands become string types and numeric types. According to Figure 1, the string type needs to be converted to numeric types. As mentioned earlier, the empty string becomes a number of 0. So the expression becomes:
0 == 0
So far, the types of the two operands are finally the same, and the result is obviously true.
From this example, we can see that in order to master the rules of == operation, in addition to remembering Figure 1, we also need to remember the rules of the toString() and valueOf() methods of those built-in objects. Including Object, Array, Date, Number, String, Boolean, etc.
8. Let's summarize
The previous statement is very confusing. Here I will summarize the rules of == operation expressed in Figure 1:
The result of undefined == null is true. The result of their comparison with all other values is false.
When a string == number, the string is converted to a number.
Boolean value == When other types are used, the Boolean value is converted to a number.
When an object == numeric/string, the object is converted to a primitive type.
Finally, I changed the picture to be for entertainment only: )
OK, it's over. If you think this article is useful to you, please like it so that more people can see it.
In addition, please point out the fallacies in the article.