关于valueOf、toString和[Symbol.toPrimitive]方法
前言
在掘金上看到一篇文章,大意是如何让a === 1 && a === 2 && a === 3
等于true
;一开始确实一脸懵逼,不过后来想到了访问修饰符能够解决这个问题,毕竟每访问一次值就加上1
就好了;当然,我居然忘了全局变量本身就是window
的一个属性这个点,有点疏忽大意。
1 | let val = 1 |
不过,我又想到貌似对象还有一些方法能够改变访问到的值;比如:
Object.prototype.valueOf()
Object.prototype.toString()
[Symbol.toPrimitive](hint)
然而,经过折腾发现这三种方法并不像访问修饰符那样的机制,在使用全等号===
进行判断时并没有触发:
1 | let a = { |
所以,心里自然就有疑问,这几个方法到底在什么时候才会触发?
Object.prototype.valueOf()
对象的valueOf
旨在返回对象的原始值,会在需要将对象转换成原始值的地方自动执行。
根据MDN
上的资料[1]可知,内置的一些对象已经定义valueOf
方法,而对象默认则返回本身作为valueOf()
的返回值。
Object.prototype.toString()
同理,toString()
方法会返回表示该对象的字符串,会在对象预期要被转换成字符串的地方自动执行。对象默认的toString()
方法会返回[object type]
,这个type
就是对象构造函数的名称。
Symbol.toPrimitive
[Symbol.toPrimitive](hint)
方法作用同valueOf()
一样,但是优先级要高于valueOf()
;而且该方法还会接受一个参数hint
,这个参数用来表示期望转换的原始值的具体类型,有以下几种:
number
:数字类型string
:字符串类型default
:默认
对象转换成原始值
很明显,以上三种方法都是在对象被预期转换成某种原始值时触发,那么触发的时机是什么,需要何种类型的原始值就需要弄清楚了。
预期被转换成字符串类型
对应的
hint
类型为string
-
进行输出的地方,如
alert()
。 -
String(obj)
:1
2
3
4
5
6
7let a = {
toString () {
return '2'
}
}
console.log(String(a)) // 2 -
字符串连接(
+
)操作:1
2
3
4
5
6
7let a = {
toString () {
return '2'
}
}
console.log(a + 'vv') -
模板字符串:
1
2
3
4
5
6
7
8let a = {
[Symbol.toPrimitive] (hint) {
console.log(hint) // string
return 2
}
}
console.log(`你是老${a}?`) // 你是老2?
预期被转换成数字类型
对应的
hint
类型为number
-
除法:
1
2
3
4
5
6
7let a = {
valueOf () {
return 2
}
}
console.log(2 / a, a / 2) // 1 1可以使用
[Symbol.toPrimitive]()
方法来验证一下:1
2
3
4
5
6
7
8let a = {
[Symbol.toPrimitive] (hint) {
console.log(hint) // number
return 2
}
}
console.log(2 / a) // 1 -
Number(obj)
:1
2
3
4
5
6
7
8let a = {
[Symbol.toPrimitive] (hint) {
console.log(hint) // number
return 2
}
}
console.log(Number(a)) // 2 -
正负号(注意不是加减运算):
1
2
3
4
5
6
7
8
9let a = {
[Symbol.toPrimitive] (hint) {
console.log(hint) // number
return 2
}
}
console.log(+a) // 2
console.log(-a) // -2
预期被转换成默认类型(其他)
对应的
hint
类型为default
-
数字加法(即与对象相加的一方为数字类型):
1
2
3
4
5
6
7
8let a = {
[Symbol.toPrimitive] (hint) {
console.log(hint) // default
return 2
}
}
console.log(1 + a) // 3这一点有点意外,原以为像这种情况预期转换的类型应该是数字类型,但事实上却是
default
; -
布尔运算:所有对象都被转换成
true
;1
2
3
4
5
6
7
8let a = {
[Symbol.toPrimitive] (hint) {
console.log(hint) // 没有触发
return false
}
}
console.log(Boolean(a), a && 123) // true 123布尔运算包括
==
;
三种方法触发的顺序
-
首先判断对象是否有
[Symbol.toPrimitive](hint)
方法,若有则执行该方法;1
2
3
4
5
6
7
8
9
10
11
12
13
14let a = {
[Symbol.toPrimitive] (hint) {
console.log(hint)
return 'right your mother right'
},
toString () {
return 'f**k'
},
valueOf () {
return 'sb'
}
}
console.log(String(a)) // right your mother right -
如果预期被转换成字符串类型时,则优先执行
toString()
方法;1
2
3
4
5
6
7
8
9
10let a = {
toString () {
return 'f**k'
},
valueOf () {
return 'sb'
}
}
console.log(String(a)) // f**k -
如果预期被转换成默认类型或者数字类型时,则优先执行
valueOf()
方法:1
2
3
4
5
6
7
8
9
10let a = {
toString () {
return 123
},
valueOf () {
return 456
}
}
console.log(Number(a)) // 456注:若没有
valueOf()
方法,但是定义了toString()
方法,则会执行toString()
方法;1
2
3
4
5
6
7let a = {
toString () {
return 123
}
}
console.log(Number(a)) // 123