学习链接
数据类型转换
原始值之间
字符串转换
转换发生在输出内容的时候,也可以通过 **String(value)** 进行显式转换。原始类型值的 string 类型转换通常是很明显的。
数字型转换
转换发生在进行算术操作时,也可以通过 **Number(value)** 进行显式转换。
数字型转换遵循以下规则:
| 值 | 变成…… |
|---|---|
undefined |
NaN |
null |
0 |
true / false |
1 / 0 |
string |
“按原样读取”字符串,两端的空格会被忽略。空字符串变成 0。转换出错则输出 NaN。(是用 Number而非 parseInt) |
布尔型转换
转换发生在进行逻辑操作时,也可以通过 **Boolean(value)** 进行显式转换。
布尔型转换遵循以下规则:
| 值 | 变成👇 |
|---|---|
0, null, undefined, NaN, "" |
false |
| 其他值 | true |
值得注意的例子
- 对
undefined进行数字型转换时,输出结果为NaN,而非0。 - 对
"0"和只有空格的字符串(比如:" ")进行布尔型转换时,输出结果为true。
对象转原始值
转换类型
- 没有转换为布尔值。所有的对象在布尔上下文(context)中均为
**true**。只有字符串和数字转换。 - 数字转换,例如数学运算
- 字符串转换,例如
alert()的参数期望是字符串
hint
JavaScript 类型转换在各种情况下有三种变体。它们被称为 “hint”。
"string":对象到字符串的转换"number":对象到数字的转换"default":在少数情况下发生,当运算符“不确定”期望值的类型时。- 二元加法
**+**可用于字符串连接,也可以用于数字相加,这时到底应该进行哪种转换不是很明确。
将依据"default"hint 来对其进行转换。 - 如果对象被用于与字符串、数字或 symbol 进行
**==**比较,这时到底应该进行哪种转换不是很明确。
将依据"default"hint 来对其进行转换。 - 像
<和>这样的小于/大于比较运算符,也可以同时用于字符串和数字。
不过,它们使用"number"hint,而不是"default"。这是历史原因。
- 二元加法
除了一种情况(Date 对象)之外,所有内建对象都以和 **"number"** 相同的方式实现 **"default"** 转换。
Date 对象以和 "string" 相同的方式实现 "default" 转换。
转换算法
对象到原始值的转换,是由许多期望以原始值作为值的内建函数和运算符自动调用的。
- 先调用
obj[Symbol.toPrimitive](hint),如果这个方法存在的话。 - 否则,如果 hint 是
**"string"**
按序尝试调用obj.toString()或obj.valueOf()。 - 否则,如果 hint 是
**"number"**或**"default"**
按序尝试调用obj.valueOf()或obj.toString()。
所有这些方法都必须返回一个原始值才能工作(如果已定义)。
Symbol.toPrimitive
名为 Symbol.toPrimitive 的内建 symbol,被用来给转换方法命名
obj[Symbol.toPrimitive] = function(hint) {// 这里是将此对象转换为原始值的代码// 它必须返回一个原始值 否则会报错// hint = "string"、"number" 或 "default" 中的一个}
如果 Symbol.toPrimitive 方法存在,则它会被用于所有 hint,无需更多其他方法。
toString/valueOf
如果没有 Symbol.toPrimitive,那么 JavaScript 将尝试寻找 toString 和 valueOf 方法:
- 对于
**"string"**hint:先调用toString方法,如果它不存在,则调用valueOf方法
(因此,对于字符串转换,优先调用toString) - 对于其他 hint:先调用
valueOf方法,如果它不存在,则调用toString方法
(因此,对于数学运算,优先调用valueOf方法)
自定义这些方法时,应该返回一个原始值(string/boolean/number)。
如果 toString 或 valueOf 返回了一个对象,那么返回值会被忽略,但是方法依旧会被执行。
如果 toString 或 valueOf 返回了一个 Symbol,直接报错。
默认情况下,普通对象具有 toString 和 valueOf 方法:
toString方法返回一个字符串"[object Object]"。valueOf方法返回对象自身。
测试用例
const obj = {};obj.toString = () => {console.log('toString');// return {};return 'toString'}obj.valueOf = () => {console.log('valueOf');// return {};// return Symbol();return 'valueOf';}// obj[Symbol.toPrimitive] = (hint) => {// console.log('Symbol.toPrimitive');// console.log('hint: ', hint);// // return {};// return 'Symbol.toPrimitive';// }console.log('obj + ' + obj);
转换可以返回任何原始类型
关于所有原始转换方法,有一个重要的点需要知道,就是它们不一定会返回 “hint” 的原始值。
没有限制 toString() 是否返回字符串,或 Symbol.toPrimitive 方法是否为 "number" hint 返回数字。
唯一强制性的事情是:这些方法必须返回一个原始值(string/boolean/number),而不是对象。
历史原因
由于历史原因,如果
toString或valueOf返回一个对象,则不会出现 error,但是这种值会被忽略(就像这种方法根本不存在)。这是因为在 JavaScript 语言发展初期,没有很好的 “error” 的概念。相反,
Symbol.toPrimitive更严格,它 必须 返回一个原始值,否则就会出现 error。
进一步的转换
许多运算符和函数执行类型转换,例如乘法 * 将操作数转换为数字。
如果我们将对象作为参数传递,则会出现两个运算阶段:
- 对象被转换为原始值(通过前面我们描述的规则)。
- 如果还需要进一步计算,则生成的原始值会被进一步转换。
例如:
let obj = {// toString 在没有其他方法的情况下处理所有转换toString() {return "2";}};alert(obj * 2); // 4,对象被转换为原始值字符串 "2",之后它被乘法转换为数字 2。
- 乘法
obj * 2首先将对象转换为原始值(字符串 “2”)。 - 之后
"2" * 2变为2 * 2(字符串被转换为数字)。
