Type conversion

Winter – Geek Time – Relearn front end is authorized by the author, if you want to see more complete content, please go to the original text.

Here’s a phenomenon: type conversions.

Because JS is a weakly typed language, type conversions occur very frequently, and most familiar operations begin with type conversions. Most type conversions are intuitive, but if we don’t understand the strict definition of type conversions, we can easily make errors in our code.

The most notorious of these is the “==” operation in JavaScript, whose rules are so complex that few people can remember them for trying to make comparisons across types. We certainly won’t go into the == rule here, it’s a design error and not a valuable part of the language, and many practices recommend disallowing “==” and requiring programmers to compare with === after explicitly casting.

Other operations, such as addition, subtraction, multiplication, and division greater than or less, also involve type conversions. Fortunately, most of the conversion rules are actually quite simple, as shown in the following table:

In this case, the more complicated parts are the conversions between Number and String, and between objects and primitive types. Let’s take a look at the rules for each transformation.

StringToNumber

There is a syntactic structure for string to number conversions. Type conversions support decimal, binary, octal, and hexadecimal, for example:

  • 30;
  • 0 b111;
  • 0 o13;
  • 0 XFF.

In addition, JavaScript supports a string syntax that includes a scientific notation for positive and negative signs, which can be represented by either uppercase or lowercase e:

  • 1 e3;
  • E – 2-1.

Note that parseInt and parseFloat do not use this transformation, so the syntax supported is different.

Without passing in a second argument, parseInt only supports the hexadecimal prefix “0x”, ignores non-numeric characters, and does not support scientific notation. In some older browser environments, parseInt also supports numbers starting with 0 as a base 8 prefix, which is a source of many errors. So in any case, it is recommended to pass in the second argument to parseInt, whereas parseFloat parses the string directly as decimal and does not introduce any additional bases.

In most cases, Number is a better choice than parseInt and parseFloat.

The following is from MDN

parseInt(string [, radix ]);

  • String Specifies the value to be parsed. If the argument is not a string, it is converted to a string (using the ToString abstraction operation). Whitespace at the beginning of the string will be ignored.

  • Radix Optional from 2 to 36, representing the number of the carry system. For example, to specify 10 is to specify the decimal place. Note that the usual default is not 10 carry!

If the radix is undefined, 0, or unspecified, JavaScript assumes the following:

  1. If the input string begins with “0x” or “0x” (a 0 followed by a lowercase or uppercase X), the radix is assumed to be 16 and the rest of the string is parsed into hexadecimal numbers.
  2. If the input string begins with “0” (0), the radix is assumed to be 8 (octal) or 10 (decimal). Which radix to choose depends on the implementation. ECMAScript 5 clarifies that 10 (decimal) should be used, but not all browsers support it. Therefore, it is important to specify a radix when using parseInt.
  3. If the input string begins with any other value, the radix is 10 (decimal).

If parseInt encounters a character that is not a number in the specified Radix parameter, it ignores that character and all subsequent characters and returns the parsed integer value up to that point. ParseInt truncates a number to an integer value. Leading and trailing Spaces are allowed. – the MDN

Normally, the return value of parseInt is a number parsed from the given string. Another possible return value is NaN. There are two scenarios:

  1. NaN is returned when a non-whitespace character is encountered that cannot be converted to a number, such as parseInt(‘321’,2). Because base 2 is specified, the 3 in the string does not conform to the base rule and cannot be parsed properly
  2. The imported radix is less than 2(non-0) or greater than 36 (please think about why)

Conclusion: When a string is passed that begins with “0x”, the default is a hexadecimal number. All other formats are base 10, so the format for converting a binary number is parsetInt(‘011’, 2), not parsetInt(‘0b11’, 2). The same is true for base 8 numbers. Instead of using parseInt(‘0o11’, 8), use parseInt(‘011’, 8).

The following figure is from the definition in the standard specification. See the definition of parseInt in the standard for more details

NumberToString

On a smaller scale, the numeral-to-string conversion is a decimal representation that perfectly conforms to your intuition. When the absolute value of Number is large or small, the string representation is represented using scientific notation. This algorithm is a lot of detail, but from an emotional point of view, it’s basically a guarantee that the resulting string will not be too long. For specific algorithms, refer to the JavaScript language standard.

The transformation using the

Each basic type Number, String, Boolean, Symbol have corresponding classes in the object, the so-called boxing conversion, is to convert the basic type into the corresponding object, it is a rather important type conversion.

The global Symbol function cannot be called using new, but we can still use the boxing mechanism to get a Symbol object. We can use the call method of a function to force a boxing. We define a function that only has return this in it, and then we call the call method of the function to a value of type Symbol, which produces a symbolObject. We can use console.log to look at the type of this object, which is object, and we can use symbolObject instanceof to see that it’s an instanceof the Symbol class, We find its constructor equal to Symbol, so it is the boxed object of Symbol whichever way we look at it:

var symbolObject = (function(){ return this; }).call(Symbol("a"));

console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true
Copy the code

The boxing mechanism frequently produces temporary objects, and in some performance-critical scenarios, we should avoid boxing primitives.

Using the built-in Object function, we can explicitly invoke the boxing capability in JavaScript code

var symbolObject = Object(Symbol("a"));

console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true
Copy the code

The Class attribute to each type of packing objects are private, these properties can use Object. The prototype. The toString access:

var symbolObject = Object(Symbol("a"));

console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol]
Copy the code

In JavaScript, there is no way you can change the private Class attribute, therefore Object. The prototype. The toString is a basic type of Object can accurately identify the corresponding methods, it is much more accurate than instanceof. Note, however, that call itself generates a boxing operation, so you need to work with Typeof to distinguish between primitive types and object types. As follows:

const OBJECT_TYPE = 'object';
const OBJECT_FLAG = '[object ';

// Implement a method that can be used to get the private Class property of the incoming data
const GetValueClass = function (value) {
  // Determine if it is a base type
  if (typeofvalue ! == OBJECT_TYPE) {// Base types are converted to output
    let type = typeof value;
    return type.charAt(0).toUpperCase() + type.substring(1);
  }

  let classString = Object.prototype.toString.call(value);
  return classString.substring(OBJECT_FLAG.length, classString.length - 1);
};

// Implement a MyInstanceof function similar to instanceof
const MyInstanceof = function (value, typeString) {
  // Get private attributes
  let type = GetValueClass(value);
  // Determine the value of the private attribute
  return type === typeString;
};
Copy the code

Unboxing conversion

In the JavaScript standard, the ToPrimitive abstract function is specified, which is an object type ToPrimitive type conversion, namely unboxing conversion.

The conversions of objects to String and Number follow the “unboxing before converting” rule. Unboxing converts an object to a primitive type and then converts it to a String or Number. If neither valueOf nor toString exists or does not return a primitive type, a TypeError is generated. The following demonstrates calling valueOf first and then toString

    var o = {
        valueOf : () = > {console.log("valueOf"); return {}},
        toString : () = > {console.log("toString"); return {}}
    }

    o * 2
    // valueOf
    // toString
    // TypeError
Copy the code

The following demonstrates calling toString first and then valueOf


    var o = {
        valueOf : () = > {console.log("valueOf"); return {}},
        toString : () = > {console.log("toString"); return{}}}String(o)
    // toString
    // valueOf
    // TypeError
Copy the code

After ES6 unboxing conversion, if the object has [symbol.toprimitive] deployed, [symbol.toprimitive] will be called and terminated. Without [symbol.toprimitive], valueOf and toString (in different order depending on the scenario) are called to get the unboxed base type


    var o = {
        valueOf : () = > {console.log("valueOf"); return {}},
        toString : () = > {console.log("toString"); return {}}
    }
    o[Symbol.toPrimitive] = () = > {console.log("toPrimitive"); return "hello"}
    console.log(o * 2);
    // toPrimitive
    // NaN

		console.log(String(o));
		// toPrimitive
    // "hello"

		console.log(o + ' ');
		// toPrimitive
    // "hello"
Copy the code

Unpacking conversion from standard specification


This section on unpacking conversion is defined in the standard specification as follows

As can be seen from the above specification, ToPrimitive conversion relies on an optional parameter preferredType, supporting number and string. If it is not transmitted, the default value is number as shown in figure 2 C. This will eventually result in valueOf being called first. Those who are interested can be directLook at the original.

So if you want to know whether valueOf or toString comes first, just look up the corresponding preferredType. For example, when you want to know the true order of execution of a + b, you can query binaryCriteria of operation



The first step is to verify that the incoming data is accessible and reachable using GetValue (The original specificationHere), screenshot below

Then you can get the next string or number operation criteria



The order of execution of a + b is as follows:

  1. The first step is to make a the left hand end of the expression and B the right hand end of the expression. Then check whether the expression can be accessed normally. If there is no definition, an error is reported
  2. Access to the value of the into ApplyStringOrNumericBinaryOperator due to operational connector opText is a plus sign “+”, meet the specification of scenario 1
  3. Scenario 1 calls ToPrimitive without passing the second parameter preferredType, so the default is number
  4. Remember, remember (°, °) Blue ✿)

typeof






In the table, most items correspond, but note that Object — Null and function — object are special cases.