In general, casts take place at compile time in statically typed languages, while casts take place at run time in dynamically typed languages, though in JavaScript they are often called casts. In fact, I think it’s more accurate to divide it into “explicit casting” and “implicit casting”.

The data type

There are six simple data types in the JS, undefined, null, string, number, symbol, and a type of complex object, but when the JavaScript in the statement only one type, only to run during will determine the current type, at run time, Because JavaScript is a weakly typed language, there are no strict restrictions on types, so that operations can be performed between different types, which involves converting between types.

Displays casts

A display conversion is a manual conversion from one value to another, and is a conversion that code has an explicit intent for. Boolean(),String(), and Number() are used to convert a value to a Number,String(), or Boolean(). ToString(),ToBoolean(); ToNumber(); ToString(); ToBoolean();

In addition to the Number(),String(), and Boolean() functions, the following methods also display conversions.

Refer to the official ECMA documentation

1. toString()

ToString handles non-string coercion as follows:

// Number to string
(123).toString() / / '123'
// Boolean to string
(true).toString() // 'true'
// Array to string
['hello'.'world'].toString() // 'hello,world'
// Object to string
({name: 'hello world'}).toString() // '[object Object]'
// Date object to string
Date().toString() // 'Sat Aug 08 2020 01:26:31 GMT+0800 '
//JSON object to string
JSON.toString() // '[object JSON]'
// Function turns to a string
Function.toString() // 'function Function() { [native code] }'
// Function to string
(function(){ return 1; }).toString() // 'function () { return 1; } '
Copy the code

Ordinary objects, invoke is internal toPrimirive method, unless its own definition, or with the Object. The prototype. The toString () returns the value of [[class]] internal attributes, such as [Object Object].

2.toNumber

Other rules for converting to number are shown in the table below:Rule for converting String to Number:

  • (1) If the string contains numbers so long convert to a number of pairs
  • (2) If the string contains a hexadecimal format, convert to a decimal number
  • (3) If the string is empty, then it is converted to 0
  • (4) If the string contains characters other than the above, then it is converted to NaN.

3.toBoolean

ToBoolean conversion rules are as follows:

As can be seen above, all objects return true, including [] and {}. But there’s a problem with falsy objects.

var a = new Boolean(false) //false
var b = new Number(0)  //false
var c = new String(' ')  //false

Boolean(a && b && c) //true Note that this must be wrapped in Boolean
Copy the code

So false returns true when encapsulated.

4. Unary operator (+ -)

+ and -0 explicitly convert non-numbers to numbers.

console.log(+ '1.02');  / / 1.02
console.log(+ -0); / / - 0
console.log(+ []); / / 0
console.log(+ {}); //NaN
console.log({} -0); //NaN
Copy the code

5.parseInt()

ParseInt () is often used when dealing with integers. When converting a string, the parseInt() function ignores the space before the string until it finds the first non-space character.

ParseInt () returns NaN if the first character is not a number or a minus sign, as does parseInt() converting an empty string.

If the first character is a numeric character, parseInt() continues parsing the second character until all strings are parsed or a non-numeric character is encountered, returning only the numeric part.

The parseInt() method also has a base mode that converts binary, octal, hexadecimal, or any other base string into integers.

The base is specified by the second argument to the parseInt() method, so to parse the hexadecimal value, of course, you can call the parseInt() method this way for binary, octal, or even decimal (the default mode).

If the second argument is 0, undefined, or null, it is ignored.

console.log( parseInt(' -88ddd2 '));/ / - 88
console.log(parseInt("100".2));  / / 4
console.log(parseInt("AF"));  //NaN
console.log(parseInt("AF".16));  / / 175
console.log(parseInt("AG".16));  // convert only hexadecimal parts
console.log(parseInt("1.23")); // 1 converts 1.23 to a string first
console.log(( parseInt('90'.undefined)));/ / 90
Copy the code

6.parseFloat

Like the parseInt() function, parseFloat() parses each character from the first character (position 0). Parse to the end of the string or until an invalid floating-point numeric character is encountered.

That is, the first decimal point in the string is valid, but the second decimal point is invalid, and the string after it is ignored.

But parseFloat() only parses decimal, so it doesn’t need to specify a second argument as the base.

ParseFloat () returns an integer if the string contains a number that can be parsed to a positive number (no decimal point, or zero after the decimal point).

console.log(parseFloat('123AF')); / / 123 console. The log (parseFloat (" 22.3.56 ")); / / 22.3 console. The log (parseFloat (" 04.900 ")); / / 4.9Copy the code

The difference between parseInt() and parseFloat() is:

  • The first decimal point in the string parsed by parseFloat() is valid, whereas parseInt() stops parsing when it encounters a decimal point, because a decimal point is not a valid numeric character.
  • ParseFloat () always ignores leading zeros, hexadecimal strings are always converted to 0, and the second argument to -parseint () sets the base to convert to.

Implicit cast

Implicit conversions are usually the case when operators are involved, such as adding two variables, or comparing whether two variables are equal. Implicit type conversions are already shown in our example above. ToPrimitive’s rules are also followed for converting objects ToPrimitive types, as discussed below.

1. Type conversion from the ES specification

ToPrimitive

When an object is converted to its original type, the built-in ToPrimitive method is called, which in turn calls the OrdinaryToPrimitive method.

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

  • If it doesn’t come inPreferredTypeArgument to make hint equal to “default”
  • ifPreferredType“Hint String”, “hint” equals “String”
  • ifPreferredTypeHint Number, so hint equals “Number”
  • letexoticToPrimIs equal to theGetMethod(input, @@toPrimitive), which means to get parametersinput@@toPrimitivemethods
  • ifexoticToPrimInstead of Undefined, let result equal toCall(exoticToPrim, input, « hint »)ExoticToPrim (hint) returns result if result is the original data type, otherwise throws an exception of the wrong type
  • If hint is “default”, make hint equal to “number”
  • Returns the result of the OrdinaryToPrimitive(input, hint) abstraction operation

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) If hint is a string and the value is ‘string’ or ‘number’

(3) If hint is ‘string’, set methodNames to toString, valueOf

(4) If hint is ‘number’, set methodNames to valueOf, toString

Set method to O[name] (valueOf and toString)

(6) If method can be called, set result equal to the result of method execution, return result if result is not an object, or raise a type error.

ToPrimitive code implementation

If you only use words to describe it, you will feel too obscure, so I will use my own code to implement these two methods to help you understand.

 // Get the type
 const getType = (obj) = > {
     return Object.prototype.toString.call(obj).slice(8, -1);
 }
 // Whether it is a primitive type
 const isPrimitive = (obj) = > {
     const types = ['String'.'Undefined'.'Null'.'Boolean'.'Number'];
       returntypes.indexOf(getType(obj)) ! = = -1;
 }
 const ToPrimitive = (input, preferredType) = > {
     // If input is a primitive type, no conversion is required
     if (isPrimitive(input)) {
         return input;
     }
     let hint = ' ', 
         exoticToPrim = null,
         methodNames = [];
     // Hint defaults to "default" when the optional preferredType parameter is not provided;
     if(! preferredType) { hint ='default'
     } else if (preferredType === 'string') {
         hint = 'string'
     } else if (preferredType === 'number') {
         hint = 'number'
     }
     exoticToPrim = input.@@toPrimitive;
     // If there is toPrimitive method
     if (exoticToPrim) {
         // If exoticToPrim returns a primitive type after execution
         if (typeof(result = exoticToPrim.call(O, hint)) ! = ='object') {
             return result;
         // If exoticToPrim returns an object
         } else {
             throw new TypeError('TypeError exception')}}Here the default hint value is given as number. Symbol and Date change the default value by defining the @@toprimitive method
     if (hint === 'default') {
         hint = 'number'
     }
     return OrdinaryToPrimitive(input, hint)
 }
 const OrdinaryToPrimitive = (O, hint) = > {
     let methodNames = null,
         result = null;
     if (typeofO ! = ='object') {
         return;
     }
     // This determines whether toString or valueOf is called first
     if (hint === 'string') {
         methodNames = [input.toString, input.valueOf]
     } else {
         methodNames = [input.valueOf, input.toString]
     }
     for (let name in methodNames) {
         if (O[name]) {
             result = O[name]()
             if (typeofresult ! = ='object') {
                 return result
             }
         }
     }
     throw new TypeError('TypeError exception')}Copy the code

To summarize, when performing a type conversion, you usually convert a reference type to a primitive type using the ToPrimitive method. If the @@Toprimitive method has a reference type, the @@Toprimitive method is called, and the original type is returned. If it is still an object, an error is thrown.

If there is no toPrimitive method on the object, the toString or valueOf method is called first based on the target type of the conversion, and returns the valueOf the original type if either method is returned. Otherwise, an error will be 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

Summary type conversion has been learning JS is difficult to understand a concept, because conversion rules are more complex, often make people feel puzzling. But if you understand how these transformation rules work from the ECMA specification, it’s easy to see why you end up with those results.


Reference: juejin. Cn/post / 684490…

https://zhuanlan.zhihu.com/p/85731460
Copy the code