{T}

类型检测与转换

typeof 操作符

typeof 是 JavaScript 中最基本的类型检测操作符,用于确定任意变量的数据类型。

返回值表

typeof 操作返回值说明
typeof undefined"undefined"未定义
typeof true"boolean"布尔值
typeof 123"number"数值
typeof "hello""string"字符串
typeof Symbol()"symbol"符号
typeof 123n"bigint"大整数
typeof null"object"⚠️ 历史遗留 bug
typeof {}"object"对象
typeof []"object"数组(对象的一种)
typeof function(){}"function"函数

使用示例

javascript
let message = "some string"
let count = 95
let isActive = true
let person = null
let age
let sym = Symbol("id")
let bigInt = 123n

console.log(typeof message) // "string"
console.log(typeof count) // "number"
console.log(typeof isActive) // "boolean"
console.log(typeof person) // "object"(注意:这是 JavaScript 的一个 bug)
console.log(typeof age) // "undefined"
console.log(typeof sym) // "symbol"
console.log(typeof bigInt) // "bigint"
console.log(typeof function () {}) // "function"
console.log(typeof []) // "object"
console.log(typeof {}) // "object"

typeof 的语法

javascript
// 作为操作符使用(推荐)
typeof variable

// 也可以使用括号(但这不是函数调用)
typeof variable

// 示例
typeof 42 // "number"
typeof 42 // "number"(括号只是分组,不是函数调用)

⚠️ typeof 的局限性 1. typeof null 返回 "object":这是 JavaScript 的一个历史 bug,源于早期实现 2. 无法区分数组和对象typeof [] 返回 "object" 3. 无法识别具体对象类型typeof new Date() 返回 "object" 4. typeof NaN 返回 "number":虽然 NaN 表示"不是数字" 对于精确的类型检测,请参考类型检测方法章节。

类型检测方法

JavaScript 提供了多种类型检测方法,各有优缺点。

类型检测方法对比表

方法优点缺点适用场景
typeof简单快速null 返回 "object",无法区分对象类型基本类型检测
instanceof可检测具体对象类型只能检测对象,跨 iframe 失效检测对象实例
Object.prototype.toString最准确较为繁琐精确类型检测
Array.isArray专门检测数组只能检测数组数组检测
Object.hasOwn()安全检测自身属性只能检测属性存在性属性检测(ES2022)
constructor简单可被修改简单类型检测

1. typeof 操作符

javascript
// 基本类型
typeof "string" // "string"
typeof 123 // "number"
typeof true // "boolean"
typeof undefined // "undefined"
typeof Symbol() // "symbol"
typeof 123n // "bigint"

// 对象类型
typeof null // "object"(bug)
typeof [] // "object"
typeof {} // "object"
typeof function () {} // "function"
typeof new Date() // "object"
typeof new RegExp() // "object"

2. instanceof 操作符

用于检测对象是否是某个构造函数的实例。

javascript
// 基本用法
[] instanceof Array // true
{} instanceof Object // true
new Date() instanceof Date // true
new Date() instanceof Object // true(原型链)

// 原始类型
123 instanceof Number // false
"hello" instanceof String // false

// 局限性:跨 iframe 或 window 时可能失效
// const iframe = document.createElement("iframe")
// document.body.appendChild(iframe)
// const iframeArray = iframe.contentWindow.Array
// console.log([] instanceof iframeArray) // false

3. Object.prototype.toString()

最可靠的类型检测方法,返回 "[object Type]" 格式的字符串。

javascript
// 基本类型
Object.prototype.toString.call("string") // "[object String]"
Object.prototype.toString.call(123) // "[object Number]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(Symbol()) // "[object Symbol]"
Object.prototype.toString.call(123n) // "[object BigInt]"

// 对象类型
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(new Date()) // "[object Date]"
Object.prototype.toString.call(/regex/) // "[object RegExp]"
Object.prototype.toString.call(new Error()) // "[object Error]"
Object.prototype.toString.call(function () {}) // "[object Function]"

// 封装为工具函数
function getType(value) {
  return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
}

getType("string") // "string"
getType([]) // "array"
getType(null) // "null"
getType(undefined) // "undefined"
getType(new Date()) // "date"

4. Array.isArray()

专门用于检测数组,是最推荐的数组检测方法。

javascript
Array.isArray([]) // true
Array.isArray([1, 2, 3]) // true
Array.isArray(new Array()) // true
Array.isArray({}) // false
Array.isArray("array") // false
Array.isArray(null) // false
Array.isArray(undefined) // false

5. Object.hasOwn()(ES2022)

Object.hasOwn() 是 ES2022 新增的静态方法,用于安全地检测对象是否具有指定的自身属性,是 Object.prototype.hasOwnProperty() 的替代方案。

javascript
const obj = { name: "John", age: 30 }

Object.hasOwn(obj, "name") // true
Object.hasOwn(obj, "toString") // false(继承属性)
Object.hasOwn(obj, "age") // true

const obj2 = Object.create(null)
obj2.key = "value"
Object.hasOwn(obj2, "key") // true
obj2.hasOwnProperty("key") // TypeError: obj2.hasOwnProperty is not a function

Object.hasOwn(
  {},
  "toString"
)(
  // false
  {}
).hasOwnProperty("toString") // false

hasOwnProperty 的区别:

特性Object.hasOwn()hasOwnProperty()
调用方式静态方法实例方法
Object.create(null)✅ 安全❌ TypeError
属性被覆盖✅ 不受影响❌ 可能被覆盖
推荐程度✅ 推荐⚠️ 旧代码兼容

6. 其他专用检测方法

javascript
// 检测 NaN
isNaN(NaN) // true
isNaN("NaN") // true(会转换)
Number.isNaN(NaN) // true(更严格,不会转换)
Number.isNaN("NaN") // false

// 检测有限数
isFinite(123) // true
isFinite("123") // true(会转换)
Number.isFinite(123) // true(更严格)
Number.isFinite("123") // false

// 检测整数
Number.isInteger(123) // true
Number.isInteger(123.5) // false
Number.isInteger("123") // false

// 检测安全整数
Number.isSafeInteger(9007199254740991) // true
Number.isSafeInteger(9007199254740992) // false

// 检测 null
value === null

// 检测 undefined
value === undefined
typeof value === "undefined"

// 检测 null 或 undefined
value == null // 同时检测 null 和 undefined

类型检测决策流程

code
需要检测类型?
    │
    ├─ 基本类型?
    │   ├─ undefined → typeof value === "undefined"
    │   ├─ null → value === null
    │   ├─ string → typeof value === "string"
    │   ├─ number → typeof value === "number"
    │   ├─ boolean → typeof value === "boolean"
    │   ├─ symbol → typeof value === "symbol"
    │   └─ bigint → typeof value === "bigint"
    │
    ├─ 数组?
    │   └─ Array.isArray(value)
    │
    ├─ 函数?
    │   └─ typeof value === "function"
    │
    ├─ 对象?
    │   └─ typeof value === "object" && value !== null
    │
    └─ 精确类型?
        └─ Object.prototype.toString.call(value)

类型检测最佳实践

javascript
// ✅ 检测 undefined
value === undefined
typeof value === "undefined"

// ✅ 检测 null
value === null

// ✅ 检测 null 或 undefined
value == null // 同时检测 null 和 undefined

// ✅ 检测数组
Array.isArray(value)

  // ... 中间省略 ...


// ❌ 不推荐:使用 instanceof 检测原始类型
value instanceof String // 对原始类型返回 false

// ❌ 不推荐:使用 constructor(可被修改)
value.constructor === Array // 不安全

类型转换

JavaScript 是一门弱类型语言,变量的类型可以在运行时自动转换。理解类型转换规则对于编写可靠的代码至关重要。

概述

类型转换分为两种:

  • 显式转换:开发者主动调用的类型转换
  • 隐式转换:JavaScript 引擎自动进行的类型转换

显式类型转换

转换为字符串

javascript
// String() 函数
String(123) // "123"
String(true) // "true"
String(null) // "null"
String(undefined) // "undefined"
String([1, 2, 3]) // "1,2,3"
String({ a: 1 })(
  // "[object Object]"

  // toString() 方法
  123
)

  // ... 中间省略 ...

  )
  .toString(8)(
    // "12"(八进制)
    10
  )
  .toString(16) // "a"(十六进制)

转换为数值

javascript
// Number() 函数
Number("123") // 123
Number("12.3") // 12.3
Number("") // 0
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number("123abc") // NaN
Number([1]) // 1
Number([1, 2]) // NaN

// parseInt() 函数
parseInt("123") // 123
parseInt("123.45") // 123
parseInt("123abc") // 123
parseInt("abc123") // NaN
parseInt("10", 2) // 2 (二进制转换)
parseInt("AF", 16) // 175 (十六进制)

// parseFloat() 函数
parseFloat("123.45") // 123.45
parseFloat("123.45.67") // 123.45
parseFloat("123abc") + // 123
  // 一元加号运算符
  "123" + // 123
  true + // 1
  "" // 0

ℹ️ 最佳实践:始终为 parseInt() 提供第二个参数(进制),通常为 10 ```javascript // ✅ 推荐 parseInt("123", 10) // 明确指定十进制 // ❌ 不推荐 parseInt("123") // 依赖自动检测,可能出错

code

转换为布尔值

javascript
// Boolean() 函数
Boolean(1) // true
Boolean(0) // false
Boolean("") // false
Boolean("hello") // true
Boolean(null) // false
Boolean(undefined) // false
Boolean(NaN) // false
Boolean([]) // true(注意!)
Boolean({}) // true(注意!)

// 双重否定运算符
!!1 // true
!!0 // false
!!"" // false

隐式类型转换 ⚠️

隐式类型转换是 JavaScript 中最容易出错的特性之一,理解转换规则非常重要。

转换规则

转换为原始类型

当对象需要转换为原始类型时,会依次调用:

  1. valueOf() 方法
  2. toString() 方法
javascript
const obj = {
  valueOf() {
    return 1
  },
  toString() {
    return "2"
  }
}

Number(obj) // 1 (优先 valueOf)
String(obj) // '1' (valueOf 返回原始类型后转字符串)

运算符中的隐式转换

加法运算符 (+)
javascript
// 数值 + 数值
1 + 2                 // 3

// 数值 + 字符串
1 + '2'               // '12' (数值转换为字符串)
'1' + 2               // '12'

// 布尔值 + 数值
true + 1              // 2 (true 转换为 1)
false + 1             // 1 (false 转换为 0)

// 对象 + 数值
[] + 1                // '1' ([] 转换为 '')
[1] + 1               // '11'
({}) + 1              // '[object Object]1'
其他算术运算符 (- * / %)
javascript
// 其他运算符会将操作数转换为数值
"5" - 2 // 3
"5" * "2" // 10
"10" / "2" // 5
"10" % "3" // 1

true - false // 1
"10" - null // 10 (null 转换为 0)
"10" - undefined // NaN
比较运算符
javascript
// 相等运算符 (==) 会进行类型转换
1 == "1" // true
0 == false // true
0 == "" // true
null == undefined // true

// 全等运算符 (===) 不进行类型转换
1 === "1" // false
0 === false // false
null === undefined // false

// 关系运算符
"2" > 1 // true
"2" > "10" // false (字符串比较)
逻辑运算符
javascript
// 逻辑运算符返回其中一个操作数,不一定是布尔值
1 && 2 // 2
0 && 2 // 0
1 || 2 // 1
0 || 2 // 2

类型转换表 ⚠️

不同类型转换为布尔值

转换结果说明
undefinedfalse
nullfalse
0false包括 -0
NaNfalse
''false空字符串

// ... 中间省略 ...

| {} | '[object Object]' | 普通对象 |

常见陷阱

1. 数组相加

code
[] + []     // '' (两个空数组都转换为 '')
[1] + [2]   // '12'
[] + {}     // '[object Object]'
{} + []     // 0 (注意:{} 被解释为代码块)
({}) + []   // '[object Object]'

2. 比较运算符的字符串比较

javascript
"10" > "9" // false (字符串比较,逐字符比较)
"10" > 9 // true (数字比较)

3. 相等运算符的特殊情况

javascript
"" == 0 // true
"" == false // true
0 == false // true
null == undefined // true
null == 0 // false
undefined == 0 // false
NaN == NaN // false (NaN 不等于任何值)

4. 对象转原始类型

javascript
const obj = {
  valueOf() {
    return {}
  },
  toString() {
    return {}
  }
}

// Number(obj)  // TypeError: Cannot convert object to primitive value

最佳实践

1. 使用全等运算符

javascript
// ❌ 不推荐
if (value == null) {
} // 同时检查 null 和 undefined

// ✅ 推荐
if (value === null || value === undefined) {
}
// 或者
if (value == null) {
} // 如果确实需要同时检查

2. 显式类型转换

javascript
// ❌ 不推荐
const num = +"123"
const str = 123 + ""

// ✅ 推荐
const num = Number("123")
const str = String(123)

3. 检查 NaN

javascript
// ❌ 不推荐
if (value === NaN) {
} // 永远为 false

// ✅ 推荐
if (Number.isNaN(value)) {
}

4. 检查空对象

javascript
// ❌ 不推荐
if (obj == {}) {
} // 永远为 false

// ✅ 推荐
if (Object.keys(obj).length === 0) {
}

Symbol.toPrimitive

ES6 引入了 Symbol.toPrimitive 方法,可以更精确地控制类型转换:

javascript
const obj = {
  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case "number":
        return 1
      case "string":
        return "hello"
      case "default":
        return true
    }
  }
}

Number(obj) // 1
String(obj) // 'hello'
obj + "" // 'true'