学习链接
数据类型转换
原始值之间
字符串转换
转换发生在输出内容的时候,也可以通过 **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
(字符串被转换为数字)。