This is a continuation of the previous article, and it is recommended to learn implicit casting in JavaScript by following a smooth Curve.

The first two chapters discussed the relationship between primitive data types and primitive wrapper types, and two methods that are important in type conversions: valueOf and toString methods. The next section builds on the previous two chapters and presents a method for judging the results of implicit type conversions. The last section of the article provides exercises and parsing to verify the correctness of the methods discussed in this article.

3 Cast between types

The cast here refers to basic data types and conversions between objects except symbol types. Symbol types are discussed separately.

3.1 ToPrimitiveConvert variables to primitive data types

The conversion process of converting a variable to a primitive data type can be abstracted into an operation called ToPrimitive, which is an abstract name rather than a concrete method, and the implementation of which is concrete by the various data types.

ToPrimitive converts a variable to a basic type, taking different actions depending on the type of the variable:

  1. If the variable is already of a primitive type: the conversion is not performed and the variable is returned, using its value directly.

  2. When the variable is an object: the object’s internal method [[DefaultValue]] is called to convert the object to its primitive type.

In simple terms, return itself directly for primitive data types. For the object, the [[DefaultValue]] method of the object itself is executed to get the result. How does the [[DefaultValue]] method work?

3.2 [[DefaultValue]]The operation returns the value of the object’s basic data type (primitive type)

The [[DefaultValue]] method returns the basic data type of the operand using the valueOf method or toString method inside the object (you can specify desired type preferences). The process of this operation can be understood as follows:

By default:

The valueOf() method is called first, which is used if the return value is a primitive type; Otherwise: call toString() and get the return value. If neither method returns a value for the base data type, a TypeError exception is thrown.

Also: For Date objects, the order in which the two methods are called is reversed. Call toString first, and valueOf. If you can’t get a valueOf the primitive type. If neither can get a value of the primitive type, TypeError is also raised.

To summarize part 3.1: When converting a value to a primitive data type, use itself if the value is a primitive data type. If the value is an object, two of the object’s methods, valueOf and toString, are called, which return the valueOf the primitive type to which the object was converted.

3.2 Type conversion between basic data types

The previous section discussed how objects can be cast to basic data types, and the studio section focuses on transformations between basic data types. It mainly consists of three parts:

  1. Other types of data are converted to strings
  2. Other types are converted to numeric values
  3. Other types are converted to Booleans

Let’s discuss them one by one.

3.2.1 Convert Other Types of Data to Strings

Other basic data types can be converted to strings as easily as possible. Such as:

Null -> "null", undefined -> undefined, true -> "true", false -> "false", 3.14159 -> "3.14159"

Note: For very large or very small numbers, the conversion to a string is in the form of scientific notation, for example:

3140000000000000000000 -> "3.14 e+21" // Large numbers are converted to strings

0.000000314 -> "3.14 e-7"  // Small numbers are converted to strings
Copy the code

3.2.2 Other types of data are converted to values

Other basic data types are relatively easy to convert to numeric types, with only strings requiring very simple judgment. Specific as follows:

null -> 0, undefined -> NaN, true -> 1, false -> 0

For strings, this can be broken down into the following:

  • The empty string is converted to 0
  • If the string contains only numbers, plus and minus signs, and decimal points, the conversion is direct and the leading 0 is ignored. For example:
"3.14"- > 3.14"0003.14"-> -3.14 // Leads with 0Copy the code
  • If the content of the string is hexadecimal, the decimal value is converted, for example, “0xa -> 10”.

  • In other cases, the result is NaN, for example:

"A10" -> NaN
"1A0" -> NaN
"10A" -> NaN
Copy the code

3.2.3 Converting other types of data to Boolean values

This is easier. Only a few special values are converted to false. All other values are converted to true: NaN, undefined, null, 0, +0, -0, empty string “”

The contents of section 3.2 above are easy to remember and need to be used flexibly when dealing with specific type conversions, but sometimes the same variable in the same problem involves multiple conversions, such as from an object to a string and then from a string to a value.

The next section will discuss practical applications involving implicit type conversions, including many examples.

4. Cases involving implicit type conversions

Many common operations in JavaScript cause implicit casts, and the following sections give a few common examples.

4.1 Implicit type conversion + – * / caused by addition, subtraction, multiplication and division

The four commonly used operators sometimes cause implicitly cast conversions

4.4.1 plus +

In JavaScript, the plus sign + can be used for addition or concatenation of strings, so how do you determine which operation it does?

Some people say that a concatenation is performed as long as one of the two variables in the plus concatenation is a string, otherwise an addition is performed. This statement is incomplete. For example, the following examples cannot be used because the variables on either side of the plus sign are not strings:

/ / 1
console.log(true + true); // ?
console.log(1 + null); // ?


/ / 2
let array = [2.3];
let a = 1 + array;

let obj = { name: 'doug'.age: 4 };
let b = 1 + obj;

console.log(a, b); // ?
Copy the code

So how do you know? I think we can do this in two simple cases:

  1. If the left and right sides of the plus sign are values of primitive types other than strings, or can be converted to objects of these types through the ToPrimitive(see Section 3.1) abstraction, then the background attempts to convert both variables to numbers for addition (see Section 3.2.2 for details).

Take a look at the results:

console.log( 1 + 1 );       / / 2

// true -> 1; false -> 0
console.log( 1 + true );    / / 2
console.log( 1 + false );   / / 1
console.log( false + 1 );   / / 1
console.log( false + true );    / / 1

// null -> 0
console.log( 1 + null );    / / 1
console.log( true + null ); / / 1

// undefined -> NaN
console.log( 1 + undefined );   // NaN
console.log( true + undefined );    // NaN
console.log( null + undefined );    // NaN


// Use ToPrimitive to return number, Boolean, null, undefined object of primitive type
// Overrides the valueOf and toString methods of the object
let obj = {
    valueOf: function(){
        return true;
    },

    toString: function(){
        return 'call obj'; }}console.log(1 + obj);   / / 2
console.log(obj + obj); // this is a more typical example
Copy the code

As mentioned earlier, by default ToPrimitive first calls the valueOf method of obj to get the valueOf the primitive type, so it gets true. Log (1+ true) and console.log(true + true), after which true is converted to numeric type 1 and console.log(1+1). The experimental results confirm the hypothesis and apply to the case of example 1.

So when do you concatenate strings? The idea is as follows:

  1. If there is a string to the left and right of the plus sign or an object that can be converted to a string through the ToPrimitive (see Section 3.1) abstract operation, then concatenation is performed.

In example 2:

let array = [2.3];
let a = 1 + array;

let obj = { name: 'doug'.age: 4 };
let b = 1 + obj;

console.log(a, b); // ?
Copy the code

ToPrimitive = 1; ToPrimitive = 1; ToPrimitive = 1; ToPrimitive = 1; The toString method is then called, which returns an entry using “,” concatenated strings “2,3”. So now we have let a = 1 + “2,3”, which is the sum of the number and the string, and the result is “12,3”.

The object obj calls valueOf to return itself and toString to return the string “[object object]”. Let b = 1 + “[object object]” let b = 1 + “[object object]”

Here’s another example:

function fn(){ console.log('running fn'); }

console.log(fn + fn);
/* function fn(){ console.log('running fn'); }function fn(){ console.log('running fn'); } * /
Copy the code

The result of this example is relatively easy to get with the assumption. Fn returns a string via ToPrimitive, which then becomes string + string, resulting in string concatenation.

So far. The above judgment may exist not rigorous place, welcome criticism and correction.

4.1.2 Cast from the subtraction, multiplication, and division – * / operator

These three operators cast the left and right variables that are not numeric types to simple numeric types and then perform mathematical operations, such as:

console.log(true - false);	/ / 1
console.log(true - null);	/ / 1
console.log(true * true);	/ / 1
console.log(2 - undefined);	// NaN

console.log([2] - 1);	/ / 1
// [2] -valueOf-> [2] -toString-> "2" -> 2

console.log('3' * 2);	/ / 6

console.log('4' / '2');	/ / 2

let obj = {
	toString: function(){ // Overrides the toString method to return a string
		return '4'; }};console.log(obj * [2]);	/ / 8
Copy the code

In each of these cases, the variable was eventually converted to a numeric primitive data type. Arrays and objects are converted to strings via ToPrimitive (see Section 3.1), then cast to numeric types for mathematical operations.

4.2 Logical operators||&&

We often use logical operators in conditional judgments, for example:

if(a || b){
    // codes
}
Copy the code

It’s a natural thing to do.

However, the logical operator does not return true or false as expected, but one of its left and right operands. Such as:

let a = 50;
let b = 100;

console.log(a || b, a && b); // 50, 100, does not print true or false
Copy the code

You can see that the output is not a Boolean, but one of the two operands. At the same time, also found that for the same two operands, | | and the output of the && operator is not the same. Here’s why.

The two operators decide which operand to return based on the value of the left operand after it has been converted to a Boolean type, checking the truth value of the left operand before making the decision. The specific mechanism is as follows:

  1. for||, when the truth value on the left is true, the left is returned; Otherwise return the right-hand operand.
  2. for&&When the truth value on the left is false, the left is returned directly; Otherwise return the right-hand operand.

Can be so simple to understand: | | meaning or, as long as there is a true two clock is ok, so if the left is true for the whole truth, directly back to the left. && meaning and, for both sides is true, if the left is true, then it depends on the right of the true and false, so directly back to the right.

Back to the example at the beginning:

if(a || b){
    // codes
}
Copy the code

According to the above discussion, can know a | | b does not return booleans, however if it is based on Boolean value to decide whether to perform the internal operation, then why can normal operation? Because of if statements to a | | b return values for an implicit casts, converted to Boolean values, and then on to the next step.

Examples of implicit casting judgments include:

  • The for loop
  • While and do while loops
  • The ternary operator xx? a:b

4.3 Non-strict equality symbol= =

4.3.1 Symbol for strict equality= = =The similarities and differences

The non-strict equality symbol (==) is a related concept to the strict equality symbol (===). The difference is that == allows casting and comparing the left and right operands; === does no casting and compares the left and right operands directly.

When the left and right operands are of the same type, the two comparison symbols have the same effect and apply the same principle.

When comparing objects, the two comparison symbols work the same way: they compare whether the left and right variables refer to the same object.

4.3.2 Between objects (including arrays and functions) and basic data types= =To compare

When an object is compared to a primitive data type, the object returns the value of the primitive data type through the ToPrimitive (see Section 3.1) operation and then makes the comparison.

4.3.3 Booleans and other types= =To compare

Boolean values are first converted to numeric values when compared to other types: true->1, false->0.

4.3.4 String and numeric= =To compare

The string is converted to a numeric type and then compared

4.3.4 nullundefinedThe comparison of

Null and undefined return true when compared with ==, and only return true when compared with each other other than themselves.

In other words, except for itself, null is true only when compared to undefined with == and false when compared to any other value; Similarly, undefined is true only when compared to null with == and false when compared to any other value except itself.

For null and undefined, only three cases are true:

console.log( null == undefined ); 	// true
console.log( undefined == undefined ); 	// true
console.log( null == null ); 	// true
Copy the code

Other Special circumstances

  1. NaN is not equal to any value, even compared to itself
console.log(NaN == NaN); 	// false
Copy the code
  1. Plus 0, minus 0, 0 is the same thing
console.log(0 == +0);	// true
console.log(0 == -0);	// true
console.log(-0 == +0);	// true
Copy the code

To summarize: when objects (including arrays and functions) are compared to other types, it is the objects that are converted; When a Boolean value is compared to other data, it is a Boolean value that is converted to a numeric value of type 1 or 0. When comparing a value to a string, the type to convert is a string, to a numeric type.

5 application and analysis with examples

Here are some examples of implicit casts, using the previous discussion and parsing:

console.log( "4"= =4 );		// true
// The string is converted to a number, i.e. "4" -> 4

console.log( "4a"= =4 );		// false
/* A string is converted to a number, but contains "a", so it is NaN ("4a") -> NaN; The formula becomes' NaN == 4 ', which is false */ since NaN is not equal to any value

console.log( "5"= =4 );		// false
// The string is converted to a value, i.e. "5" -> 4, and the formula becomes' 5 == 4 ', false

console.log( "100"= =true );		// false
/* There is a Boolean value, first Boolean value is converted to a value, that is: true -> 1, the formula becomes: '"100" == 1', then the string is converted to a value, the formula becomes' 100 == 1 ', false */

console.log( null= =undefined );		// true
console.log( undefined= =undefined );		// true
console.log( null= =null );		// true
console.log( null == [] );		// false
console.log( null= ="" );		// false
console.log( null == {} );		// false
console.log( null= =0 );		// false
console.log( null= =false);		// false
console.log( undefined == [] );		// false
console.log( undefined= ="" );		// false
console.log( undefined == {} );		// false
console.log( undefined= =0 );		// false
console.log( undefined= =false );		// false
console.log(null= =Object(null));// false
console.log(undefined= =Object(undefined));// false
// The above answer is relatively easy to get because null and undefined know each other except themselves, section 4.3.4


console.log( "0"= =false );		// true
/* contains a Boolean value, which is converted to a value, i.e., false -> 0, and then becomes' '0" == 0'; The string is converted to a numeric value, i.e., "0" -> 0, and then the formula becomes' 0 == 0 ', true */

console.log( 0= =false);		// true
// false converts to 0, true

console.log( false= ="" );		// true
/* false -> 0; /* false -> 0; The string is converted to a numeric value, that is: "" -> 0, the formula becomes' 0 == 0 ', true */

console.log( false == [] );		// true
// contains objects and boilers. Boolean values are converted first, and then objects are converted ToPrimitive data types by ToPrimitive:
// false -> 0, [] -> "" -> 0; ` 0 == 0 `, true

console.log( false == {} );		// false
// contains objects and boilers. Boolean values are converted first, and then objects are converted ToPrimitive data types by ToPrimitive:
// false -> 0, {} -> "Object Object" -> NaN; ` 0 == NaN ` false

console.log( 0= ="");		// true
/ / "" - > 0; ` 0 == 0 ` true

console.log( "" == [] );		// true
// compare strings and objects, which are converted ToPrimitive data types by the ToPrimitive operation:
// [] -toString- -> ""; The formula becomes' "" == "", true

console.log( 0= = []);// true
// compare values and objects, which are converted ToPrimitive data types by the ToPrimitive operation:
// [] -toString- -> ""; The formula becomes' 0 == "", where the numeric type is compared to the string, and the string is converted to a numeric value
// "" -> 0; The formula becomes' 0 == 0 ',true

console.log( 0= = {});// false
// compare values and objects, which are converted ToPrimitive data types by the ToPrimitive operation:
// {} -> "Object Object" -> NaN; ` 0 == NaN ` false
Copy the code

There is more to casting designed in JavaScript than that mentioned in this article, but there are other things that are not covered that will be discussed in the future. At the same time, there may be errors in the article, please correct, thank you.

References:

  • JavaScript Advanced Programming
  • JavaScript You Don’t Know
  • MDN