Implicit casts take a fairly smooth learning curve from basics to applications. The specific route is:

The primitive data types give rise to the concept of wrapper types ==> valueOf and toString methods ==> ToPrimitive abstract operations and [[DefaultValue]] operations ==> Rules for type conversions between various types ==> Common situations that cause implicit type conversions ==> Multiple example exercises to verify the usefulness of the method.

1. Start with basic packaging types

A discussion of basic wrapper types starts with an understanding of basic data types (also known as primitive types, simple data types, and so on). We then introduce the concept and role of the basic wrapper type through the behavior of the basic data type calling method.

1.1 Basic data types

It has been known that JavaScript has six basic data types: string, number, Boolean, NULL, undefined, and symbol.

Base types are neither objects nor methods to call. However, it is common to see primitive types of variables calling methods, such as:

let index = 'ABCDE'.indexOf('CD');
console.log(index); / / 2
Copy the code

In the above example, ‘ABCDE’ is the basic type of a string that calls indexOf directly, stores the return value of the method in the variable index, and prints it on the second line. You can see that this example works and the results are correct.

Since primitive types have no methods to call, why doesn’t the string ‘ABCDE’ get an error when calling indexOf? And it looks like indexOf was actually called, so who called it and returned the correct result? The answer is the basic packaging type. Let’s introduce basic packaging types.

1.2 Basic packaging types

Key words: package, basic package type, basic package type object

The previous section discussed that it’s not the string itself that calls the method, but the basic wrapper type. It’s time to talk more about basic packaging types.

In order for the basic type to call the method properly, the background automatically creates an object corresponding to the value of the basic type. This object is called the basic wrapper type, and then the method is called with this object. After the method is called, the object is destroyed. This process of generating a primitive wrapper type object from a primitive data type is called wrapping.

To put it simply, when a primitive type calls a method, the real caller of the method is not the primitive type we define directly, but the object we create behind the scenes for the primitive wrapper type. Also, this object is temporary, will not last forever, will be destroyed when used up.

Another feature of this object is that it corresponds to values of primitive types. However:

Not every basic type has a corresponding wrapper type

Four of the six basic types mentioned in the previous section have corresponding packaging types:

String (string) -> string, number(value) -> number, Boolean (Boolean) -> symbol(symbol) -> symbol

The symbol -> indicates the corresponding. Also: Note the capitalization of the first letter (the default for object names is capitalization).

Note: this article is for discussion onlyString, number, BooleanThree basic types and their corresponding packaging types.

It is also important to understand that not only can the system implicitly create a wrapper object, but users can also manually and explicitly create an object of the basic wrapper type by using the keyword new and the corresponding wrapper type constructor, and passing in a value of the basic type, such as:

let n = new Number(22);             // n is the wrapper object corresponding to the value 22
let str = new String('example');    // STR is the wrapper object for the string 'example'
let flag = new Boolean(false);      // flag is the wrapper object corresponding to the Boolean value false
Copy the code

At this point, looking back at the example in section 1, you can again imagine the system creating a wrapper object in general:

let index = 'ABCDE'.indexOf('CD');
console.log(index); / / 2
Copy the code

You can see wrapper behavior in the background in the first line of code: the background wraps an object based on the string ‘ABCDE’. That is: When the indexOf method is called, a wrapper object is generated for a value of a primitive type that does not have a method to call (operation 1). The indexOf method is then called from this wrapper object (operation 2). The return value of the method is then assigned to the variable index. Finally, the wrapper object is destroyed (operation 3).

According to the above ideas, the corresponding code of the above process can be roughly simulated:

// step 1. Create an object of the basic wrapper type corresponding to 'ABCDE' :
let temp = new String('ABCDE');

// step 2. Call indexOf with the wrapper type temp and assign the return value to index:
let index = temp.indexOf('CD');

// step 3. Destroy the temp object
temp = null;
Copy the code

1.3 summarize

When a primitive datatype wants to call a method, a temporary wrapper object is generated for it, the method is called with this object, the result of the method execution is returned, and the temporary object is destroyed.

1.4 unpack the object box <-> unbox

In contrast to the process of generating a value of a primitive type, there is a process of generating a value of a primitive type from a wrapper object, called unbox. This process can be done either implicitly in the background or manually by calling the method valueOf.

The next section starts with the valueOf method and introduces another equally important method, toString.

2. Two important methods for objectsvalueOftoString

2.1 Unbox for basic packaging typesvalueOfmethods

The unwrapping of the primitive wrapper type uses the valueOf function in the wrapper object, which converts an object to a valueOf the primitive type.

For Boolean, Number, and String primitives, a call to valueOf returns the valueOf the corresponding primitives:

let n = new Number(22);     // The basic value of the package is 22
// The unwrapped result is the value of the corresponding base data type
console.log(n.valueOf() === 22);    // true

let str = new String('example');    // Wrap basic string data 'example'
// The unwrapped result is the value of the corresponding base data type
console.log(str.valueOf() === 'example');   // true, 

let flag = new Boolean(false);      // Wrap basic Boolean data false
// The unwrapped result is the value of the corresponding base data type
console.log(flag.valueOf() === false);  // true
Copy the code

Sometimes implicit unwrapping occurs in the background, where valueOf methods are called on objects of wrapper type, for example:

let a = new Number(1);
let b = a + 1;  Let b = a.valueof () + 1;

console.log(b);  / / to 2 b

console.log(typeof a);  // object
console.log(typeof b);  // number, the type of b is the basic data type, not the wrapper type
Copy the code

Even a line of code can be wrapped and unwrapped, for example:

let num = 3.14159;
console.log(num.valueOf());   / / 3.14159
Copy the code

In the second line of the above block, the variable num calls the function valueOf. In this case, num is first wrapped as an object of basic wrapper type, and this object is wrapped when the valueOf method is called.

2.2 Of other objectsvalueOfmethods

The valueOf method is not the only one for basic wrapper types. Many JavaScript build-in objects have this function, and most objects override this method to make the result of execution match the object itself. Let’s look at how the valueOf methods of some other objects behave.

The following lists the return values of the valueOf method for common built-in objects:

object The return value
Boolean, Number and String Values corresponding to each base type
Array, Function, Object In its own right
Date The number of milliseconds between the current time and midnight 1970.01.01
Math and Error There is no valueOf method

Here are the results:

// Array calls valueOf, which returns the array itself
let array = [1.'hello'.false];
console.log(array.valueOf() === array);   // true

// The function calls valueOf, which returns the function itself
function foo(){}
console.log(foo.valueOf() === foo);   // true

// The object calls valueOf, which returns the object itself
let obj = {
    name: 'doug'.age : 22
};
console.log(obj.valueOf() === obj);   // true

// The number of milliseconds between the current time and midnight, January 1, 1970
console.log(new Date().valueOf());   / / 1551684737052

Copy the code

Conclusion: The valueOf method can convert an object to a primitive data type. Not every object has this method (e.g., Math and Error objects). Call this function to return the value of the corresponding primitive type for Boolean, numeric, and string primitives. Object returns itself when it calls this function (arrays and functions return themselves, since they are also objects by nature).

The valueOf method brings to mind another important method for type conversions, toString, which is discussed in the next section.

2.3 Methods by which objects can be represented as stringstoString()

Every built-in Object has this method, which is inherited from Object. Most built-in objects override this function to make the result of execution match the object itself. A common object call to the toString method returns the following:

  • For user-created objects, return ‘[Object object]’. (There is an exception, see last section)

  • For Math objects, return “[object Math]”:

// A custom object
console.log({name: 'doug'}.toString());  // '[object Object]'

/ / Math object
console.log(Math.toString());  // '[object Math]'
Copy the code
  • For objects of the three basic wrapper types mentioned in Part 1:
  1. For Boolean objects, the string “true” or “false” is returned, depending on the value of its corresponding base data type.
  2. For numeric objects, returns the string representation of the number in the specified base, which is 10.
  3. For string objects, return the string (and call) corresponding to the base data typevalueOfThe method gives the same result.
// Boolean wrapper type object
console.log(new Boolean(false).toString());   // 'false'

// Numeric wrap the object of type object
console.log(new Number(3.14159).toString());  / / '3.14159'

// String wraps an object of type object
console.log(new String('str').toString());    // 'str'
Copy the code
  • For arrays, return a string of all items, concatenated with a comma.
/ / array
console.log([1.'hello'.false].toString());  // '1,hello,false'
Copy the code
  • For functions, the toString method returns a string containing the source text used to define the function.
/ / function
function foo(){console.log('hello foo'); }console.log(foo.toString());  
// 'function foo(){console.log('hello foo'); } '
Copy the code
  • Other objects
  1. For the RegExp object, return the string of the regular expression.
  2. For Date objects, return a string representing a specific time.
  3. For Error objects, return a string containing the contents of the Error.
// Regular objects
console.log(new RegExp("a+b+c").toString());      // "/a+b+c/"

// Date object
console.log(new Date().toString()); 
// Mon Mar 04 2019 17:07:54 GMT+0800


/ / the Error object
console.log(new Error('fatal error').toString()); 
// 'Error: fatal error'
Copy the code

Not every Object has a toString() method. For example, an Object created by passing a null argument to object.create has no toString or valueOf methods because its prototype is null

2.4 summarize

The Japanese section discusses two important functions, valueOf, which returns a valueOf the caller’s primitive type, and toString, which can turn an object into a string.

These two functions will play an important role in the casting process.

Note: There are other names for packaging and unpackaging processes, such as wrap and unwrap, which are just different words for the same process.