3

1. What is type conversion?

The definition of type conversion is easy to understand, that is, the value is converted from one type to another type, for example, from String type to Number type, '42'→42 . However, if we don't know enough about the type conversion rules in JS, we will encounter many confusing problems, as if we did not learn physics and chemistry, there would be magic everywhere in our lives.

Type conversion in JS can be roughly divided into explicit type conversion and implicit type conversion . The difference between the two is very obvious, such as the explicit type conversion ' String(42) ', we can clearly see that this line of code is to convert 42 to String type. However, implicit type conversion is usually a side effect of certain operations, such as ''+42 , which is not so obvious. It is a side effect of addition operations.

If you don't understand the type conversion in JS, the following examples may make you Wang Defa.

//ex.1
console.log([] == ![]); //true

//ex.2
const a1 = {},
    a2 = {};
console.log(a1 < a2); //false
console.log(a1 == a2); //false
console.log(a1 > a2); //false
console.log(a1 >= a2); //true 
console.log(a1 <= a2); //true

//ex.3
const a3 = {
    i: 1,
    valueOf() {
        return this.i++;
    },
};

console.log(a3 == 1 && a3 == 2); //true

These strange phenomena all have the truth behind them, of course, the premise is that you have to understand it.

2. The abstract operation of type conversion

Before introducing explicit and implicit type conversion in detail, we need to know the basic rules of type conversion between String , Number and Boolean The ES5 specification defines abstract operations (or conversion rules) for conversion between these types. The following briefly introduces several abstract operation rules.

  1. ToPrimitive (responsible for converting objects to basic data types)

    In order to convert the value to the corresponding basic type value, the abstract operation ToPrimitive first checks whether the value has the valueOf method. If there is and the method returns basic type value , then use this value to perform a coercion.

    If not, use toString (if it exists) for type conversion.
    If valueOf and toString are not return to the basic type value, it will have a TypeError.

    E.g:

    //ex.4
    const testObj = {};
    console.log(testObj.valueOf()); // {}  testObj本身
    console.log(testObj.toString()); //'[object Object]' string字符串
    /*
        首先调用testObj的valueOf方法,返回值是testObj本身,不是原始(基本)值类型;所以调用testObj的toString方法,返回了字符串'[object Object]',是基本类型,所以使用'[object Object]'进行操作。
    
    */
    console.log('' + testObj); // '[object Object]'
    
    testObj.valueOf = () => 'valueOf';
    /*
        此时调用testObj的valueOf方法,返回的是字符串'valueOf',是基本类型,所以会用该返回值进行操作,而不会再继续调用toString方法了。
    */
    console.log('' + testObj); // 'valueOf'
  1. ToString (responsible for converting non-string to string)

    a. When the value to be converted is basic type ,

    The value to be convertedThe result after converting to String
    null'null'
    undefined'undefined'
    true , false'true' , 'false'
    Symbol('example')Only support explicit type conversion to get 'Symbol('example')'
    Ordinary numbers 0,1,2Comply with general rules '0','1','2'
    Very large or very small number 1/10000000Use exponential form '1e-7' ,'1e+21'

    b. When the value to be converted is an object, if it is an explicit type conversion, the toString method of the value is called. If it is an implicit type conversion, it is converted to a basic type through the ToPrimitive operation first, and then converted according to the rule a It is of String type.

    valueOf method and the toString method of the value to be converted have not been modified, the commonly used object conversions are as follows:

    The value to be convertedThe result after converting to String
    Object'[object Object]'
    Array [1,2,3]Each array element separated by a comma, '1,2,3'
    When the array element is null or undefined, [null],[undefined]Empty string ''
    DateA string in American English date format, Oct 15 2021 14:04:28 GMT+0800 (China Standard Time)'
    Function represents a string of function source code
  1. ToNumber (responsible for converting non-digits to numbers)

    The value to be convertedThe result after converting to Number
    true1
    false0
    undefinedNaN
    null0
    string '42','42px'Basically follow the relevant rules of numeric constants, and return NaN 42,NaN
    symbolNon-convertible
    Complex types such as ObjectFirst ToPrimitive 161695f424d17d, and then convert to Number type

    E.g:

    console.log(Number([null])); //0
    // [null] 通过ToPrimitive转成基本类型是空字符串 '',空字符串转为Number得到0
  2. ToBoolean

    The values ​​in JavaScript can be divided into the following two categories:
    (1) Values ​​that can be converted to false by type: undefined , null , +0, -0 and NaN, false , ''
    (2) Others (values ​​converted to true by type), that is, in addition to the above values.

    It should be noted that there is a concept called false value object . The difference between false value object and ordinary object is that when it is converted to Boolean, it will get false. This kind of situation is very rare, but it does exist, such as document.all object, console.log(!!document.all); //false .

Okay, so far we have a certain understanding of these several abstract operations, and we can dive a little deeper into type conversion below.

3. Explicit type conversion

  1. Explicit type conversion between strings and numbers

    ​ The type conversion between string and number should be our most common one. The explicit type conversion between them is achieved through the native constructor String and Number , but not through new The machine makes a constructor call. When these two functions perform type conversion, they all follow the abstract operations of ToString and ToNumber

    E.g:

    console.log(Number('3')); // 3
    console.log(String(3)); //'3'
  1. Explicit Parsing String

    Although the results of parsing the numbers in the string ( parseInt , parseFloat ) and converting the string into numbers are numbers, the conversion rules of the two are different.

    ​ a. Relative to Number, parseInt allows incoming string parameters to contain non-numeric characters, and the parsing is in the order from left to right, and the parsing is stopped when a non-numeric character is encountered. For example, console.log(parseInt('10px')); //10 , console.log(Number('10px'));//NaN .

    ​ b. parseInt is a function for strings. When the first parameter passed in is not a string, it will be converted to a string and then parsed.

    ​ c. parseInt has a second parameter, which represents the hexadecimal used in parsing, such as console.log(parseInt('11', 8)); //9 , and parses '11' in octal. The range of this parameter is 2-36. If you pass in a value outside the range (if you pass in 0, it will be set to decimal), it will return NaN.

    When using parseInt(str,radix) , radix does not specify a default value (but in most cases radix will be set to 10 by default), which means that if you do not pass in this parameter, the result may be unexpected. For example, when str with 0x or 0X (capital X), radix will be set to 16 by default, so when console.log(parseInt('0x11')); //17 will get 17.

    There is a very interesting example: console.log([1, 2, 3].map(parseInt)); //[ 1, NaN, NaN ] , is to use this hexadecimal parameter, the three items of the resulting array are parseInt(1,0) , parseInt(2,1) , parseInt(3,2) , in the first item, because the hexadecimal is passed in 0, it is regarded as decimal Processing, so it returns 1. In the second item, because the hexadecimal is passed in 1 , which is not in the legal range (2-36), so it returns NaN ; in the third item, because the hexadecimal is passed in 2, it needs to be parsed The parameter is 3 (only the expressions of 0 and 1 are legal in binary), so NaN is returned.

  1. display conversion Boolean

    Like String and Number , Boolean is an explicit ToBoolean type conversion, following the ToBoolean operation rule. However, the method !! For example: console.log(Boolean(1), !!1);

4. Implicit type conversion

Implicit type conversion is generally a side effect of other operations.

  1. addition operation:

    If the operand is the object , the object ToPrimitive operation, and then the operation is continued.

    If one of the operands is of string (String) , then the other operand is also converted to string (String) for string splicing.

    in the operand does not have String type, then the two operands are as 161695f424d7f4 Number type for addition.

    E.g:

    // 因为 'hello' 是字符串,所以把 1 转成了 '1' ,然后进行字符串拼接
    console.log(1 + 'hello'); // '1hello'
    //因为没有字符串类型的操作数,所以进行加法运算,将 true 转成数字类型为 1 ,所以结果为2
    console.log(1 + true); // 2
    //因为 [2] 是对象类型,将其通过 ToPrimitive 操作之后,转成字符串 '2' ,进而需要把 1 转成 '1' ,进行字符串拼接
    console.log(1 + [2]); // '12'
  1. Subtraction, division, multiplication and other operations:

    If the operand is the object , the object ToPrimitive operation, and then the operation is continued.

    Use the two operands as number (Number) type to perform operations.

    E.g:

    // [3] - [2]  → '3' - '2' → 3 - 2 =1
    console.log([3] - [2]); // 1
    //3 - 1 =2
    console.log([3] - true); // 2
  1. The operation of implicitly converting other types to Boolean:

    When the value is used as the judgment condition. The IF statement the conditional expressions; for loop statement conditional expressions; the while loop and a do..while cycle the conditional expressions; ternary operator ( ?:) conditional judgment expression; logical operator || and && as the conditional judgment expression. The value will be implicitly converted to Boolean type for judgment, following the operation rules of ToBoolean

  2. loose and equal (==):

    When comparing relations in JS, loose equality (==) is often interpreted as: "Only compare whether the values ​​of the two are equal, but not the types." In fact, this interpretation has some problems. The "value" in this interpretation , How should we understand it?

    For example, I am comparing console.log(0 == ''); //true , 0 and the empty string ", the original value and type of the two are not the same, but they are loosely equal.

    To explain it more accurately, it should be "When performing a loosely equal relationship comparison, if the two types are the same, then only compare whether the values ​​of the two are the same; if the two types are different, you need to first convert the types to the same Type, and then compare whether the values ​​are the same."

    In other words, when two values ​​of different types are compared for loose equality, implicit type conversion occurs.

    In loose equality, the type conversion rules when the types are different are as follows (assuming that the two operands are x, y ):

    1. If the types of x and y are string (String) and number (Number) , then the string type converted to a number and then compared.

      //先将字符串 '1' 转成了数字 1 ,再进行比较 
      console.log(1 == '1');//true
    2. Boolean type need to be converted to number (Number) type.

      //先将 false 转成了 0,然后再根据规则1,将 '0' 转成了 0,然后进行比较
      console.log('0' == false);//true
    3. If one party is the object type , you need to pass the ToPrimitive abstract operation rule, and then continue to compare after converting to the basic type.

      // 先将对象类型的数组 [1] 转成了字符串 '1' ,再根据规则1,将字符串 '1' 转成了数字 1,然后进行比较
      console.log([1] == 0);//false
    4. Null and undefined are loosely equal. It can be said that in a loosely equal comparison, null and undefined are the same thing.

    Another thing to understand is:

    • NaN is not equal to NaN.
    • +0 is equal to -0

  3. greater than (>), less than (<), greater than or equal to (>=), less than or equal to (<=):

    When making two comparisons: greater than and less than

    A. if the object type by first ToPrimitive operation type turn substantially, making comparisons.

    b. If both are character strings, compare them according to the character's ASCII code value.

    c. is not a string, then both are ToNumber abstract operation, and then compare.

    When performing two comparisons of greater than or equal to and less than or equal to

    greater than or equal means not less than , that is, the result of a>=b is! (a<b). The same is true for less than or equal to.

5. Review

After understanding the JS type conversion rules, looking back at the previous examples, we can all understand these strange phenomena.

ex.1:

//ex.1
console.log([] == ![]); //true
//[] 通过ToPrimitive转成基本类型,得到 '',  ![]得到false。宽松相等比较规则,转成数字 0
// 即 ''==0 , 空字符串''有转成了数字 0 ,所以最终为true 

ex.2:

//ex.2
const a1 = {},
    a2 = {};
//a1 a2都是对象类型,通过ToPrimitive操作之后,结果都是 '[object Object]'
//显然 '[object Object]'<'[object Object]' 和 '[object Object]'>'[object Object]'都是false
console.log(a1 < a2); //false
console.log(a1 > a2); //false
//在进行宽松相等比较时,二者类型相同,不会进行类型转换,而是比较二者的值,a1,a2显然指向的地址不同,所以a1==a2 =也为false
console.log(a1 == a2); //false

//小于等于  大于等于 分别为 大于和小于的结果取反,所以都为true
console.log(a1 >= a2); //true 
console.log(a1 <= a2); //true

ex.3:

//ex.3
const a3 = {
    i: 1,
    valueOf() {
        return this.i++;
    },
};

console.log(a3 == 1 && a3 == 2); //true

//a3是对象类型,通过ToPrimitive操作,转成基本类型得到 a3.i (即1),所以 a3==1 为true
//但是在上面执行valueOf的时候,i自增了一次,所以在 a3==2的比较时, a3转为基本类型的值时,得到的就是2了,所以a3==2也为true。

forceddd
271 声望912 粉丝

一名前端爱好者。