主题
JavaScript 数据类型详解
提示
本文内容基于《JavaScript 高级程序设计(第 4 版)》及社区优质文章整理。如有理解偏差,请以官方规范或您的实践经验为准。
JavaScript 的数据类型体系主要包含两大类别:基本数据类型(Primitive Types) 与 引用数据类型(Reference Types)。
基本数据类型
注意:基本数据类型也被称为“原始数据类型”。
截至 ES2020 标准,JavaScript 共定义了 7 种基本数据类型:
undefined:表示未定义。null:表示空指针或空值。boolean:布尔值(true/false)。string:字符串。number:数值(包含整数和浮点数)。symbol:独一无二的值(ES6 新增)。bigint:任意精度的整数(ES2020 新增)。
核心特性
- 存储方式:基本类型的值直接存储在**栈内存(Stack)**中。
- 访问机制:采用按值访问(By Value),操作的是变量中实际存储的数据。
- 不可变性:基本类型的值是不可变的,且不包含任何属性或方法。
- 复制行为:赋值时会创建一个全新的副本,两者互不影响。
疑问:为何 'hello'.length 能正常工作?
既然基本类型没有属性和方法,为什么我们可以直接调用字符串的方法呢?
为了赋予基本类型操作能力,JavaScript 提供了 Boolean、Number、String 三种基本包装类型。当读取基本类型的属性时,JS 引擎会在后台自动执行以下操作:
- 创建对应类型的包装对象实例(如
new String('hello'))。 - 调用该实例上的指定属性或方法。
- 操作完成后立即销毁该实例。
js
// 代码示例
const str = 'hello world'
const len = str.length // 访问 length 属性
/**
* 引擎背后的实际操作流程:
* 1. const temp = new String('hello world') // 创建临时包装对象
* 2. const len = temp.length // 读取属性
* 3. temp = null // 销毁临时对象
*/引用数据类型
除了上述基本类型外,其余所有类型均属于引用类型。常见的包括:
Object:普通对象Array:数组Function:函数Date:日期对象RegExp:正则表达式Map/WeakMap:键值对集合(ES6)Set/WeakSet:唯一值集合(ES6)
核心特性
- 存储方式:引用类型的实际对象存储在堆内存(Heap)中,而变量在栈内存中仅保存指向该对象的内存地址(指针)。
- 访问机制:采用按引用访问(By Reference),操作的是对象的引用地址。
- 复制行为:赋值时仅复制内存指针,两个变量指向同一个堆内存对象,修改其中一个会影响另一个。
栈内存 vs 堆内存
| 特性 | 栈内存 (Stack) | 堆内存 (Heap) |
|---|---|---|
| 存储内容 | 基本类型值、引用类型指针 | 引用类型实际对象 |
| 空间特点 | 空间连续,系统自动分配释放 | 空间不连续,由垃圾回收机制 (GC) 管理 |
| 存取速度 | 快 | 较慢 |
扩展阅读:深入理解 JS 中的栈内存与堆内存
类型检测全解
JavaScript 中常用的类型检测手段主要有以下五种:
typeofinstanceofconstructorArray.isArray()Object.prototype.toString
1. typeof
最基础的检测方法,适用于检测基本类型(null 除外)和函数。
js
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof 'feng feng' // 'string'
typeof 123 // 'number'
typeof Symbol() // 'symbol'
typeof BigInt(123) // 'bigint'
typeof null // 'object' (历史遗留 Bug)关于
typeof null === 'object': 这是一个著名的历史遗留 Bug。在 JS 早期版本中,值的前 3 位用于表示类型,对象的标签是 000,而null被表示为全 0,因此被误判为object。
对于非函数的引用类型,typeof 统一返回 'object',无法区分具体类型。
js
typeof {} // 'object'
typeof [] // 'object'
typeof new Date() // 'object'
typeof console.log // 'function' (特殊)2. instanceof
主要用于检测引用类型。其原理是检查构造函数的 prototype 属性是否出现在实例对象的原型链上。
js
// 基本类型检测无效
true instanceof Boolean // false
// 引用类型检测
const p1 = new Person('fengfeng')
p1 instanceof Person // true
p1 instanceof Object // true (原型链向上查找)
// 原型链修改会影响结果
Reflect.setPrototypeOf(p1, Array.prototype)
p1 instanceof Person // false
p1 instanceof Array // true局限性:
- 无法检测基本类型。
- 原型链可被修改,导致结果不准确。
3. constructor
通过访问实例的 constructor 属性来判断其构造函数。
js
;((123).constructor ===
Number(
// true
[],
).constructor) ===
Array // true局限性:
null和undefined没有此属性。- 该属性可被随意修改,不可靠。
4. Array.isArray()
ES5 引入的专门用于检测数组的静态方法,比 instanceof 更稳健(不受 iframe 环境影响)。
js
Array.isArray([]) // true
Array.isArray({}) // false5. Object.prototype.toString
这是最准确的类型检测方式。通过调用 Object.prototype.toString.call(value),可以返回格式为 [object Type] 的字符串,从而精准区分各种内置类型。
js
const toString = Object.prototype.toString
toString.call(null) // '[object Null]'
toString.call(undefined) // '[object Undefined]'
toString.call(123) // '[object Number]'
toString.call('abc') // '[object String]'
toString.call(true) // '[object Boolean]'
toString.call(Symbol()) // '[object Symbol]'
toString.call({}) // '[object Object]'
toString.call([]) // '[object Array]'
toString.call(new Date()) // '[object Date]'
toString.call(new RegExp()) // '[object RegExp]'
toString.call(new Map()) // '[object Map]'原理简述: 当调用 toString 方法时,它会获取内部的 [[Class]] 属性(ES5)或 Symbol.toStringTag(ES6+),并组合成 [object Tag] 字符串返回。
