• js中隐式类型转换与toPrimitive


    前言

    我们知道Js的隐式类型转换主要出现在有运算符的情况下【逻辑运算符、关系运算符、算术运算符】。那么在接触toPrimitive之前,我们需要先知道其他值到某个类型值的转换规则。

    其他值到数值的转换规则

    Boolean: true — 1 false — 0
    Null:0
    Undefined:NaN
    String:若仅含数字,则结果跟用Number()强制转换一致,若存在非数字字符,则返回NaN
    Symbol不能转换,会报错。

    对象:会先转换成其对应的原始类型值【也就是基本类型值,像undefined就是基本数据类型,它的值也是Undefined,就是基本类型值】,如果返回的是非数字的原始类型值,就根据上述规则进行类型转换。

    将对象转换成对应的原始类型值,就是进行toPrimitive抽象操作,他会根据传进来的对象以及目标类型,在内部调用不同方法(这里后文会细说),在对于目前对象到数值的转换中,它会优先调用valueOf(),返回结果如果是基本类型值,则直接使用,否则会继续调用toString()。如果这两个方法都不返回原始类型值,则会抛出TypeError。

    其他值到字符串的转换规则

    null:“null”
    undefined: “undefined”
    Boolean:true — “true” false — “false”。
    Number :直接转换,极小和极大的数字会使用指数形式。
    Symbol :只允许显式强制类型转换,使用隐式强制类型转换会产生错误。

    const symbol2 = Symbol(42);
    const symbol3 = Symbol('foo');
    
    console.log(String(symbol2));
    // Expected output: false
    
    console.log(symbol3.toString());
    // Expected output: "Symbol(foo)"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对象:也是一样会先转换成其对应的原始类型值,再根据上述规则进行类型转换。不过在这里toPrimitive抽象操作会变成优先调用toString(),再降级到valueOf()。那么我们知道,除非该对象自定义了 toString() 方法,否则调用 toString()一定会是会沿着原型链调用到原型的toString()(Object.prototype.toString())来返回内部属性 [[Class]] 的值。

    let obj ={
      toString:function(){
        return 'a'
      }
    }
    console.log(obj=='a')//true
    let obj1 ={}
    console.log(obj1=='a')//false
    //上述过程说明是调用了toString()
    let obj1 ={
      toString:function(){
        return 1
      }
    }
    console.log(obj1=='1')//true
    //上述过程说明是拿到对应的原始类型值,再根据规则进行类型转换
    let obj2 ={
      toString:function(){
        return {}
      },
      valueOf:()=>{
      	return 1
      }
    }
    console.log(obj2=='1')//true
    //上述过程说明是优先调用toString(),再降级到valueOf()去拿到对应的原始类型值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    其他值到布尔值的转换规则

    假值: undefined null false +0 -0 NaN “”

    基本类型值在不同操作符的情况下隐式转换的规则

    1. +运算符的两边有至少一个string类型时,两边都会被隐式转换为字符串;其他情况下两边都会被转换为数字
    2. 其他算术运算符会将两边转成Number类型
    3. ==比较复杂

    如果任一操作数是布尔值,则将其转化为数值再比较是否相等。false转化为0,true转化为1.
    如果一个操作数是字符串,另一个操作数是数值,则会尝试将字符串转化为数值,再比较是否相等。
    如果一个操作数是对象,另一个不是,则获取其原始值,再根据前面的规则进行比较。
    在比较时,有如下特殊情况,那就是存在undefined与null的情况:
    null和undefined相等
    null和undefined不能转化为其他类型的值再进行比较。
    (那就是如果一边存在null和undefined,另一边若不是null或undefined,直接返回false)
    若有任一操作数是NaN,则直接返回false。(另外,NaN是Number类型,但是是所有数据类型中唯一与自身不全等的数。在Object.is(NaN,NaN)中才会返回true。)

    1. 关系运算符转Number
    2. 逻辑运算符转布尔

    若两个操作数都是对象,则比较他们指向的是不是同一个对象。如果两个操作数都指向同一个对象,则会返回true,否则返回false。

    toPrimitive是何方神圣

    根据前面的规律,我们发现总有到对象的转换才需要用到toPrimitive。
    然后它的本质上是:

    ToPrimitive(input, PreferredType)

    input是调用的对象,PreferredType是期望返回的结果类型

    a:判断input是否是对象,如果不是直接return input

    b:根据PreferredType定义hint,hint的取值一共有三种情况:‘default’(PreferredType为空), ‘number’(PreferredType是number), ‘string’(PreferredType是string)

    c:判断该对象是否有Symbol.toPrimitive方法, 如果有则传入参数hint进行调用,若执行结果是基本类型,则return该结果,否则抛出TypeError。
    d:如果hint是’default’,则改为’number’。也就是说默认是按’number’。除了Date对象,和Symbol对象。Date对象转换时hint参数是String。Symbol对象进行类型转换会抛出异常。
    e: 执行OrdinaryToPrimitive运算 如果hint是’string’,调用toString方法,得到的是原始数据类型,返回该值;否则调用valueOf方法,如果得到的是原始数据类型,返回该值;否则抛出异常 如果hint是’number’,则相反;先调用valueOf方法,得到的是原始数据类型,返回该值;否则调用toString方法,如果得到的是原始数据类型,返回该值;否则抛出异常

    js中加法操作,参数PreferredType一般是Default

    +正号操作,参数PreferredType一般是Number

    valueOf和toString的返回值

    (1)对于Object
    valueOf:返回对象本身
    toString:返回的是内部属性"[object type]"字符串。

    (2)对于Array
    valueOf:返回对象本身
    toString: 相当于用数组值调用join(‘,’)所返回的字符串。例如:[1,2,3].toString()会是"1,2,3"

    let arr=[1,2,3]
    let arr1=[]
    console.log(arr.toString())//"1,2,3"
    console.log(arr.valueOf())//[1, 2, 3]
    console.log(arr1.toString())//""
    console.log(arr1.valueOf())//[]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    参考:聊聊JS中的ToPrimitive原始值转换方法

  • 相关阅读:
    【Pytorch基础知识】数据的归一化和反归一化
    SAP PI PO 接口常见问题处理:队列平衡的统计平均分配
    《深度探索C++对象模型》阅读笔记 第五章 构造、解构、拷贝语意学
    Pythonnumpy多维数组
    怎么将音频合并到一起?安利音频合并的3个方法
    uniapp分包
    链游:未来游戏发展的新风向
    MyBatis-Plus(2.0)
    【Android】Android应用性能优化总结
    [NLP] LLM---<训练中文LLama2(二)>扩充LLama2词表构建中文tokenization
  • 原文地址:https://blog.csdn.net/weixin_53936072/article/details/133906464