This is the 7th day of my participation in the August Challenge. Check out the August Challenge for details

preface

Type conversions in JavaScript have always been one of the biggest headaches for front-end developers. There’s a picture on the web that says JavaScript is amazing.

Let’s look at that graph and see if the output is a foregone conclusion.

Because JavaScript is a weakly typed language, type conversions occur frequently. This article will help you sort out the conversions between the various types. Of course, you can see why the above code is executed and the output is the way it is. Let’s dive deeper into JavaScript type conversions so they don’t get in the way of front-end development.

JavaScript type conversion classification

There are two types of type conversions: explicit and implicit.

Explicit type conversion: where we have to manually convert one value to another

Implicit type conversion: programs perform type conversion stealthily without human intervention

Generally speaking, type conversion is mainly from basic type to basic type and reference type to basic type. The target types of conversion are divided into the following types:

  • Is converted to a number
  • Convert a string
  • Converted to Boolean

Each will be explained below:

Explicit/cast

ToNumber: converts ToNumber

The other rules for converting to number are shown in the table below

The original value Conversion results
string Convert according to syntax and conversion rules
true 1
false 0
null 0
undefined NaN
symbol TypeError: Cannot convert a Symbol value to a number
object Call toPrimitive first, then toNumber

Note:

  • According to the specification, the Number function returns +0 if it takes no arguments, or is called if it takes any argumentsToNumber(value)

Note that this ToNumber represents a method on the underlying specification implementation and is not directly exposed.

We can use + to convert other types to number to verify the conversion results of the table above

console.log(+true); / / 1
console.log(+false); / / 0
console.log(+null); / / 0
console.log(+undefined); // NaN
console.log(+Symbol('hi')); // Uncaught TypeError: Cannot convert a Symbol value to a number
// The code that previews the image, the output result
console.log('b' + 'a' + + 'a' + 'a'); 
// "baNaNa", then call the string method, make the string lowercase, the result is baNaNa
console.log( ('b' + 'a' + + 'a' + 'a').toLowerCase() ); // "banana"
Copy the code

The rule for converting a string value to a number: essentially

  1. If the string contains only numbers, convert to the corresponding number.
  2. If the string contains only hexadecimal format, it is converted to the corresponding decimal number
  3. If the string is empty or contains only Spaces, it is converted to 0
  4. If the string contains strings other than the above, then convert to NaN.
/ / 1 point
console.log(+'123'); / / 123
/ / 2 points
console.log(+'0x100f'); / / 4111
/ / 3 points
console.log(+' '); / / 0
/ / 4 points
console.log(+'hi 234'); // NaN
Copy the code

ToString: converts ToString

The original value Conversion results
number The corresponding string type
true ‘true’
false ‘false’
null ‘null’
undefined ‘undefined’
symbol TypeError: Cannot convert a Symbol value to a string
object ToPrimitive is called first, then toString is called

We can use template strings to convert other types to strings to verify the above rule:

console.log(`The ${11}`); / / "11"
console.log(`The ${true}`); // "true"
console.log(`The ${false}`); // "false"
console.log(`The ${null}`); // "null"
console.log(`The ${undefined}`); // "undefined"
console.log(`The ${Symbol('hi')}`); // Uncaught TypeError: Cannot convert a Symbol value to a string
console.log(`The ${{}}`); // "[object Object]"
Copy the code

ToBoolean: convert ToBoolean

Remember the five Falsy rules:

In JS, only “0 / NaN/empty string “”/null/undefined” are converted to Boolean false, the rest are converted to true note:

  • A Boolean function returns false when it passes no arguments
  • False is itself a Boolean value

We can use it!! Convert other types to Boolean types to verify the above rule:

/ /!!!!! If I take the inverse twice, that's the same thing as if I didn't take the inverse at all
console.log(!!0); // false
console.log(!!NaN); // false
console.log(!!' '); // false
console.log(!!null); // false
console.log(!!undefined); // false
console.log(!!Symbol()); // true
console.log(!! {});// true
Copy the code

Let’s see if we get the pattern, what is the output of this code?

console.log(!!false); // false
console.log(!!new Boolean(false)); // true
Copy the code

Why is the second output true? New Boolean(false) creates a Boolean object whose value is false, so it is not one of the five falsy values, so the result is true.

Implicit type conversion

Let’s take a look at a few scenarios where implicit type conversions occur and how.

Type conversions in mathematical operators

Subtraction, multiplication and division

When we apply the mathematical operator (- * /) to various non-number types, we first convert non-number types to number types;

1 - true // 0, first convert true to the number 1, then execute 1-1
1 - null // 1, first convert null to the number 0, then execute 1-0
1 * undefined // NaN, where undefined converts to a number NaN
2 * ['5'] // 10, ['5'] will first become '5', and then the number 5
Copy the code

The conversion of [‘5’] in the above example involves unpacking.

Special addition

Addition, in particular, requires a distinction.

Because + isn’t just math, it can also be used to concatenate strings. So here are three rules to keep in mind:

  1. When a side forstringType that is recognized as string concatenation and will preferentially convert the other side to string type.
  2. When a side fornumberType, the other side is the original type, the original type is converted tonumberType.
  3. When a side fornumberType, on the other side is the reference type, which references the type andnumberType to string concatenation.

Above 3 points, priority from high to low.

/ / rule 1
123 + '123' / / "123123"

2 / / rules
123 + null  / / 123
123 + true / / 124

/ / 3
123 + {}  // "123[object Object]"
Copy the code

If statements and logical statements

In if and logical statements, if there is only a single variable, the variable is first converted to Boolean, the five falsy values and false are converted to false, and the rest are converted to true. Remember the five falsy rules:

In JS, only “0 / NaN/empty string “”/null/undefined” are converted to Boolean false, the rest are converted to true

= =

In everyday development, we usually use === to judge, but it’s good to know the characteristics of == because it feels too hard to remember.

When == is used, if both sides of the type are the same, then the comparison result is the same as ===, otherwise an implicit conversion will occur. The conversion that occurs when == is used can be divided into several different cases (only the two sides of the type are considered) :

Case 1

NaN always returns false when compared to any other type (including himself).

NaN= =NaN // false
Copy the code
Case 2

Boolean is first converted to the number type in comparison to any other type.

true= =1  // true 
true= ='2'  // false, instead of making '2' true, change true to 1
true= = ['1']  ['1'] = '1'
true= = ['2']  / / false, the same as above
undefined= =false // false, first false becomes 0, then refer to case 4
null= =false // false as above
Copy the code
Case 3

Compare string and number. First, convert string to number

123= ='123' // true, '123' becomes 123 first
' '= =0 // true, '' becomes 0 first
Copy the code
Scenario 4

Null == undefined the comparison result is true. Otherwise, the comparison value of null, undefined, and any other result is false

null= =undefined // true
null= =' ' // false
undefined= =' ' // false
Copy the code
Scenario 5

When the original type and reference type are compared, the object type is converted to the original type according to ToPrimitive rules:

The ToPrimitive rule, which converts a reference type to a primitive type, follows the valueOf then toString pattern of expecting a primitive type. If you still can’t get a primitive type, TypeError is raised.

'[object Object]'= = {}// true, an object is compared to a string. The object gets a primitive value from toString
'1, 2, 3'= = [1.2.3] 
[1, 2, 3] returns a primitive type value through toString[] = =! []// true
/ / `! 'takes precedence over' == ', '! [] 'is first converted to' false ', and then according to the second point above,
// 'false' converts to type '0' and left '[]' converts to type '0'.

[null] = =false // true
[undefined] = =false // true
// According to ToPrimitive array rules, when the array element is null or undefined,
// This element is treated as an empty string, so both [null] and [undefined] are converted to 0.
Copy the code

I’m finally done with ==, it’s too hard to use.

Here is the “truth table” of x == y. The left side represents the value of x, the top side represents the value of y, green represents true, and white represents false:

== is a difficult symbol to use and very, very error-prone!!

Therefore, we recommend using === instead of ==.

Truth table of x === y:

Type conversions from the ES specification

Throughout the article, the word ToPrimitive appears more frequently. Next, let’s look at type conversions from the ES specification.

Let’s take a look at toString and valueOf

toString

The toString() method returns a string representing the object.

Each reference type has a toString method, which is inherited by every Object by default. If this method is not overridden in a custom object, toString() returns “[Object type]”, where type is the type of the object.

It is worth noting that toString works only if the method is not overridden in a custom object. In fact, most reference types, such as Array, Date, RegExp, etc., override toString.

console.log(({}).toString()) // "[object Object]"

console.log([].toString()) / / ""
console.log([0].toString()) / / "0"
console.log([1.2.3].toString()) / / "1, 2, 3"
console.log((function(){var a = 1; }).toString())// "function(){var a = 1; }"
console.log((/\d+/g).toString()) // "/\\d+/g"
console.log((new Date(2010.0.1)).toString()) // "Fri Jan 01 2010 00:00:00 GMT+0800"
Copy the code

valueOf

The valueOf() method returns the original valueOf the specified object.

The default valueOf method returns the object itself, and arrays, functions, and regees simply inherit the default method and return the object itself. The exception is the date, which returns one of its content representations: the number of milliseconds since January 1, 1970.

var date = new Date(2021.8.14);
console.log(date.valueOf()); / / 1631548800000
Copy the code

How is object to string converted

The parameter types The results of
Object 1. primValue = ToPrimitive(input, String)

2. Return the ToString (primValue).

The so-called ToPrimitive method simply takes a value and returns a value that must be of a primitive type.

summary

When we use the String method to convert a value

  • If it is a basic type, the table corresponding to “ToString: convert ToString “is used
  • If it is not a basic type, we call the ToPrimitive method, convert it to a basic type, and then perform the same conversion as the above basic type

How are objects converted to numbers

The parameter types The results of
Object 1. primValue = ToPrimitive(input, Number)

2. Return ToNumber(primValue).

summary

When we convert a value using the Number method

  • If it is a basic type, use the table corresponding to “ToNumber: convert ToNumber”
  • If it is not a basic type, we call the ToPrimitive method, convert it to a basic type, and then perform the same conversion as the above basic type

The ToPrimitive method is used for all conversions to base values, but the final processing is different depending on the argument, with ToString being called for converting ToString and ToNumber being called for converting ToNumber.

ToPrimitive

The ToPrimitive method is used for converting an object to a string and a number, but the final processing is different depending on the arguments passed. ToString is called for converting a string, ToNumber is called for converting a number.

Let’s look at the ES5 specification 9.1 function syntax as follows:

ToPrimitive(input[, PreferredType])
Copy the code

The first parameter, input, represents the input value to be processed.

The second parameter is the PreferredType, which is optional and indicates the type you want to convert to. You can choose from two values, Number or String.

When the PreferredType is not passed, it is equivalent to a String if the input is of date type, and a Number otherwise.

If the input is Undefined, Null, Boolean, Number, or String, the value is returned.

If ToPrimitive(obj, Number) is used, the procedure is as follows:

  1. If obj is a basic type, return it directly
  2. Otherwise, the valueOf method is called, and if a raw value is returned, JavaScript returns it.
  3. Otherwise, the toString method is called, and if a raw value is returned, JavaScript returns it.
  4. Otherwise, JavaScript throws a type error exception.

If it is ToPrimitive(obj, String), the processing is as follows:

  1. If obj is a basic type, return it directly
  2. Otherwise, the toString method is called, and if a raw value is returned, JavaScript returns it.
  3. Otherwise, the valueOf method is called, and if a raw value is returned, JavaScript returns it.
  4. Otherwise, JavaScript throws a type error exception.

summary

The reference type is converted to the number type

That’s the conversion rule behind the Number function

The first step is to call the valueOf method of the object itself. If a value of the original type is returned, the Number function is used directly on the value without further steps.

Second, if the valueOf method still returns an object, call the toString method of the object itself instead. If the toString method returns a value of the original type, the Number function is used on the value without further steps.

Third, if the toString method returns an object, an error is reported.

The reference type is converted to string

The String method is essentially the same as the Number method, but the order in which valueOf and toString are executed is reversed.

The first step is to call the object’s own toString method. If a value of the original type is returned, the String function is used for that value and the following steps are not performed.

Second, if the toString method returns an object, call the valueOf method of the original object. If the valueOf method returns a valueOf the original type, the String function is used for that value and the following steps are not performed.

Third, if the valueOf method returns an object, an error is reported.

Is it over? Not yet, you can go further, and you can decide if you want to go further, depending on the actual situation.

When an object is converted to a primitive type, the built-in ToPrimitive method is called, and the ToPrimitive method calls the OrdinaryToPrimitive method. See the ECMA documentation

Big guy’s translation

The ToPrimitive method takes two parameters, the input value and the PreferredType of the desired conversion.

  1. If it doesn’t come inPreferredTypeParameters,hintIs equal to “default”
  2. ifPreferredType 是 hint StringAnd lethintIs equal to “string”
  3. ifPreferredType 是 hint NumberAnd lethintIs equal to the “number”
  4. letexoticToPrimIs equal to theGetMethod(input, @@toPrimitive), which means to get parametersinput 的 @@toPrimitivemethods
  5. ifexoticToPrimnotUndefinedSo let’sresultIs equal to theCall(exoticToPrim, input, « hint »)“Means to executeexoticToPrim(hint)If the result is executed afterresultIs the original data type and returnsresultOtherwise, an exception of the wrong type is thrown
  6. ifhintBe “default”, let mehintIs equal to the “number”
  7. returnOrdinaryToPrimitive(input, hint)The result of an abstract operation

To put it in plain English, a reference type is converted to a primitive type using the toPrimitive method. If the @@Toprimitive method is on the reference type, the @@Toprimitive method is called, which is equivalent to the above exoticToPrim, which returns the original value. Otherwise, an error exception is thrown.

OrdinaryToPrimitive

The OrdinaryToPrimitive method also takes two parameters, O, the input value, and hint, the type to be converted.

  1. If the input value is an object
  2. ifhintIt’s a string and it’s either ‘string’ or ‘number’
  3. ifhintIt’s ‘string’, so it’s going tomethodNamesSet totoString,valueOf
  4. ifhintIt’s ‘number’, so it’s going tomethodNamesSet tovalueOf,toString
  5. traversemethodNamesGet the value in the current loopnameThat will bemethodSet toO[name](that is, to get thevalueOf 和 toStringTwo methods)
  6. ifmethodCan be called, so let’sresultIs equal to themethodResults after execution ifresultReturn if it is not an objectresultOtherwise, a type error is thrown.

To put it in plain English, when calling OrdinaryToPrimitive, you need to determine the target type to which you want to convert and decide whether toString or valueOf should be called first. If both methods return the valueOf the original type, then return it. Otherwise, an error exception is thrown.

Symbol.toPrimitive

After ES6, the symbol.toprimitive method is provided, which has the highest precedence when converting types.

const obj = {
    toString() {
        return '1111'
    },
    valueOf() {
        return 222},Symbol.toPrimitive]() {
        return Awesome!}}const num = 1 + obj; / / 667
const str = '1' + obj; / / '1666'
Copy the code

reference

  • Conversion of data types
  • Help you understand JavaScript type conversions once and for all
  • Data type conversion is enough for this article
  • Do you really understand variables and types
  • JavaScript deep headache type conversion (part 1)
  • Understand JavaScript type conversion

If there are any mistakes in this article, please correct them in the comments section. If this article helped you or you liked it, please like it and follow it.