• 【JavaScript】一文搞懂JavaScript当中晦涩难懂的类型转换


    文章参考:
    What the f*ck JavaScript?
    再谈:JavaScript 中的对象是如何进行类型转换的?

    一、类型转换在啥情况下会发生

    1、类型转换的种类
    • 隐式类型转换
    • 显示类型转换
    2、何时发生隐式类型转换?

    通常发生在比较运算(==!=><)、算术运算+-*/%)和if语句,而且运算符两边的操作数不是同一类型。

    注: 相等运算符 == 中有特殊情况,null == undefined 的比较结果返回 true。

    3、何时发生显示类型转换?

    NumberStringBoolean这都是显示转化

    二、基础数据类型转换规则

    1、转Number

    在使用 Number() toNumber() 方法将一个字符串转换为数字时,如果字符串中出现非数字字符,则会返回 NaN

    2、转String

    基础数据类型均会转为字符串

    3、转Boolean

    只有以下几种情况会转为false

    • undefined
    • null
    • +0、-0和NaN
    • “”
    • “false”

    三、引用数据类型转换规则

    1、对象属性: [Symbol.toPrimitive]

    toPrimitive(primitive:原始的),作用:转为基础数据类型。

    对象发生类型转换时,首先会检查对象上是否存在 [Symbol.toPrimitive] 属性,如果存在的话就调用。该属性存在一些限制:它必须是一个函数,而且返回值必须是基本类型,否则就要报错

    例如:

    1). 不是函数
    在这里插入图片描述

    2). 函数返回值不是基本类型
    在这里插入图片描述

    hint 值

    [Symbol.toPrimitive] 在调用时,系统会自动给与一个参数 hint,这个 hint 可以理解为,此次对象发生转换的预期类型为何。

    hint 有三种可能取值:stringnumberdefault

    下面我们来演示下,这三种情况的发生场景。
    【运行结果】
    在这里插入图片描述
    【代码片段】

    var obj = {
        [Symbol.toPrimitive](hint) {
            console.log('[Symbol.toPrimitive]', hint)
        }
    }
    undefined
    // 第一种情况(期望类型是 number):
    +obj
    // [Symbol.toPrimitive] number
    // NaN
    // 第二种情况(期望类型是 string):
    String(obj)
    // [Symbol.toPrimitive] string
    // "undefined"
    // 第三种情况(未知):
    obj + ':('
    // [Symbol.toPrimitive] default
    // "undefined:("
    obj + 1
    // [Symbol.toPrimitive] default
    // NaN
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    【结论解释】
    [Symbol.toPrimitive] 方法里我们没有定义返回值,因此方法返回值是默认的 undefined

    第一种情况:+obj -> +undefined -> Number(undefined) -> NaN。
    第二种情况:String(obj) -> String(undefined) -> “undefined”。
    第三种情况:

    • obj + ‘: (’ -> undefined + ‘: (’ -> “undefined: (”。
    • obj + 1 -> undefined + 1 -> NaN。

    然而当对象不存在 [Symbol.toPrimitive] 的时候,转换规则又是怎样的呢?
    这就关系到 Object.prototype 对象上的两个方法了:valueOf 和 toString

    2、Object.prototype.valueOf / Object.prototype.toString

    当对象上不存在 [Symbol.toPrimitive] 属性的时候,若发生类型转换,就要用到 Object.prototype 对象上的 valueOf toString 两个方法了。

    两个方法调用有先后,可能都会调用,也可以只调用一个就完成转换,返回结果。这跟方法返回值和hint值有关系。

    具体是:

    • 如果 hint 值为 ‘default’,则与 hint 为 ‘number’ 时一样对待。
    • 如果 hint 值为 ‘number’,则
      • 先调用对象的 valueOf 方法,如果方法返回的是一个基本类型值,则对象的转换结果就是这个返回值;
      • 否则,接着调用对象的 toString 方法。
    • 如果 hint 值为 ‘string’,则先调用对象的 toString 方法,
      • 如果方法返回的是一个基本类型值,则对象的转换结果就是这个返回值;
      • 否则,接着调用对象的 valueOf 方法。

    你可能要问了,如果两个方法的返回值都是对象的话,岂不是得不到对象最终的转换结果了?一点都没错,我们来试一下:

    在这里插入图片描述
    上图里,我们在对象 obj 上定义了 valueOf 和 toString 方法,在发生类型转换时,覆盖掉原型对象上的同名方法,以便我们能更加真切地感受到对象内部的实际的转换流程。

    我们制造了一个极端情况,两个方法都没有返回基本类型只而是对象,结果呢?然后控制台就报错了,告诉我们不要这样玩。

    需要说明的是——在 JavaScript 中,当我们在一个对象上调用 valueOf 方法的时候,实际上调用的是 Object.prototype.valueOf 这个原型方法。默认这个方法的返回值始终是调用对象自身,也就是说 valueOf 方法的返回的始终是对象,而非一个基本类型值。

    注:
    1、Date 对象除外,因为 Date.prototype 上定义的 valueOf 方法覆盖掉了 Object.protortype 上的。在 Date 对象上调用 valueOf方法,返回的是时间对象内部的时间戳表示。
    2、另外,Date.prototype 也定义了自己的 [Symbol.toPrimitive] 属性,默认是不可写入的。
    在这里插入图片描述

    这就得到了一个结论:对象发生到基本类型转换时,最终转换结果就是 obj.toString() 返回值!这就解释了下面代码里的输出结果:

    var obj = {}
    // (1)
    obj + ' :)' // "[object Object] :)"
    // (2)
    obj - 1 // NaN
    
    • 1
    • 2
    • 3
    • 4
    • 5

    【结果分析】
    obj + ’ : )’ -> ‘[object Object]’ + : )’ -> ‘[object Object] : )’
    obj - 1 -> ‘[object Object]’ - 1 -> Number(‘[object Object]’) - 1 -> NaN - 1 -> NaN

    3、数组的转换

    数组本质也是对象,因此上面的转换规则也适应于它。不同的是数组原型对象上定义了自己的实现方法:Array.prototype.toString,这个方法覆盖掉了 Object.prototype 上的同名方法,toString会以去拼接显示。

    在这里插入图片描述
    重写之后的 toString 方法的逻辑(类似)如下:

    Array.prototype.toString = function () { return this.join() }
    
    • 1

    因为并未覆盖 valueOf 方法(也是返回对象本身,对应到这里就是数组实例),因此数组转换结果就是调用 array.join() 的返回结果。

    下面举几个例子:

    var emptyArray = []
    var array1 = [123]
    var array2 = ['hi', 'How are you']
    
    // (1)
    Number(emptyArray) // 0
    // (2)
    array1 - 100 // 23
    // (3)
    array2 + '?' // "hi,How are you?"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    对应的转换过程如下:

    • emptyArray 首先会转换成空字符 ‘’,Numbe(‘’) 的结果就是 0,
    • array1 会转换成字符串 ‘123’,‘123’ - 100 相当于 Number(‘123’) - 100,也就是 23,
    • array2 会转换成字符串 ‘hi,How are you’,‘hi,How are you’ + ‘?’ 的结果自然是 ‘hi,How are you?’。
    4、总结:对象转换算法步骤

    总结下来,一个对象转换到基本类型的算法步骤如下:

    • 首先,检查对象上是否有 [Symbol.toPrimitive] 属性:
      • 有的话,调用此方法(属性),此方法的返回值即对象最终的转换值。
      • 没有的话,进入第二步。
    • 检查当前转换的 hint 值:
      • 如果为 ‘default’ / ‘number’:
        • 先调用对象的 valueOf 方法,如果方法返回的是一个基本类型值,则对象的转换结果就是这个返回值。
        • 否则,接着调用对象的 toString 方法。
      • 如果为 ‘string’:
        • 先调用对象的 toString 方法,如果方法返回的是一个基本类型值,则对象的转换结果就是这个返回值。
        • 否则,接着调用对象的 valueOf 方法。

    到这里的话,对象类型转换的内容就讲完了,希望能帮助到大家!

  • 相关阅读:
    Python基础内容补充
    OA系统必备的五个基本要素
    手把手教你玩转 Gitea|使用 Docker 安装 Gitea
    centos安装TDengine
    解决:使用MySQL Command Line Client时光标不显示的问题
    webpack不同环境下使用CSS分离插件mini-css-extract-plugin
    ch0-01
    测试用例设计底层逻辑
    rust编程语言(chapter 1)
    9.3.3网络原理(网络层IP)
  • 原文地址:https://blog.csdn.net/Triumph_light/article/details/133686248