• JS 类型转换


    1、JS类型转换共有三种情况:

    • 转换为布尔值
    • 转换为数字
    • 转换为字符串

    2、类型转换表格:

    原始值 转换目标 结果
    number 布尔值 除了0、-0、NaN都为true
    string 布尔值

    除了空字符串都为true

    undefined、null 布尔值 false
    引用类型 布尔值 true
    number 字符串 5 -----> '5'
    Boolean 字符串 true -----> 'true'
    数组 字符串 [1, 2] ----> '1, 2'
    对象 字符串 {id: 1} ----> '[object Object]'
    string 数字 '1' ----> 1 ; 'a' ----> NaN
    数组 数字 空数组为0,存在一个元素且为数字转数字,其他情况NaN
    null 数字 0
    除了数组的引用类型 数字 NaN
    Symbol 数字 抛错
    • 转Boolean:在条件判断时,除undefinednullfalse''NaN0-0其他都转为true,包括所有对象。

    3、类型转换

    • valueOf():如果转换为基础类型,就返回这个对象逻辑上对应的原始类型的值。如String包装对象的valueOf(),应该返回这个对象所包装的字符串;
    • x.toString()返回这个对象的字符串表示。用一个字符串来描述这个对象的内容

      基本类型的valueOf()会返回自身的原始类型,而Array、Function、Object都返回自身,Date返回时间戳,Error和Math没有valueOf()方法

    // object
    var obj = {a: 1};
    obj.valueOf();    // {a: 1}
    obj.toString();    // "[object Object]"
    
    // array
    var arr = [1, 2];
    arr.valueOf();    // [1, 2]
    arr.toString();    // "1, 2"
    
    // function
    var fun = function() {};
    fun.valueOf();    // ƒ () {}
    fun.toString();    // "function() {}"
    
    // date
    var date = new Date();
    date.valueOf();    // 1572592931967
    date.toString();    // "Fri Nov 01 2019 15:22:01 GMT+0800 (中国标准时间)"
    
    // string
    var str = 'abc';
    str.valueOf();    // "abc"
    str.toString();    // "abc"
    
    // number
    var num = 222;
    num.valueOf();    // 222
    num.toString();    // "222"
    
    // boolean
    var bool = true;
    bool.valueOf();    // true
    bool.toString();    // "true"
    
    // null & undefined  无法调用,报错
    
    // regExp
    var reg = /cat/g;
    reg.valueOf();    // /cat/g
    reg.toString();    // "/cat/g"
    
    // window
    var win = window;
    win.valueOf();    // window
    win.toString();    // "[object Window]"
    
    // error
    var err = new Error('abc');
    err.valueOf();    // Error: abc
        at <anonymous>:1:11
    err.toString();    // "Error: abc"

    4、检测类型的四个内部方法:

    • ToPrimitive( input [, PreferredType ] )
    • ToBoolean( argument )
    • ToString( argument )
    • ToNumber( argument )

      这4个方法是ECMAScript定义的4个抽象的操作,它们在JS内部使用,进行类型转换。JS的使用者不能直接调用这些函数,但是了解这些函数有利于我们了解JS类型转换的原理。

      (1)ToPrimitive( input [, PreferredType ] )

        将input转换成一个原始类型的值。PreferredType参数要么不传入,要么是Number或String。如果PreferredType参数是Number,ToPrimitive这样执行:

    1. 如果input本身就是原始类型,直接返回input。
    2. 调用input.valueOf(),如果结果是原始类型,则返回这个结果。
    3. 调用input.toString(),如果结果是原始类型,则返回这个结果。
    4. 抛出TypeError异常。

        以下是PreferredType不为Number时的执行顺序:

      • 如果PreferredType参数是String,则交换上面这个过程的第2步和第3步的顺序,其他执行过程相同。
      • 如果PreferredType参数没有传入
        • 如果input是内置的Date类型,PreferredType视为String
        • 否则PreferredType视为Number  -  先调用ValueOf再调用toString
    Date 转换的时候 PreferredType 视为String
        所有先调用toString。
    
    console.log(111 + new Date());
    // "111Thu Nov 07 2019 20:29:33 GMT+0800 (中国标准时间)"

      (2)ToBoolean:

    Argument Type Result
    Undefined Return false
    Null Return false
    Boolean Return argument
    Number 仅当argument为+0,-0,NaN时,return false;否则一律return true
    String 仅当argument为空字符串(长度为0)时,return false;否则一律return true
    Symbol Return true
    Object Return true

      这些规定都来自ECMA的标准,在条件判断时,除了undefined、null、false、NaN、''、+0、-0,其他所有值都转为true,包括所有对象。

      (3)ToNumber:

    Argument Type Result
    Undefined Return NaN
    Null Return +0
    Boolean 如果argument为true,return 1,为false则return +0
    Number 直接return argument
    String 将字符串的内容转化为数字(比如"23" -> 23),如果转化失败则return NaN(比如"23a" -> NaN)
    Symbol 抛出TypeError异常
    Object primValue = ToPrimitive(argument, Number),再对primValue使用ToNumber(primValue)

      由此可见ToNumber的转化并不总是成功,有时会转化成NaN,有时则直接抛出异常。

    所以Number([1, 2]) -> 先由ToPrimitive(argument, Number)转换[1, 2] 
    
    -> valueOf返回本身,再调用toString返回'1, 2' -> 再ToNumber(primValue)转换为NaN

      (4)ToString:

    Argument Type Result
    Undefined Return 'undefined'
    Null Return 'null'
    Boolean 如果argument为true,return 'true',为false则return 'false'
    Number 用字符串来表示这个数字
    String 直接return argument
    Symbol 抛出TypeError异常
    Object primValue = ToPrimitive(argument, hint String),再对primValue使用ToString(primValue)

    5、隐式类型转换(自动类型转换):

    (1)当JS期望得到某种类型的值,而实际的值是其他类型,就会发生隐式类型转换。系统会自动调用内部的ToBoolean( argument )、ToNumber( argument )、ToString( argument )转换方法。

      eg1:

    if(!undefined
        && !null
        && !0
        && !NaN
        && !''
    ) {
        console.log('true');
    }
        // true

        eg1:因为在if的括号中,js期望得到boolean的值,所以对括号中每一个值都是用Toolean( argument ),将它们转换成boolean。

      eg2:

    3 * { valueOf: function() { return 5 } };    // 15

         eg2:在乘号的两段,js期望得到Number类型的值,所以对右边的对象使用ToNumber( argument ),得到结果5,再与乘号左边的3相乘。

      eg3:

    3 * { valueOf: function() { return {} }, toString: function() { return {} } }
    // Uncaught TypeError: Cannot convert object to primitive value

        eg3:调用ToNumber( argument )的过程中,调用了ToPrimitive(input, Number),因为在toPrimitive中valueOf和toString都没有返回原始类型,所以抛出异常。

    (2)加法:

      符号“+”既可以表示“算数加法”,也可以表示“字符串拼接”。因此:只要‘+’两端的任意一个操作数是字符串,就表示拼接。否则表示算数加法

    12 + 3     // 15
    12 + '3'    // '123'

      计算过程:

    1. 令lval = 符号左边的值,rval = 符号右边的值;
    2. 令lprim = ToPrimitive(lval), rprim = ToPrimitive(rval);
    3. 如果lprim和rprim中有任意一个为String类型,将ToString(lprim)和ToString(rprim)的结果做字符串拼接;

        否则,将ToNumber(lprim)和ToNumber(rprim)的结果做加法。

    [] + []    // ''
    // 提示:ToPrimitive([])返回空字符串
    
    [] + {}    // "[object Object]"
    // 提示:ToPrimitive({})返回"[object Object]"
    
    {} +[]    // 0
    // 结果不符合我们的预期:"[object Object]"
    // 提示:在Chrome中,符号左边的{}被解释成了一个语句块,而不是一个对象
    // 注意在别的执行引擎上可能会将{}解释成对象
    // 这一行等价于 '+ []'
    // '+ anuValue' <==>Number(anyValue)
    
    ({}) + []    // "[object Object]"
    // 加上括号以后,{}被解释成了一个对象

      另外对于加法还需要注意 'a' + + 'b'

    'a' + + 'b'    // "aNaN"
    // 先执行+'b',等于NaN,所以结果为"aNaN"
    // +'1' 类似的形式快速获取Number类型,相当于调用ToNumber方法

      (3)其他运算符:其他运算符中,只要其中一方是数字,那么另一方就会被转为数字

    // 减法运算
    4 - '';    // 4
    4 - '3';    // 1
    4 - 'b';    // NaN
    4 - true;    // 3
    4 - [];    // 4
    4 - [1, 2];    //NaN
    4 - {};    //NaN
    4 - {id: 1};    // NaN
    
    // 乘法运算
    4 * '';    // 0
    4 * '3';    // 12
    4 * 'b';    // NaN
    4 * true;    // 4
    4 * [];    // 0
    4 * [1, 2];    //NaN
    4 * {};    //NaN
    4 * {id: 1};    // NaN
    
    // 除法运算
    4 / '';    // Infinity
    4 / '3';    // 1.333333333
    4 / 'b';    // NaN
    4 / true;    // 4
    4 / [];    // Infinity
    4 / [1, 2];    //NaN
    4 / {};    //NaN
    4 / {id: 1};    // NaN

     TEST:

    5 * '5';    // 25
    Null / 5;    // 0
    5 * [1, 2];    // NaN
    [] + [ ];    // ''
    5 >= NaN;    // false
    5 * 'a';    // NaN
    5 / NaN;    // NaN
    [] + {};    // "[object Object]"
    0 / 0;    // Infinity
    5 * [];    // 0
    5 < [1, 2];    // false
    5 / 'a';    // NaN
    5 >= null;    // true
    5 / [1];    //5
    'a' + + 'b';    // "aNaN"
    5 / undefined;    // NaN
    5 * false;    // 0
    5 + [1, 2, 3];    // "51, 2, 3"
    5 >= true;    // true
    {} + [];    // 0
    5 / [];    // Infinity
    5 * true;    // 5
    ({}) + [];    // "[object Object]"
    5 * null;    // 0
    5 >= 'true';    // false
    5 > [];    // true
    123 + {toString: function() { return 'def'; }}    // "123def"
    5 / null;    // Infinity
    "16" > "5";    // false
    0 > [1];    // false
    123 + {toString: function() { return 'def'; },
        valueOf: function() { return 3; }}    // 126

      (4)比较运算符:

       1.如果是对象,就通过 ToPrimitive 转换对象;

       2.如果是字符串,就通过 unicode 字符索引来比较

    let a = {
        valueOf() {
            return 0;
        },
        toString() {
            return '-1';
        }
    }
    
    a > -1;    // true

       3. == 双等:

        1)x、y都为 Null 或 undefined,return true;一方为 null 或 undefined、NaN,return false;

        2)如果x和y为String、Number或Boolean并且类型不一致,都转为Number再进行比较;

        3)如果存在Object,都转为原始值,再比较

     TEST:

    "0" == null;    // false
    "0" == undefined;    // false
    "0" == false;    // true
    "0" == NaN;    // false
    "0" == 0;    // true
    "0" == '';    // false
    
    false == null;    // false
    false  == undefined;    // false
    false  == NaN;    // false
    false  == 0;    // true
    false  == '';    // true
    false  == [];    // true
    false  == {};    // false
    
    "" == null;    // false
    "" == undefined;    // false
    "" == NaN;    // false
    "" == 0;    // true
    "" == '';    // true
    "" == [];    // true
    "" == {};    // false
    
    0 == null;    // false
    0 == undefined;    // false
    0 == NaN;    // false
    0 == [];    // true
    0 == {};    // false
    
    [] == ![];    // true

    6、显式类型转换(强制类型转换):

      (1) 显式调用Boolean(value)、Number(value)、String(value)完成的类型转换,叫显式类型转换

      (2)new Boolean(value)、new Number(value)、new String(value)传入各自对应的原始类型的值,可以实现“装箱” -- 将原始类型封装成一个对象。其实这三个函数不仅仅可以当作构造函数,可以直接当作普通函数使用,将任何类型的参数转换成原始类型的值:

    Boolean('sdjdh');    //    true
    Number('123');    // 123
    String({ a: 24 });    //"[object Object]"

        其实这三个函数用于类型转换的时候,调用的就是JS内部的 ToBoolean( argument )、ToNumber( argument )、ToString( argument ) 方法!

      Number()比parseInt parseFloat严格。parseInt parseFloat会只截取数字转换。

      (3) 这里解释一下 String({ a: 24 });   //  "[object Object]" 的过程:

        · 执行String({a: 24}) 

          · 执行js内部函数ToString({a: 24})

            · 执行 primValue = ToPrimitive({a: 24}, hint String)

          · {a: 24}不是原始类型,进入下一步

            · 在ToPrimitive内调用({a: 24}).toString(),返回原始值"[object Object]",因此直接返回这个字符串,ToPrimitive后面的步骤不用继续进行了

         · primValue被复制为ToPrimitive的返回值"[object Object]"

            · 执行JS内部函数ToString("[object Object]"),返回"[object Object]"

            · 返回"[object Object]"

          · 返回"[object Object]"

        · 返回 "[object Object]"

      Tips: 为了防止出现意料之外的结果,最好在不确定的地方使用显示类型转换

    let obj2 = {
        valueOf() {
            return 2
        },
        toString() {    
            return []
        }
    }
    
    String(obj2);    // '2'
    2 + obj2;    // 4

      可以重写 Symbol.toPrimitive,该方法在转原始类型时调用优先级最高

    let a = {
        valueOf() {
            return 0
        },
        toString() {
            return '1';
        },
        [Symbol.toPrimitive]() {
            return 2;
        }
    }
    
    1 + a;    // 3
    
    a = {
        valueOf() {
            return 3
        },    
        toString() {
            return '1';
        }
    }
    
    1 + a;    // 4

       (4)parseInt:只会转换字符串。如果传入的不是字符串,会先转换成字符串在进行parse。

    let obj = {
        valueOf: function() {
            return '2px'
        },
        toString: function() {
            return []
        }
    }
    
    parseInt(obj);    // 2      --->如果不是字符串会先根据ToString转换成字符串,再去转换
    
    parseInt(1/0, 19);    // 18    --->1/0转换成Infinity。而有效数字范围是0-9 a-i  所以第一位为I代表18.n超出19的范围了,所以只返回18
    
    parseInt(0.0000008);    // 8    ---> 8e+7 6个0转换成字符串会转换成指数
    
    parseInt(0.000008);    // 0    ---> 0.000008
    
    parseInt(false, 16);    // 250    ---> 'false'=>'fa'在16进制下有效
    
    parseInt(function() {...}, 16);    // 15   ---> 'f'在16进制下有效

        如果不填写第二个参数,则会根据传入的值来判断是什么进制,填写了代表转换成多少进制。

    parseInt('0x10');    // 16
    parseInt('103', 2);    // 2

      (5)~运算符表示-(x + 1):

    ~2;    // -3
    ~-1;    // 0

        根据这个特性,可以对-1进行特殊判断。比如indexof时,if(~a.indexof('bbb'))表示如果是-1就会返回false。

    let a = 'aa';
    if(~a.indexOf('bbb')) { console.log(11); } else {console.log(1222);}        // 1222

      (6)| 运算符:

    3.2 | 0;    // 3
    -6.7 | 0;    // -6
  • 相关阅读:
    遍历一个枚举类型
    ASP.NET:C#中时间格式的转换
    DataAdapter去批量更新数据的FAQ
    .Net/C#: 实现支持断点续传多线程下载的 Http Web 客户端工具类 (第2版) (C# DIY HttpWebClient) 收藏
    如何使数据库中取出的数据保持原有格式
    如何获取控制台应用程序自己的文件名
    2008将倒掉一大部分的工厂和贸易公司
    组六对半分组组合投资方案(36789)
    重又归孑然一身
    善于总结
  • 原文地址:https://www.cnblogs.com/minozMin/p/11739314.html
Copyright © 2020-2023  润新知