共计 2861 个字符,预计需要花费 8 分钟才能阅读完成。
在学习《关于类型,有哪些你不知道的细节》一章节中,讲到了 javascript
内置了 Symbol.toPrimitive
属性,可自定义类型转换操作。
typeof
我们最常使用 typeof
来进行类型检测,对于基本类型使用 typeof
能得到我们想要的结果
console.log(typeof '123') //'string'
console.log(typeof 123) //'number'
console.log(typeof undefined) //'undefined'
console.log(typeof true) //'boolean'
console.log(typeof Symbol('foo')) //'symbol'
对于复杂类型使用 typeof
进行判断,就不是我们想要的结果了。
console.log(typeof {a: 1}) //'object'
console.log(typeof new Date()) //'object'
console.log(typeof [1,3,4]) //'object'
console.log(typeof new String('123')) //'object'
console.log(typeof new RegExp()) //'object'
显然我们对 new Date()
进行类型判断时,想要得到是 Date
;对数组进行类型判断时,想要得到Array
,但是用typeof
进行判断时,得到的结果都是 object
,所以对复杂类型使用typeof
结果都是object
。
这里有两个特殊的值,对他们使用 typeof
进行类型检测,和你想象的结果会不一样
console.log(typeof null) //'object'
console.log(typeof function fn(){}) //'function'
虽然 null
是基本类型,使用 typeof
进行类型检测时,结果却是 object
;而function
是复杂类型,使用 typeof
进行类型检测却是function
。
当你类型不确定时,要谨慎使用 typeof
进行类型检测。
toString
使用 typeof
无法准确的判断出想要的类型,这里就可以使用 toString
方法进行判断
let type = {}.toString
console.log(type.call('123')) //'[object String]'
console.log(type.call(123)) //'[object Number]'
console.log(type.call(true)) //'[object Boolean]'
console.log(type.call(undefined)) //'[object Undefined]'
console.log(type.call(Symbol('foo'))) //'[object Symbol]'
console.log(type.call(null)) //'[object Null]'
console.log(type.call(function fn() {})) //'[object Function]'
console.log(type.call({a: 11})) //'[object Object]'
console.log(type.call(new Date)) //'[object Date]'
console.log(type.call(new RegExp)) //'[object RegExp]'
console.log(type.call([1,2,3])) //'[object Array]'
使用 toString
可以很好的解决了类型判断的问题,从上面例子中可以看出无论是基本类型,还是复杂类型都能准确的判断出来。
这里 ({}.toString).call(123)
等同于Object.prototype.toString.call(123)
Symbol.toPrimitive
前面讲解了如何进行类型检测,在类型检测时,实际上是发生了类型转换,那么类型转换在 javascript
内部是如何实现的呢?
将对象转换到基本类型时,javascript
提供了 toSring
和valueOf
的转换方法。
let o = {valueOf: () => {console.log('valueOf'); },
toString: () => {console.log('toString');}
}
String(o) //'toString'
Number(o) //'valueOf'
将对象转换成 string
类型时,会调用 toString
方法;将对象转换成 number
时,会调用 valueOf
方法。
如果在调用 toString
时没有得到基本类型值时,则会调用 valueOf
,再调用valueOf
时也没有得到基本类型,则会报 TypeError
错误
同理在调用 valueOf
时也是如此。
那么为什么在转换成 string
时会先调用 toString
方法,而转换成 number
时会先调用 valueOf
方法呢?
这是因为 javascript
内置了一个 Symbol.toPrimitive
的属性。当对象被要被转换为基本类型时,会调用 Symbol.toPrimitive
,同时会传入一个hint
参数:
- 如果想要转换成
string
,hint
值为string
,则会顺序调用toString
、valueOf
,toString
一定会被执行,其值如果返回基本类型时,则return
,终止运算,否则继续调用valueOf
,如果过调用valueOf
没有得到基本类型,则会报TypeError
错误; - 如果想要转换成
number
,hint
值为number
,则会顺序调用valueOf
、toString
,valueOf
一定会被执行,其值如果返回基本类型时,则return
,终止运算,否则继续调用toString
方法,如果调用toString
没有得到基本类型,则会报TypeError
错误; - 如果不确定是到底要转换成
string
还是number
,hint
值为default
,则会顺序调用valueOf
、toString
,valueOf
一定会被执行,其值如果返回基本类型时,则return
,终止运算,否则继续调用toString
方法,如果调用toString
没有得到基本类型,则会报TypeError
错误。
如何用户显示指定了 Symbol.toPrimitive
方法,这会直接调用用户定义的 Symbol.toPrimitive
方法:
let o = {[Symbol.toPrimitive](hint){console.log(`hint: ${hint}`)
},
valueOf: () => {console.log('valueOf'); }, // 未执行
toString: () => {console.log('toString');} // 未执行
}
String(o) //'hint: string'
Number(o) //'hint: number'
o + ''//'hint: default'