JavaScript 中的数据类型可以分为两大类:原始类型(基本类型) 和引用类型(对象类型)。
一、核心概念:原始类型 vs 引用类型
在深入具体类型之前,理解这个区别至关重要。
特性 | 原始类型 (Primitive Types) | 引用类型 (Reference Types) |
---|---|---|
存储方式 | 值本身直接存储在栈内存中 | 值存储在堆内存中,栈内存中存储的是地址(指针) |
比较方式 | 比较的是值是否相等 | 比较的是内存地址是否相等(即是否是同一个对象) |
可变性 | 不可变,值本身无法被改变 | 可变,可以随时添加、修改、删除属性 |
复制方式 | 复制时会创建一个全新的值 | 复制时只复制地址,多个变量指向同一个对象 |
二、原始类型 (Primitive Types)
ECMAScript 标准定义了 7 种原始数据类型:
1. undefined
-
含义:表示变量已声明但未初始化,或函数没有明确返回值。
-
特点:
-
一个变量只声明不赋值,它的默认值就是
undefined
。 -
它是一个原始值,不是对象,没有方法。
-
-
示例:
let a; console.log(a); // undefined console.log(typeof a); // "undefined" function foo() {} console.log(foo()); // undefined
2. null
-
含义:表示一个空值或对象指针为空。通常用于主动释放对象引用。
-
特点:
-
typeof null
返回"object"
,这是一个历史悠久的 bug,无法修复(会破坏现有兼容性)。 -
在逻辑运算中,
null
被视为falsy
(假值)。
-
-
示例:
let empty = null; console.log(empty); // null console.log(typeof empty); // "object" (注意这个坑!) // 判断一个值是否为 null 的正确方法 console.log(empty === null); // true
3. boolean
-
含义:逻辑值,只有两个:
true
和false
。 -
特点:
-
常用于条件判断和循环控制。
-
其他类型的值可以通过布尔转换变成布尔值。转换规则中,
false
,0
,""
,null
,undefined
,NaN
会转为false
,其他所有值都转为true
。
-
-
示例:
let isActive = true; let isGreater = 10 > 5; // true // 布尔转换 console.log(Boolean(0)); // false console.log(Boolean("hello")); // true console.log(!!"hello"); // true (!! 是快速转换为布尔值的技巧)
4. number
-
含义:用于表示整数和浮点数(小数)。JavaScript 中所有数字都是基于 IEEE 754 标准的双精度 64 位二进制格式。
-
特点:
-
整数和浮点数:
let count = 10; let price = 99.95;
-
特殊值:
-
NaN
(Not-a-Number):一个特殊的值,表示一个本来要返回数值的操作失败了(如0 / 0
)。NaN
与任何值(包括自己)都不相等。使用isNaN()
函数来判断。 -
Infinity
和-Infinity
:表示正负无穷大(如1 / 0
)。
-
-
数值范围:
Number.MAX_VALUE
,Number.MIN_VALUE
。 -
精度问题:由于二进制浮点数的限制,进行小数运算时可能会有精度误差(如
0.1 + 0.2 !== 0.3
)。处理金融计算时建议使用BigInt
或以分为单位存储,或使用第三方库。
-
-
示例:
let num = 42; let pi = 3.14159; let result = 10 / 0; // Infinity let invalid = Math.sqrt(-1); // NaN console.log(isNaN(invalid)); // true console.log(0.1 + 0.2); // 0.30000000000000004
5. string
-
含义:表示文本数据,由零个或多个 16 位 Unicode 字符组成。
-
特点:
-
不可变:字符串一旦创建,其内容就不能改变。
str[0] = 'X'
这样的操作是无效的。修改字符串实际上是创建了一个新的字符串。 -
可以用单引号 (
'
)、双引号 ("
) 或反引号 (`
) 表示。 -
使用反引号可以定义模板字符串,支持多行文本和字符串插值(
${expression}
)。
-
-
示例:
let name = "Alice"; let greeting = `Hello, ${name}!`; // Hello, Alice! let multiLine = `This is a multi-line string.`;
6. symbol
(ES6 新增)
-
含义:表示唯一且不可变的值,主要用于对象的属性名,以确保属性名不会冲突。
-
特点:
-
通过
Symbol()
函数创建,每次调用都会返回一个全新的、唯一的 Symbol。 -
可以传入一个字符串作为描述,便于调试,但即使描述相同,Symbol 值也不同。
-
Symbol 作为属性名时,在
for...in
、Object.keys()
中不可枚举,需要使用Object.getOwnPropertySymbols()
来获取。
-
-
示例:
let sym1 = Symbol('id'); let sym2 = Symbol('id'); console.log(sym1 === sym2); // false let obj = { [sym1]: "secret value" }; console.log(obj[sym1]); // "secret value"
7. bigint
(ES2020 新增)
-
含义:用于表示任意精度的整数,解决
number
类型无法安全表示大于2^53 - 1
的整数的问题。 -
特点:
-
通过在整数末尾加
n
来创建,或者调用BigInt()
函数。 -
不能和
number
类型直接进行混合数学运算,需要先显式转换。
-
-
示例:
let bigNum = 9007199254740991n; // 最大的安全整数 let evenBigger = bigNum + 1n; console.log(typeof bigNum); // "bigint" // 需要转换 let regularNum = 100; console.log(bigNum + BigInt(regularNum)); // 9007199254741091n
三、引用类型 (Reference Types)
object
对象是属性的集合,属性是键值对(key-value pair)。键通常是字符串或 Symbol,值可以是任何类型,包括其他对象。
-
创建方式:
-
字面量语法:
let obj = { name: 'Bob', age: 25 };
-
构造函数:
let obj = new Object();
-
-
常见内置对象:
-
Array
(数组):有序的数据集合。let fruits = ['Apple', 'Banana']; console.log(Array.isArray(fruits)); // true
-
Function
(函数):函数也是对象,是一种可调用的对象。function sayHi() { console.log('Hi!'); } console.log(typeof sayHi); // "function" (但本质上它是对象的一种子类型)
-
Date
(日期):处理日期和时间。 -
RegExp
(正则表达式):用于模式匹配文本。 -
Map
,Set
,WeakMap
,WeakSet
(ES6):更复杂的集合数据结构。 -
以及其他许多内置对象,如
Error
,Promise
等。
-
引用类型的核心陷阱:复制与比较
// 1. 复制:只复制地址
let obj1 = { value: 10 };
let obj2 = obj1; // obj2 和 obj1 指向同一个对象
obj2.value = 20;
console.log(obj1.value); // 20 (!) 原对象也被修改了
// 2. 比较:比较的是地址,不是内容
let obj3 = { value: 20 };
let obj4 = { value: 20 };
console.log(obj3 == obj4); // false (!) 尽管内容相同,但它们是两个不同的对象
console.log(obj3 === obj4); // false
四、类型检测方法
-
typeof
操作符:-
用于检测原始类型(除
null
外)非常有效。 -
对引用类型,除了
function
返回"function"
,其他都返回"object"
。
typeof "hello" // "string" typeof 42 // "number" typeof true // "boolean" typeof undefined // "undefined" typeof Symbol() // "symbol" typeof 10n // "bigint" typeof null // "object" (著名的bug) typeof {} // "object" typeof [] // "object" (无法区分数组和普通对象) typeof function(){} // "function"
-
-
instanceof
操作符:-
用于检测一个对象是否是某个构造函数的实例。适用于检测具体的引用类型。
-
原理:检查构造函数的
prototype
属性是否出现在对象的原型链上。
[] instanceof Array; // true {} instanceof Object; // true new Date() instanceof Date; // true // 注意:所有引用类型的原型链末端都是 Object [] instanceof Object; // true
-
-
Array.isArray()
:-
专门用于判断一个值是否为数组,比
instanceof
更可靠(尤其是在多个 frame 或 realm 中)。
-
-
Object.prototype.toString.call()
:-
最强大、最准确的类型检测方法。可以准确返回
[object Type]
字符串。
Object.prototype.toString.call(null); // "[object Null]" Object.prototype.toString.call([]); // "[object Array]" Object.prototype.toString.call({}); // "[object Object]" Object.prototype.toString.call(new Date()); // "[object Date]"
-
总结
类型 | 分类 | 可变性 | typeof 结果 | 备注 |
---|---|---|---|---|
undefined | 原始 | 不可变 | "undefined" | 未定义 |
null | 原始 | 不可变 | "object" | 空值(历史bug) |
boolean | 原始 | 不可变 | "boolean" | 布尔值 |
number | 原始 | 不可变 | "number" | 数字(含浮点、NaN、Infinity) |
string | 原始 | 不可变 | "string" | 字符串 |
symbol | 原始 | 不可变 | "symbol" | 唯一值(ES6) |
bigint | 原始 | 不可变 | "bigint" | 大整数(ES2020) |
object | 引用 | 可变 | "object" | 对象(包括Array, Date等) |
function | 引用 | 可变 | "function" | 函数(对象的一种) |
理解这些数据类型的特性和区别,是编写健壮、高效 JavaScript 代码的基石。