This paper analyzes the classification of JS types, the transformation of JS types of a variety of scenarios are analyzed in detail, explained ToNumber, ToString, ToBoolean, ToPrimitive mechanism and call law; The principle of implicit type conversion is analyzed.

I. Classification of data types

Primitive data type

Number string Boolean undefined null, symbol(ES6) bigint(ES10)

Reference data type

There is only one reference type, object. This type has some fixed special implementations, which inherit from object’s method of adding its own features and become a new ‘subtype’. Array Date RegExp Function Basic package type: Boolean String Number Single built-in object: Global object Math Key Set type: Map WeakMap Set WeakSet Reflection type: Proxy Reflect controls abstract types: Iteration interfaces Promise types

Through the Object. The prototype. ToString. Call () can distinguish between the different types.

Type conversion

Js itself is a weak type: types can be converted to and from one another

let a = 1 + "2"; / / "12"Copy the code

Type conversion includes: explicit type conversion and implicit type conversion Display type conversion:

let a = 1; let b = String(a); / / "1"Copy the code

Implicit type conversion

let a = 1; let b = a + ""; / / "1"Copy the code

Explicit and implicit are opposites, and knowing what it’s going to convert, it’s explicit to you.

Several type conversions as cornerstones

The ToNumber, ToString, and ToBoolean methods are implemented in the specification and cannot be called directly. However, languages also expose interfaces that allow us to call interfaces implemented internally in those languages. All conversion operations are essentially conversions of these types.

1.ToNumber

As the name suggests, this is how you convert a type value to a numeric type. Some examples of its conversions are as follows:

Value Number
null 0
undefined NaN
true 1
false 0
“” 0
“1” 1
“abc” NaN
10n 10
{} NaN
“Infinity” Infinity
[1] 1
[” 1 “] 1
[1, 2] NaN
/ 123 / NaN

The ToNumber operation must return a numeric value. There are several possible values:

  • The integer
  • Floating point Numbers
  • NaN
  • Infinity/-Infinity

Here’s how ToNumber works:

If the value is numeric, it stays the same if it’s null, it’s 0; NaN if undefined, NaN if Boolean, true, 1 if true, false, 0 if BigInt, converted to Number if string, there are several possible results:

0 if the value is an empty string, or a string containing multiple Spaces, such as “” => 0,” “=> 0 If the value is an integer or a string of floating point numbers, the value is converted to the corresponding value, such as: “1.2” => 1.2 If the value is “Infinity” or “-infinity”, convert to the corresponding Infinity or -infinity if the value is any other, NaN If the value is an object, ToPrimitive is performed, and the returned value is converted to Number

Common ways to trigger ToNumber are:

let a = "1"; let b = Number(a); // 1 let c = +a; / / 1Copy the code

2. ToString

The ToString operation returns a value as a string. Here are the rules:

Value String
1 “1”
Infinity “Infinity”
NaN “NaN”
null “null”
undefined “undefined”
true “true”
false “false”
1n “1”
[1, 2, null, undefined] “1, 2,,”
{} “[object Object]”
/ 123 / 123 “/ /”

The return value of the ToString operation must be a string, and the rule is straightforward: 1. For basic types (null, undefined, Number, Boolean, BigInt, String) and regular expressions, the String form 2 is returned. For an object, we call its toString method, get its return value, and convert it toString, but this is a case in point:

  • For normal objects, there is a toString method on the prototype of the Object (object.prototype) that returns “[Object Object]”.
  • For objects that do not inherit Object stereotypes, such as objects created using the object.create (null) method, without the toString method, a syntax error is reported
  • For Array, the toString method is overridden to return the toString values of its items, concatenated with commas (,). Note that null and undefined are converted to empty strings.
  • For the Date type, a string in the format of Tue Nov 17 2020 21:58:53 GMT+0800 is returned
  • For function types, the string form of the function definition is returned

ToString is triggered by:

let a = 1; let b = String(a); // "1" let c = a + ""; / / "1"Copy the code

3. ToBoolean

The ToBoolean operation that returns the Boolean form of a value. A value that returns true is called truthy; Return the value of false, called falsy. In fact, truthy lists are infinitely long, whereas falsy values are enumerable. We just need to remember the Falsy value.

Value Boolean
0 false
0 false
0n false
“” “‘ / ` ` false
NaN false
null false
undefined false
false false

The rules are as follows: 1. Objects (including all their subtypes) are Truthy 2. In numeric types, 0, -0, and NaN are falsy 3. Empty strings are falsy, but empty strings with Spaces are not 4. The 0n value of BigInt is Falsy 5. The Boolean value false is itself falsy

The operations that trigger ToBoolean are

let a = 1; let b = Boolean(a); // true let c = !! a; // trueCopy the code

4. ToPrimitive

A mechanism for converting object types to raw data types. You need to familiarize yourself with a few methods on an object before getting familiar with ToPrimitive.

obj[Symbol.toPrimitive]();
obj.toString();
obj.valueOf();
Copy the code
1. [Symbol.toPrimitive]

This is a new object method. The default object does not have this method. We need to set it manually. It is usually written as follows:

let a = {
    [Symbol.toPrimitive](hint){
        if(hint === "string") {return "string"
        }
        if(hint === "number") {return 1;
        }
        if(hint === 'default') {return "any"}}};console.log(Number(a)); / / 1
console.log(String(a)); // "string"
console.log(a + 1); // "any1"
Copy the code

First, [symbol.toprimitive] accepts a string argument that may take the following value:

“Number” means “number” means “number”.”default” means the language doesn’t know what type to convert to. When we explicitly call the string built-in function, The ToPrimitive function takes a “string” argument and returns a value. The important thing to note here is that a value of the base type must be returned, or an error message will be returned if a value of the reference type is returned. Then, when a value of the primitive type is returned, the String method is executed to convert it to a String. Here’s an example:

let a = { [Symbol.toPrimitive](hint){ if(hint === "string"){ return {}; }}} String(a); // VM247:1 Uncaught TypeError: Cannot convert object to primitive value let a = { [Symbol.toPrimitive](hint){ if(hint === "string"){ return true; }}} String(a); // "true" // returns a stringCopy the code

As you can see, the [symbol.toprimitive] method very clearly defines all the behavior of an object’s type conversion, and should be preferred if we need to customize the object’s type conversion rules.

2. valueOf

The valueOf method, which performs an unpacking operation, changes its behavior depending on the object. Here we divide objects into plain objects and wrapped objects.

    1. Packaging object

By wrapper object, we mean the object form of the primitive type value. So what is a wrapped object? To illustrate this, let’s start with an example:

let a = "1";
a.toString === String.prototype.toString; // true
Copy the code

Here we can see that we first declare a string. Then we discover that the toString method on this String is actually the toString method on the prototype of the String built-in function. The problem is that methods are methods of objects, not strings. Why can values of primitive types be called to methods on objects? Inside the engine, we create a new wrapper object and unwrap it:

let a = "1"; function toString(val){ var _temp = new String(val); Var result = _temp.valueof (); var result = _temp.valueof (); _temp = null; _temp = null; return result; } a.toString(); // "1" toString(a); / / "1"Copy the code

Therefore, for a wrapper type, its valueOf method is an unpacking operation that extracts its base type value.

new String("1").valueOf(); // "1"
new Number(1).valueOf(); // 1
new Boolean(true).valueOf(); // true
Copy the code
  • 2. Common objects

But for ordinary objects, valueOf returns the object itself, which includes ordinary objects, functions, arrays, and regular expressions. One exception here is the Date object, whose valueOf returns the millisecond valueOf its numeric type. It is also important to note that the valueOf method exists on Object. Prototype. Therefore, to be precise, only objects that inherit from object prototypes can have valueOf methods. ValueOf does not exist on objects similar to object.create (null).

3. toString

The toString method is a method that converts an object to a string. There are different representations in different built-in function prototypes.

  1. Object.prototype.toString

The first one we use most often is toString on object prototypes:

let a = {};
a.toString === Object.prototype.toString; // true
Copy the code

This method returns an object type representing the string [object type], such as “[object object]”, “[object Array]”, “[object RegExp]”. The toString method on the Array prototype overrides the toString method on the object prototype, which behaves like this: It executes the String method on each item in the Array and concatenates the resulting value with a comma “,”. Note that null and undefined are converted to an empty string “” instead of the corresponding “null” and “undefined”. Such as:

let a = [null, undefined, {}, function(){}, 2, [3, 5], /123/, new Date(), true, "string"]; Array.prototype.toString.call(a); // ",,[object object],function(){},2,3,5,/123/,Wed Nov 18 2020 14:19:17 GMT+0800,true,stringCopy the code

The toString method of an array can be used for flattening an array of strings because it recursively calls toString on all the internal arrays. 3. The Function. The prototype. ToString Function prototype toString method, will return to the Function of the source string, such as:

function fn(a, b){ return a + b; } Function.prototype.toString.call(fn); /* "function fn(a, b){ return a + b; } "* /Copy the code
  1. Date.prototype.toString

The toString method of the Date object prototype returns a string representing the Date object, such as:

let a = new Date(); Date.prototype.toString.call(a); // "Wed Nov 18 2020 14:25:41 GMT+0800"Copy the code
  1. RegExp.prototype.toString

The toString method of a regular expression object prototype returns a string representing the regular expression object, such as:

let a = /123/; RegExp.prototype.toString.call(a); / / / 123 / ""Copy the code
  1. Custom toString

In addition to the toString methods defined in the prototype above, we can also define a toString method for our own objects:

let a = {
    toString(){
        return "toString";
    }
}
String(a); // "toString"
Copy the code

Note that the value returned must be a primitive value. If it is a reference value, a syntax error will be thrown:

let a = {
    toString(){
        return [];
    }
}
String(a); // Uncaught TypeError: Cannot convert object to primitive value
Copy the code
4. Call rules

For the ToPrimitive operation, it must return a primitive type value or a type error will be thrown. The rule is as follows: 1. The [symbol.toprimitive] method is called if it exists, and type error 2 is thrown if the method returns a reference type value. If the [Symbol. ToPrimitive] method does not exist, judge again

1. If a string conversion is specified, toString 1 is called first. If toString returns a reference type value, valueOf 2 is called instead. If valueOf also returns a reference type value, type error 2 is thrown. If no string conversion is specified, valueOf 1 is called first by default. If valueOf returns a reference type value, toString 2 is called instead. If toString also returns a reference type value, a type error is thrownCopy the code

Implicit type conversion

Implicit type conversions can occur in many scenarios, but we’ll cover only the most common ones:

The equality operator ==

In JavaScript, there are two operators for determining equality: the equality operator == and the congruence operator ===.

Some developers believe that the congruence operator === determines not only the value but also the type. The equality operator ==, on the other hand, evaluates only the value. In fact, this kind of understanding is wrong.

The correct explanation is that the equality operator implicitly casts the expressions on both sides, while the congruence operator checks for equality directly.

The operations of the equality operator are as follows: 1. Check whether the types are the same

2. If the value is a reference type, check whether the memory address of the two objects is the same. Simply speaking, check whether the two objects are the same.Copy the code

2. If the type is different, the conversion rule is as follows:

1. If there are objects, perform ToPrimitive on the objects first and then perform equality check 2. At this point, if there are strings and booleans, the value is ToNumber, converted to a numeric type, and then evaluated for equalityCopy the code

With these rules in mind, we can take a look at the following examples:

"42" == true; // false
Copy the code

At first glance, it seems strange that the string “42” is neither true nor false, giving it a very strange feel. But we should actually convert it like this:

Number("42") == Number(true);
// =>
42 == 1 // false
Copy the code

Convert the values on both sides to numeric types and compare them. There’s another one that’s easy to step on:

[] = =! []; // trueCopy the code

At first glance, a value should be the opposite of its logical non-value, but it is equal here. Why? In fact, this should be done:

[] == false;
// =>
"" == false;
// =>
Number("") == Number(false);
// =>
0 == 0 // true
Copy the code

Here’s a similar question:

{} = =! {}; // false // => {} == false; // => "[object Object]" == false; // => Number("[object Object]") == Number(false); // => NaN == 0; // falseCopy the code

The binary plus operator +

Another operator to be introduced is binary addition.

The binary addition operator can perform two operations: string concatenation 2. Numeric addition The binary addition operator operates as follows: 1. If there are objects at both ends, the ToPrimitive operation is performed on the object first, converting it to a value of primitive type 2. If one end has a string type, the ToString operation is performed on the other end, followed by string concatenation 3. If there is no string type, do ToNumber on both ends and add them. Let’s look at some examples:

let a = {} + 3; // "[object Object]3"
// =>
let a = "[object Object]" + 3;
// =>
let a = "[object Object]" + "3"; // "[object Object]3"
Copy the code

Note here that the preceding assignment expression = cannot be removed. We also often see traps like this:

{} + 3 "; / / 3Copy the code

Such a question, let us strange. In fact, the semantics of curly braces in Javascript are varied. In such a line, there is no assignment, and the engine understands it as a block of code. So, in fact, the code here should be read as follows:

{}; + ", "3"; / / 3Copy the code

In other words, there’s a trap here, where the plus sign is not a binary plus, it’s a unary plus. For unary plus expressions, the ToNumber operation is performed.

Note: under normal circumstances, the development of the use of congruent operation cost symbol, not prone to problems, to avoid strange type conversion.

thanks

This article mainly studied and quoted The article of Edward, sorted out and retold by myself to enhance the impression. Segmentfault.com/a/119000003… One more salute to Big Edward!