首先咱们来看一道面试题,问下面串代码会输出什么?
console.log([] == ![])
我先告诉你这里会输出true,接下来让我们一起来看看为什么这里会输出true吧。首先咱们先来看看稍微简单一点的原始值转换吧。
原始值转原始值 --- 显示类型转换
原始值,即JavaScript的基本数据类型,包括字符串(String)、数字(Number)、布尔(Boolean)、未定义(undefined)、空(null)和Symbol等,它们是构建复杂程序的基石。当需要将这些基本类型在彼此间转换时,我们通常采用显式转换,即明确告诉JavaScript我们想要的转换目标。
转布尔值
布尔值转换是最简单的转换之一,它常用于条件判断。使用Boolean()函数,我们可以将任意类型的值转换为布尔值,规则简单明了:
- 真实值(truthy)转换为
true,如非零数字、非空字符串、非空对象等。 - 虚假值(falsy)转换为
false,如数字0、空字符串、null、undefined、NaN等。
console.log(Boolean(0)); // false
console.log(Boolean("Hello")); // true
转数字
将其他类型转换为数字,常常使用Number()函数。这一过程相对复杂,因为它需要处理各种可能的输入:
- 字符串会被尝试解析成数字,若不能解析(如纯字母),则结果为NaN。
- 布尔值中
true转为1,false转为0。 - 特殊的undefined和null分别转换为NaN和0。
console.log(Number("123")); // 123
console.log(Number("abc")); // NaN
console.log(Number(true)); // 1
转字符串
字符串转换是最直观的,使用String()函数,任何类型的值都会被转换成字符串形式,即便是复杂的对象也会被转换成其字符串表示。
console.log(String(123)); // "123"
console.log(String(false)); // "false"
接下来咱们来聊聊今天的主角,对象的隐式转换。
对象转原始值 --- 隐式类型转换
这种转换通常是隐式的,发生在诸如数学运算、比较操作或函数调用等场景中。这里附上js官方文档,方便大家查阅相关内容Annotated ES5。
对象转Number
咱们先来看一下官方文档中的转换值和返回值。
当对象需要被当作数字使用时,JavaScript会尝试调用对象的valueOf()和toNumber()方法,按优先级顺序寻找可转换的原始值。如果这两个方法都不能提供有效的原始值,就会抛出错误。我们来看这样一串代码。咱们来看看会输出什么。
console.log(Number([]))
console.log(Number({}))
- 转number
先调用ToNumber(x),该函数中会继续调用ToPrimitive()将对象转为原始值
ToPrimitive(obj, Number)
- 判断接受的值是不是原始值类型,是则返回
- 否则,调用valueOf方法,如果得到了原始值,则返回
- 否则,调用toString方法,如果得到了原始值,则返回
- 报错
具体步骤如下,[] {} 都属于对象类型,则会触发ToPrimitive方法直到toString()得到返回值;空数组[]将会变成空字符串'' ,空对象{} 则会变成"[object Object]"字符串。继续对ToPrimitive得到的返回值字符串进行ToNumber类型转换,根据上表可知由空数组[]转换得到的空字字符串将会变成0,由空对象{}转换得到的"[object Object]"字符串会变成NaN。
[] 空数组
1. Number([])
2. let primValue = ToPrimitive([], Number)
3. toString([]) // ''
4. ToNumber('') // 0
空对象{}
1. Number({})
2. let primValue = ToPrimitive({}, Number)
3. toString({}) // "[object Object]"
4. ToNumber('') // NaN
对象转String
对象转字符串的过程类似,但优先调用的是toString(),然后才是valueOf()。这确保了对象在需要字符串表示时,能够提供合理的输出。
- 转String
先调用ToNumber(x),该函数中会继续调用ToPrimitive()将对象转为原始值
ToPrimitive(obj, String)
- 判断接受的值是不是原始值类型,是则返回
- 否则,调用toString方法,如果得到了原始值,则返回
- 否则,调用valueOf方法,如果得到了原始值,则返回
- 报错
咱们来看看下面这串代码是如何输出的。
console.log(String({}))
{} 属于对象类型,则会触发ToPrimitive方法直到toString()得到返回值;空对象{} 会变成"[object Object]"字符串。继续对ToPrimitive得到的返回值字符串进行ToString类型转换,根据上表可知,由空对象{}转换得到的"[object Object]"将保持不变。如下
1. String({})
2. let primValue = ToPrimitive({}, String)
3. toString({}) // "[object Object]"
4. ToString('') // "[object Object]"
对象转Boolean
对象转布尔值是最简单的,只需要记住转布尔值都是true就行了。
- 转布尔 任何对象转布尔都是true
看完了上面这些内容,再来看开头的面试题是不是就感觉容易多了,简直就是送分题啊。
1. [] == !true
2. [] == false
3. [] == 0
4. Number([]) == 0
5. '' == 0
6. 0 == 0
我们看右侧是一个!,明显是要进行对布尔类型的操作的,则右侧需要对空数组进行转换为布尔类型,转换得true,再去除!可得false。
若使用 === 不会触发隐式类型转换。这里涉及两种不同类型,使用 == 进行比较时,会进行类型转换:一个是对象(空数组),另一个是布尔值。
然后,需要将左边的空数组转换为数字。这通过调用 ToPrimitive 并尝试 valueOf() 和 toString() 方法实现。对于数组,valueOf() 不改变其原始值,因此会继续调用 toString(),空数组 [] 经 toString() 转换后得到空字符串 ''。
空字符串 '' 再通过 ToNumber('') 转换为数字数字 0。
因此,[] 经过一系列转换后被视为 0,与 false 经过转换后的 0 相等。所以输出是 true是不是很简单呀。
总结
总结而言,JavaScript的类型转换机制既强大又复杂,理解并灵活运用这些规则,能帮助我们编写更加健壮、易于维护的代码。无论是显式转换的直接了当,还是隐式转换的暗流涌动,都是编程艺术中不可或缺的一部分。通过掌握这些知识,你的代码将会更加清晰、高效,如同精心雕琢的宝石,熠熠生辉。