重学前端学习笔记关于类型有哪些你不知道的细节

22次阅读

共计 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提供了 toSringvalueOf的转换方法。

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 参数:

  • 如果想要转换成 stringhint 值为 string,则会顺序调用toStringvalueOftoString 一定会被执行,其值如果返回基本类型时,则 return,终止运算,否则继续调用valueOf,如果过调用valueOf 没有得到基本类型,则会报 TypeError 错误;
  • 如果想要转换成 numberhint 值为 number,则会顺序调用valueOftoStringvalueOf 一定会被执行,其值如果返回基本类型时,则 return,终止运算,否则继续调用toString 方法,如果调用 toString 没有得到基本类型,则会报 TypeError 错误;
  • 如果不确定是到底要转换成 string 还是 numberhint 值为 default,则会顺序调用valueOftoStringvalueOf 一定会被执行,其值如果返回基本类型时,则 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'

正文完
 0