对象-原始类型转换-Object基础
本文需要先了解Symbol
概述
- 所有对象在布尔上下文中均为true。所以,对象不存在布尔值转换问题。只有字符串和数值转换。
- 数值转换:对象相减
-
或者应用数学函数时。例如两个Date对象相减。 - 字符串转换,发生在期望需要字符串的上下文时,例如alert函数。
转换变体
在转换时,hint用来指代具体哪种原始值的上下文。
下面是三个类型转换的变体,hint值分别为:
"string"
。对象到字符串的转换,当我们对期望一个字符串的对象执行操作时.例如alert方法"number"
。对象到数字的转换,例如进行数学运算(二元加法除外)时。"default"
。只在少数情况下发生。当不能仅仅由运算符确定期望的类型时。例如,二原加运算符在有字符串参与运算时,可以连接字符串,在数学运算时,可以执行加法。此时hint就是default。还有一种情况是,对象使用==
与字符串,数字,或者symbol比较时。也是用default的hint。
大于,小于虽然也算不确定情况(既可以比较字符串也可以比较数字),但是由于历史原因,对象参与这两个运算时的hint是"number"。
三个对象方法
上面说的hint还要看对象有没有对应的方法:
- 若存在
实例对象[Symbol.toPrimitive](hint)
方法,则根据上面的描述决定传入的hint是"string", "number"还是"default"
- 不存在上面的1的方法,且hint是
"string"
,则尝试实例对象.toString()
和实例对象.valueOf()
- 不存在上面的1的方法,且hint是
number或者default
,尝试实例对象.valueOf()
和实例对象.toString()
Symbol.toPrimitive
此内建symbol用来给转换方法命名:
let obj = {age:999,name:'路由器'};
obj + 10; =>'[object Object]10'
obj[Symbol.toPrimitive] = function(hint) {
console.log(hint);
return hint === 'string' ? this.name : this.age;
}
obj + 10; => hint:number
obj + "10"; => hint:default
+obj; => hint:default
obj > 9; => hint:number
obj <= 1000; => hint:number
可以看出,obj[Symbol.toPrimitive]
根据转换的上下文的不同,obj可以是字符串,也可以是数值。而且一般做法是将default 和 number
视作同一个情况。
此
Symbol.toPrimitive
方法必须返回原始值(number, string,boolean,null, undefined
),否则在进行类型转换时会报错!!!
toString&valueOf
还没有symbol的时候就有了这两个方法,用来实现转换。
就像上面说的,没有Symbol.toPrimitive
时,JavaScript尝试使用这两个方法。
顺序是:
- 对于
"string"
的hint
,toString优先于valueOf - 其他情况,valueOf优先于toString
默认情况下,普通对象具有toString
和valueOf
方法:
toString
方法默认返回一个字符串"[object Object]"valueOf
方法默认返回对象本身
测试执行顺序:
let obj = {
toString() {
return "2";
}
};
obj * 4 => 8
这是number上下文,首先寻找valueOf找不到,toString找到了,就回去执行toString,得到字符串,然后尝试string到number转换。
在期望需要字符串的上下文时:
let user = {name: "John"};
alert(user); // [object Object]
alert(user.valueOf() === user); // true
所以,如果我们尝试将一个对象当做字符串来使用,例如在 alert 中,那么在默认情况下我们会
看到 [object Object]
。
自己实现这两个方法时,需要注意:
必须返回原始值,否则对象在需要进行类型转换时,你的非原始类型的返回值变得毫无意义。
现在,实现一个同前面的obj[Symbol.toPrimitive]
一样功能的方法:
let obj2 = {
age: 666,
desp: '描述',
}
obj2.toString = function () {
return this.desp;
}
obj2.valueOf = function () {
return this.age;
}
多重转换
- 对象被转换为原始值(通过前面我们描述的规则)。
- 如果生成的原始值的类型不正确,则继续进行转换。
总结
通常对于内建对象, "default" hint 的处理方式与 "number" 相同,因此在实践中,最后两个 hint 常常合并在一起。
转换算法:
- 调用
obj[Symbol.toPrimitive](hint)
如果这个方法存在, - 否则,如果 hint 是 "string"
- 先尝试
obj.toString()
,如果toString的返回值为原始类型,则得到此值就此结束,如果返回值为其他类型,则返回对象本身。 - 没有
obj.toString()
则去调用valueOf,如果valueOf的返回值为原始类型,则得到此值就此结束,如果返回值为其他类型,则返回对象本身
- 先尝试
- 否则,如果hint是number,则先尝试valueOf,再尝试toString。
在实践中,为了便于进行日志记录或调试,对于所有能够返回一种“可读性好”的对象的表达形式的
转换,只实现以 obj.toString() 作为全能转换的方法就够了