类型检测与转换
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" | 函数 |
使用示例
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 的语法
// 作为操作符使用(推荐)
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 操作符
// 基本类型
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 操作符
用于检测对象是否是某个构造函数的实例。
// 基本用法
[] 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) // false3. Object.prototype.toString()
最可靠的类型检测方法,返回 "[object Type]" 格式的字符串。
// 基本类型
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()
专门用于检测数组,是最推荐的数组检测方法。
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) // false5. Object.hasOwn()(ES2022)
Object.hasOwn() 是 ES2022 新增的静态方法,用于安全地检测对象是否具有指定的自身属性,是 Object.prototype.hasOwnProperty() 的替代方案。
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. 其他专用检测方法
// 检测 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类型检测决策流程
需要检测类型?
│
├─ 基本类型?
│ ├─ 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)类型检测最佳实践
// ✅ 检测 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 引擎自动进行的类型转换
显式类型转换
转换为字符串
// 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"(十六进制)转换为数值
// 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
转换为布尔值
// 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 中最容易出错的特性之一,理解转换规则非常重要。
转换规则
转换为原始类型
当对象需要转换为原始类型时,会依次调用:
valueOf()方法toString()方法
const obj = {
valueOf() {
return 1
},
toString() {
return "2"
}
}
Number(obj) // 1 (优先 valueOf)
String(obj) // '1' (valueOf 返回原始类型后转字符串)运算符中的隐式转换
加法运算符 (+)
// 数值 + 数值
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'其他算术运算符 (- * / %)
// 其他运算符会将操作数转换为数值
"5" - 2 // 3
"5" * "2" // 10
"10" / "2" // 5
"10" % "3" // 1
true - false // 1
"10" - null // 10 (null 转换为 0)
"10" - undefined // NaN比较运算符
// 相等运算符 (==) 会进行类型转换
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 (字符串比较)逻辑运算符
// 逻辑运算符返回其中一个操作数,不一定是布尔值
1 && 2 // 2
0 && 2 // 0
1 || 2 // 1
0 || 2 // 2类型转换表 ⚠️
不同类型转换为布尔值
| 值 | 转换结果 | 说明 |
|---|---|---|
undefined | false | |
null | false | |
0 | false | 包括 -0 |
NaN | false | |
'' | false | 空字符串 |
// ... 中间省略 ...
| {} | '[object Object]' | 普通对象 |
常见陷阱
1. 数组相加
[] + [] // '' (两个空数组都转换为 '')
[1] + [2] // '12'
[] + {} // '[object Object]'
{} + [] // 0 (注意:{} 被解释为代码块)
({}) + [] // '[object Object]'2. 比较运算符的字符串比较
"10" > "9" // false (字符串比较,逐字符比较)
"10" > 9 // true (数字比较)3. 相等运算符的特殊情况
"" == 0 // true
"" == false // true
0 == false // true
null == undefined // true
null == 0 // false
undefined == 0 // false
NaN == NaN // false (NaN 不等于任何值)4. 对象转原始类型
const obj = {
valueOf() {
return {}
},
toString() {
return {}
}
}
// Number(obj) // TypeError: Cannot convert object to primitive value最佳实践
1. 使用全等运算符
// ❌ 不推荐
if (value == null) {
} // 同时检查 null 和 undefined
// ✅ 推荐
if (value === null || value === undefined) {
}
// 或者
if (value == null) {
} // 如果确实需要同时检查2. 显式类型转换
// ❌ 不推荐
const num = +"123"
const str = 123 + ""
// ✅ 推荐
const num = Number("123")
const str = String(123)3. 检查 NaN
// ❌ 不推荐
if (value === NaN) {
} // 永远为 false
// ✅ 推荐
if (Number.isNaN(value)) {
}4. 检查空对象
// ❌ 不推荐
if (obj == {}) {
} // 永远为 false
// ✅ 推荐
if (Object.keys(obj).length === 0) {
}Symbol.toPrimitive
ES6 引入了 Symbol.toPrimitive 方法,可以更精确地控制类型转换:
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'