0基础进大厂,第21天——面试官:请你聊聊JS中的类型转换

65 阅读3分钟

引言

假设你在开发中写了这样一行代码:if ([]) { console.log('真') },它会输出什么? 又或者你看到这样的比较:[] == , String()

  • 隐式转换:JS引擎在运行时自动进行的转换(如==, +运算)

核心

理解JS如何在不同场景下自动转换类型

显式类型转换

转布尔值:Boolean()

Boolean('')       // false
Boolean(0)        // false
Boolean(null)     // false
Boolean(undefined) // false
Boolean(NaN)      // false
Boolean(false)    // false
// 其他所有值都转为true

转数字:Number()

Number('123')     // 123
Number('123abc')  // NaN
Number('')        // 0
Number(true)      // 1
Number(false)     // 0
Number(null)      // 0
Number(undefined) // NaN

转字符串:String()

String(123)       // '123'
String(true)      // 'true'
String(null)      // 'null'
String(undefined) // 'undefined'
String({})        // '[object Object]'

隐式类型转换

原始类型间的转换

'123' == 123      // true (字符串转数字)
1 + '2'           // '12' (数字转字符串)

引用类型转原始类型

转布尔:所有对象都是true

Boolean([])       // true
Boolean({})       // true

转字符串:调用toString()

[1,2,3] + ''      // '1,2,3'
({}) + ''         // '[object Object]'

转数字:先调用valueOf(),再调用toString()

+[]               // 0 ([] -> '' -> 0)
+[1]              // 1 ([1] -> '1' -> 1)
+[1,2]            // NaN ([1,2] -> '1,2' -> NaN)

类型转换的底层机制

ToPrimitive(obj, preferredType)

JS引擎转换引用类型的核心算法:

  1. 如果preferredTypeString
    • 先调用obj.toString()
    • 如果不是原始值,再调用obj.valueOf()
  2. 如果preferredTypeNumber
    • 先调用obj.valueOf()
    • 如果不是原始值,再调用obj.toString()

valueOf()和toString()

// 默认valueOf()返回对象本身
({}).valueOf()    // {}

// 数组重写了toString()
[1,2].toString()  // '1,2'

// 对象默认toString()
({}).toString()   // '[object Object]'

== 和 === 的区别

== (宽松相等)

会进行隐式类型转换

'123' == 123      // true
[] == false       // true
null == undefined // true

=== (严格相等)

不进行类型转换

'123' === 123     // false
[] === false      // false
null === undefined // false

发生隐式转换的常见场景

四则运算

1 + '2'           // '12'
'3' * '4'         // 12 (字符串转数字)
[1] + [2]         // '12' (数组转字符串拼接)

判断语句

if ({}) {         // 对象转布尔总是true
  console.log('真')
}

比较运算

[] == 0           // true
[] == ''          // true
[] == []          // false (引用类型比较地址)

+ 运算符的特殊性

一元运算符:转数字

+'123'            // 123
+[]               // 0
+{}               // NaN

二元运算符:可能转字符串

1 + 2             // 3
1 + '2'           // '12'
[] + {}           // '[object Object]'

经典面试题解析

题目1:[] ==

  1. [] == false // true
  2. false转数字为0 // [] == 0
  3. []转字符串为'' // '' == 0
  4. ''转数字为0 // 0 == 0
  5. 结果:true

### 题目2:{} + []
```javascript
// 解析步骤:
1. {}被解析为代码块 // 相当于 +[]
2. +[]            // 0 (数组转数字)
3. 结果:0

题目3:[] + {}

// 解析步骤:
1. []转字符串为''  // '' + {}
2. {}转字符串为'[object Object]'
3. 结果:'[object Object]'

面试官的鼻子怎么牵?

假设面试官问:JS中如何判断两个值是否相等?

可以这么回答: "在JS中,我们有==和===两种比较方式。==会进行隐式类型转换,比如'1' == 1是true;而===不会转换类型,要求类型和值都相等。"

"在比较对象时,==和===都是比较引用地址。如果想深度比较对象,可以递归比较每个属性,或者使用JSON.stringify()转换为字符串比较..."

"在类型转换方面,JS有一套复杂的转换规则。比如在+运算中,只要有一个操作数是字符串,就会进行字符串拼接;而其他算术运算符通常会把操作数转为数字..."

你都这么聊了,面试官当然要考考你:`[] == 后toString(),最终转数字 | | 字符串 | 对应字符串形式 | 调用toString() |

手写类型检查函数

// 判断是否为原始类型
function isPrimitive(val) {
  return val === null || 
    (typeof val !== 'object' && typeof val !== 'function')
}

// 模拟ToPrimitive转换
function toPrimitive(obj, preferredType) {
  if (isPrimitive(obj)) return obj
  
  preferredType = preferredType || 'default'
  
  if (preferredType === 'string' || preferredType === 'default') {
    const toString = obj.toString
    if (typeof toString === 'function') {
      const res = toString.call(obj)
      if (isPrimitive(res)) return res
    }
    
    const valueOf = obj.valueOf
    if (typeof valueOf === 'function') {
      const res = valueOf.call(obj)
      if (isPrimitive(res)) return res
    }
  } 
  else if (preferredType === 'number' || preferredType === 'default') {
    const valueOf = obj.valueOf
    if (typeof valueOf === 'function') {
      const res = valueOf.call(obj)
      if (isPrimitive(res)) return res
    }
    
    const toString = obj.toString
    if (typeof toString === 'function') {
      const res = toString.call(obj)
      if (isPrimitive(res)) return res
    }
  }
  
  throw new TypeError('Cannot convert object to primitive value')
}

// 测试
console.log(toPrimitive([]))          // ''
console.log(toPrimitive([1,2], 'number')) // '1,2'
console.log(toPrimitive({}, 'string'))    // '[object Object]'

现在,你已经掌握了JS类型转换的精髓,下次遇到这类问题时就能从容应对了!