JavaScript 基本数据类型有哪些?7种原始类型详解

作者:青柠

时间:2026-11-02

浏览:119

全面解析 ECMAScript 规范中的 7 种原始数据类型(Number、String、Boolean、Undefined、Null、Symbol、BigInt),深入剖析内存机制、不可变性、类型检测方案及常见陷阱。

文章目录
ECMAScript 规范标准 阅读时长:约 10 分钟 技术主题:前端基础 / JavaScript 类型系统
在现行 ECMAScript 规范中,JavaScript 共有 8 种数据类型,其中包含 7 种原始数据类型(Primitive Types) 与 1 种引用数据类型(Object)。7 种原始数据类型分别是:Number、String、Boolean、Undefined、Null、Symbol 和 BigInt。原始类型的值直接存放在调用栈中,具备不可变性(Immutable),掌握它们的底层特征与边界行为是写出健壮代码的基石。
JavaScript 基本数据类型有哪些?7种原始类型详解
图 1:ECMAScript 7 大原始数据类型全景分类矩阵

一、什么是原始数据类型?

在 JavaScript 中,“原始类型”(Primitive Type,亦称基本数据类型)是指那些最基础、不可再拆分的值。与对象类型(Object,包括普通对象、数组、函数、日期等)相比,原始数据类型具有以下三个核心本质特征:

  • 按值访问(Pass by Value):当一个变量被赋予原始类型值时,变量存储的是该值的实际内容。在赋值或作为函数参数传递时,传递的是这个值的独立副本,修改副本不会对原始变量产生任何副作用。
  • 不可变性(Immutability):原始类型的值本身在创建后永远无法被修改。我们可以给变量重新赋予一个新值,但绝不可能在原地更改已有原始值内部的数据。例如字符串的 toUpperCase() 或 slice() 方法并不会修改原字符串,而是创建并返回一个全新的字符串。
  • 存放于栈内存(Stack Memory):由于原始类型所占用的内存空间是固定的,JavaScript 引擎会在调用栈(Call Stack)中直接为其分配内存空间,存取速度极快,且由执行上下文自动管理其生命周期。
JavaScript 基本数据类型有哪些?7种原始类型详解
图 2:原始类型(栈内存)与引用类型(堆内存)的存储机制与赋值对比

二、7 种原始数据类型深度剖析

1. Number(数值类型)

JavaScript 中的 Number 类型采用 IEEE 754 标准双精度 64 位浮点数格式表示。无论写入的是整数还是带小数的浮点数,在底层均存储为 64 位浮点数。

由于采用浮点数表示,Number 存在一个安全整数范围:即 Number.MIN_SAFE_INTEGER(-(253 - 1))到 Number.MAX_SAFE_INTEGER(253 - 1,即 9007199254740991)。一旦整数超过该范围,计算就会出现精度丢失。

此外,Number 包含三个特殊的数值:

  • NaN(Not-a-Number):表示非数字,通常发生于非法数学运算(如 0 / 0 或 parseInt('abc'))。需要特别注意,NaN === NaN 的结果为 false,判断某个值是否为 NaN 必须使用 Number.isNaN()。
  • Infinity 和 -Infinity:正无穷与负无穷,当数值超过浮点数上限(例如非零数除以 0:1 / 0)时产生。
  • +0 与 -0:在绝大多数运算中两者等价,但在除法运算中符号不同:1 / +0 === Infinity,而 1 / -0 === -Infinity。可使用 Object.is(+0, -0) 准确区分。
// 浮点数典型精度问题
console.log(0.1 + 0.2 === 0.3); // 输出: false (实际为 0.30000000000000004)

// 解决方案:使用 Number.EPSILON 容差比较
function areNumbersClose(a, b) {
  return Math.abs(a - b) < Number.EPSILON;
}
console.log(areNumbersClose(0.1 + 0.2, 0.3)); // 输出: true

2. String(字符串类型)

String 类型用于表示文本数据,它是由零个或多个 16 位 Unicode 字符组成的字符序列(UTF-16 编码单元)。字符串可以通过单引号(')、双引号(")或 ES6 反引号模板字符串(`)定义。

字符串的关键特质是完全不可变。当对字符串执行拼接、替换、截取时,底层均是分配新内存并生成新字符串:

let title = "javascript";
title[0] = "J"; // 静默失败(严格模式下报错),原始字符串未变
console.log(title); // 输出: "javascript"

// 常见字符串操作均返回新值
let upperTitle = title.toUpperCase();
console.log(upperTitle); // 输出: "JAVASCRIPT"
console.log(title);      // 输出: "javascript"(原值不受影响)

3. Boolean(布尔类型)

Boolean 类型仅有两个合法的字面量值:true(真)与 false(假),用于逻辑判断与流程分支控制。

在 JavaScript 条件语句(如 if、三元表达式)中,任意类型的值都会被隐式转换为布尔值。JavaScript 中仅有以下 8 个值会被转换为 false(统称为 Falsy 假值),其余所有值(包括空对象 {}、空数组 []、字符串 "0")均会被转换为 true(Truthy 真值):

  • false
  • 0、-0、0n(BigInt 零)
  • ""(空字符串)
  • null
  • undefined
  • NaN
  • document.all(浏览器宿主特殊历史遗留对象)

4. Undefined(未定义类型)

Undefined 类型只有一个值,即 undefined。它在语义上表示“变量已声明,但尚未被赋予具体值”。

在以下常见场景中,引擎会自动返回 undefined:

  • 声明了变量但未赋值:let score; // undefined
  • 访问对象上不存在的属性:const user = {}; console.log(user.age); // undefined
  • 函数没有通过 return 显式返回值,或 return 后未跟表达式:function fn() {} console.log(fn()); // undefined
  • 调用函数时未传入对应形参:function log(msg) { return msg; } log(); // undefined
代码规范提示:尽管在语法上允许手动执行 let a = undefined;,但工程最佳实践极不提倡这样做。如果需要人为清空或显式标记一个变量“无有效值”,应当赋予 null,将 undefined 留给引擎作为未初始化的默认状态。

5. Null(空值类型)

Null 类型同样只有一个值,即 null。它在语义上表示“有意缺省的空对象值”或“空指针引用”。

与 undefined 相比,null 是由开发者主动赋值给变量的,常用于释放对象引用(辅助垃圾回收)或在 DOM API 中表示未找到对应节点(例如 document.getElementById('not-exist') 返回 null)。

历史遗留 Bug 详解:执行 typeof null 时,返回结果是 "object"。这是 JavaScript 最初版本实现中的一个著名设计缺陷:早期 JS 引擎用 32 位单元存储变量,低 1~3 位作为类型标记,其中 000 代表对象,而 null 被表示为空指针(全零),因此其类型标记恰好为 0,导致 typeof 误判为 object。TC39 委员会曾提议修复该 bug,但为了保持与现有数以亿计 Web 站点的向前兼容性而被否决。因此判断 null 必须使用严格相等运算符:val === null。

6. Symbol(符号类型,ES6 引入)

Symbol 在 ECMAScript 2015(ES6)中引入,用于生成一个全局独一无二、不可变的值。每个由 Symbol() 函数创建的值都是唯一的,即便传入完全相同的描述字符串也是两个不同的 Symbol。

Symbol 的核心用途包括:

  • 防止对象属性名冲突:在给第三方库传入的对象扩展属性或混入(Mixin)方法时,使用 Symbol 作为属性名可确保绝不覆盖已有或未来的同名属性。
  • 模拟受保护/私有属性:以 Symbol 作为属性名时,常规的 for...in、Object.keys() 或 JSON.stringify() 均无法直接遍历到该属性(需通过 Object.getOwnPropertySymbols() 或 Reflect.ownKeys() 获取)。
  • 内置知名符号(Well-Known Symbols):如 Symbol.iterator(自定义迭代器)、Symbol.hasInstance(自定义 instanceof 行为)等,允许开发者直接干预语言底层行为。
const idA = Symbol("userId");
const idB = Symbol("userId");
console.log(idA === idB); // 输出: false (两者完全独立)

const user = {
  name: "Bob",
  [idA]: 10001
};
console.log(user[idA]); // 输出: 10001
console.log(Object.keys(user)); // 输出: ["name"] (Symbol 属性被隐藏)

7. BigInt(大整数类型,ES2020 引入)

BigInt 在 ECMAScript 2020(ES11)中正式加入,旨在解决 Number 无法精确表示超过 2^53 - 1(9007199254740991)的超大整数的短板。BigInt 可以安全地对任意位数的整数进行精确计算,常见于高精度时间戳、雪花算法分布式 ID、密码学哈希等场景。

创建 BigInt 的方式是在整数末尾附加字符 n,或调用 BigInt() 转换函数:

// 超过 Number 安全上限时,普通 Number 发生精度失真
const unsafeNum = 9007199254740991 + 2;
console.log(unsafeNum); // 结果异常,计算错误

// 使用 BigInt 精确计算
const safeBig = 9007199254740991n + 2n;
console.log(safeBig); // 输出: 9007199254740993n (准确无误)
BigInt 使用的两大铁律:
  1. 禁止与 Number 隐式混合运算:执行 10n + 5 会直接抛出 TypeError: Cannot mix BigInt and other types。必须显式转换后才能运算,如 10n + BigInt(5)。
  2. 默认不可被 JSON 序列化:执行 JSON.stringify({ id: 123n }) 会报错 TypeError: Do not know how to serialize a BigInt,向后端传输时建议事先转为 String。
JavaScript 基本数据类型有哪些?7种原始类型详解
图 3:7 种原始数据类型核心特征、typeof 结果与避坑点完整对照表

三、如何精准判断 JavaScript 数据类型?

在实际项目开发中,类型判断往往是 Bug 频发的高危地带。仅仅依赖 typeof 操作符是远远不够的,开发者需要根据数据类型选择正确的判定策略。

JavaScript 基本数据类型有哪些?7种原始类型详解
图 4:JavaScript 数据类型准确判定排查决策流程图

对于不同类型的检测,应当牢记以下分级判断准则:

  • 使用 typeof 判定:适用于 string、boolean、undefined、symbol、bigint 以及 function。
  • 判定 null:必须严格使用 val === null。
  • 判定 NaN:必须使用 Number.isNaN(val),严禁使用全局函数 isNaN(val)(全局函数会在判断前执行隐式类型转换,导致 isNaN('hello') 返回 true)。
  • 判定数组:使用原生静态方法 Array.isArray(val)。
  • 终极通用判别式:使用 Object.prototype.toString.call(val)。无论传入什么原始类型或复杂内置对象,它都能准确返回标准的内部原型标签:
function getType(val) {
  if (val === null) return "null";
  if (typeof val !== "object") return typeof val;
  return Object.prototype.toString.call(val).slice(8, -1).toLowerCase();
}

console.log(getType("hello")); // "string"
console.log(getType(123n));    // "bigint"
console.log(getType(null));    // "null"
console.log(getType([1, 2]));  // "array"
console.log(getType(new Date())); // "date"

四、生产环境避坑指南与最佳实践

理解 7 种原始类型的内部原理最终是为了在工程实践中写出健壮、防御性强的代码。以下总结了 4 条高频开发规范:

JavaScript 基本数据类型有哪些?7种原始类型详解
图 5:原始类型开发避坑与防御式编程规范图示
  1. 坚决杜绝宽松相等(==):在任何情况下优先使用严格全等(===)。== 会触发极其晦涩的隐式类型转换链条,如 0 == '' 为 true、false == [] 为 true,给业务逻辑埋下巨大隐患。
  2. 使用空值合并操作符(??)替代逻辑或(||):当为变量指定默认值时,|| 会将 0、false、"" 一并当作假值拦截;而 ?? 仅在左侧操作数为 null 或 undefined 时生效,完美保留合法的 0 或 false。
  3. 前后端传递雪花 ID 强制使用字符串:后端生成的 64 位整型分布式 ID(如雪花算法 Snowflake ID,通常为 19 位整数)已远超前端 Number.MAX_SAFE_INTEGER。如果后端以 JSON 纯数值格式返回,前端在解析的一瞬间末几位就会被强制截断变零。务必协调后端在序列化为 JSON 时以 String 输出。
  4. 类型转换一律采用显式转换:避免使用 +val(转数字)或 val + ''(转字符串)或 !!val(转布尔)等简写,统一使用标准构造函数 Number(val)、String(val)、Boolean(val),代码自解释性更强,易于团队代码审计。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章