js类型转换

452 阅读2分钟

这是我参与8月更文挑战的第二天,活动详情查看:8月更文挑战

前言

如果对js的类型做简化,可以分为两种类型,分别是引用类型值类型ObjectFunction引用类型,其他是值类型

正文

从值类型到引用类型

可以通过Object()或者new Object()

当以非构造函数形式被调用时,Object 的行为等同于 new Object()。 MDN解释如下:

  • 如果给定值是 null 或 undefined,将会创建并返回一个空对象
  • 如果传进去的是一个基本类型的值,则会构造其包装类型的对象
  • 如果传进去的是引用类型的值,仍然会返回这个值,经他们复制的变量保有和源对象相同的引用地址

根据上述我们可以得出以下结论:

 let o = Object(1)
 // 结果为true
 console.log(Object.getPrototypeOf(o) == Number.prototype)
 
 let a = {}
 o = Object(a)
 // 结果为 true
 console.log(Object.getPrototypeof(o) == a)

可以看出来,从值到引用的转换比较简单,只需通过Object就可以得到预期的引用对象。

引用类型到值类型

引用转换为值,涉及到三个函数,分别是toStringvalueOf[Symbol.toPrimitive]

toString

返回一个表示该对象的字符串

当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用

valueOf

返回指定对象的原始值

JavaScript调用valueOf方法将对象转换为原始值。你很少需要自己调用valueOf方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。 常见的返回值

  • Array 返回数组对象本身
  • Object 对象本身
  • Function 函数本身
  • String 字符串 如果对象没有原始值,则valueOf将返回对象本身

Symbol.toPrimitive

Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。

该函数被调用时,会被传递一个字符串参数 hint ,表示要转换到的原始值的预期类型。 hint 参数的取值是 "number""string" 和 "default" 中的任意一个 返回值是原始数据, (stringnumberbigintbooleannullundefinedsymbol)

举个例子

const object = {
  [Symbol.toPrimitive](hint) {
    console.log('hint', hint)
    if (hint === 'number') {
      return 0;
    } else if (hint === 'string') { 
      return 'string'
    } else {
      return '1'
    }
  }
};

// 输出hint为default
console.log(object + 2);
// 输出hint为number
console.log(object - 2)
// 输出hint为string
console.log(`${obj}`)
};

过程

变量x在运算时的转换过程

  1. 如果x是原始值,直接返回x
  2. 如果x是对象(Number, String, Boolean),则通过valueOf返回原始值
  3. 如果对象没有原始值,就需要进行类型转换,在js中,如果一个运算无法确定类型,那么在类型转换前,它的运算数将被预设为number,对应上述toPrimitive的hint。当hint是number时,优先调用valueOf方法;否则优先调用toString。并且,当先调用的方法得不到非对象值时,还会调用另一个方法。 举一些例子
const toString = Number.prototype.toString
const valueOf = Number.prototype.valueOf
Number.prototype.toString = function() {
    console.log('toString')
    return toString.call(this)
}

Number.prototype.valueOf = function() {
    console.log('valueOf')
    return valueOf.call(this)
}
let o = Object(Number(1))
o[Symbol.toPrimitive]= function(hint) {
    console.log('toPrimitive', hint)
}

输出结果
执行${o}时,预期的hint是string, 所以优先执行toString

// 打印 valueOf
console.log(o - 2)
// 打印 valueOf
console.log(o + 2)
// 打印 toString
console.log(`${o}`)

改成Object

const toString = Object.prototype.toString
const valueOf = Object.prototype.valueOf
Object.prototype.toString = function() {
    console.log('toString')
    return toString.call(this)
}

Object.prototype.valueOf = function() {
    console.log('valueOf')
    return valueOf.call(this)
}
let o = {a: 2}

结果如下
前两项预期都是number,所以先执行valueOf, 但是返回的是对象,所有再执行toString

// 先打印valueOf
// 再打印toString()
console.log(+o)
// 先打印valueOf
// 再打印toString()
console.log(o - 2)
// 打印toString()
console.log(`${o}`)

如果在上述的对象加上 Symbol.toPrimitive,会发现valueOftoString不会再执行,这是因为默认的Symbol.toPrimitive会根据hint去调用valueOftoString,被重写之后,自然不会再调用。

o[Symbol.toPrimitive]= function(hint) {
    console.log('toPrimitive', hint)
}

上面讲的就是js的类型转换过程,通过这些知识就可以理解某些情况下,x == x 不为true的原理了。

参考