JavaScript 数据类型——八种数据类型总结
Category(分类): JavaScript Status: 已整理(2026)
原文按八种类型逐项介绍 Number、String、Boolean、Undefined、Null、Symbol、BigInt 和 Object。本文保留这个顺序,修正被复制按钮混入代码的数字、转义字符和示例结果,并把“基本类型/引用类型”的常见说法与 ECMAScript 的正式术语区分开。
一、JavaScript 的八种语言类型
ECMAScript 规范定义八种 语言类型:
原始值类型
Undefined:只有一个值undefined;Null:只有一个值null;Boolean:true或false;String:字符串值;Symbol:唯一的符号值;Number:双精度浮点数值,包含NaN和正/负Infinity;BigInt:任意精度的整数值。
对象类型
Object:对象值,包括普通对象、数组、函数、日期、正则表达式、Map、Set 和宿主对象等。
“基本数据类型”和“引用数据类型”是教材中常用的说法。更准确的表述是:前七种是原始值类型,最后一种是对象类型;函数是可调用的对象,数组也是对象。原始值本身不可变,但保存原始值的变量可以重新赋值;对象通常可变,但可以通过 Object.freeze()、代理或约定限制修改。
let value = 'old'
value = 'new' // 变量重新绑定,不是修改字符串本身
const object = { count: 1 }
object.count = 2 // 对象属性默认可修改
二、Number
Number 使用 IEEE 754 binary64 双精度格式表示数值。它可以表示整数和小数,也支持十进制、二进制、八进制和十六进制字面量:
const integer = 666
const decimal = 6.66
const binary = 0b1010 // 10
const octal = 0o70 // 56
const hexadecimal = 0x1f // 31
console.log(integer, decimal, binary, octal, hexadecimal)
旧式的前导 0 八进制写法依赖非严格脚本的历史语义,在严格模式和现代代码中不要使用。用 0o 明确表达八进制,用 0b 表达二进制。
1. 浮点数与精度
十进制小数通常不能被二进制浮点格式精确表示,因此会出现:
const a = 0.1
const b = 0.2
console.log(a + b) // 0.30000000000000004
console.log(a + b === 0.3) // false
“最高精度是 17 位小数”不是可靠的规范表述。可表示的有效数字数量、相邻数值间距和误差会随数值大小变化;需要精确十进制金额时,不要只依靠 Number。
常见处理方式如下:
const roundedText = (0.1 + 0.2).toFixed(1)
console.log(roundedText) // '0.3'
console.log(Number(roundedText)) // 0.3
// 金额可以使用最小货币单位的整数
const cents = 10 + 20
console.log(cents / 100) // 0.3
乘以 10 的幂再除回来只能在特定范围内减少误差,不能保证所有计算都精确。toFixed() 返回字符串,适合展示;展示国际化数字可以考虑 Intl.NumberFormat,高精度金融计算则应选择经过验证的十进制库或整数/BigInt 方案。
2. 数值范围与安全整数
Number 能表示的有限范围可通过 Number.MAX_VALUE 等常量了解,但“能表示”不等于“每个整数都能精确表示”。安全整数范围是:
console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
console.log(Number.isSafeInteger(2 ** 53 - 1)) // true
console.log(Number.isSafeInteger(2 ** 53)) // false
超出有限范围会得到 Infinity 或 -Infinity;超出安全整数范围则可能已经丢失整数精度:
console.log(9999999999999999) // 10000000000000000
console.log(Number.isFinite(1e309)) // false
3. NaN、Infinity 与判断
NaN 仍属于 Number 类型,但表示“不是一个有效数值结果”。它不等于任何值,包括自身:
console.log(typeof NaN) // 'number'
console.log(NaN === NaN) // false
console.log(Number.isNaN(NaN)) // true
console.log(Number.isNaN('not a number')) // false
全局 isNaN() 会先进行数值转换,容易把类型错误隐藏起来:
console.log(isNaN('10')) // false:字符串被转换成 10
console.log(isNaN('blue')) // true
console.log(Number.isNaN('blue')) // false:不进行隐式转换
console.log(Number.isFinite(10)) // true
console.log(Number.isFinite('10')) // false
需要判断结果是不是非有限数时使用 Number.isFinite();需要判断真正的 NaN 时优先使用 Number.isNaN()。
4. 数值转换
Number(value):按完整的 ECMAScript 数值转换规则转换;parseInt(string, radix):从字符串开头解析整数,建议明确传入进制;parseFloat(string):从字符串开头解析十进制浮点数。
console.log(Number('10.5')) // 10.5
console.log(parseInt('10px', 10)) // 10
console.log(parseFloat('10.5px')) // 10.5
console.log(Number('10px')) // NaN
parseInt('08') 在现代 JavaScript 中通常按十进制处理,但传入 10 可以明确表达意图。不要用 parseInt 代替任意字符串的严格数值校验。
三、String
字符串可以使用单引号、双引号或模板字面量:
const name1 = '小明'
const name2 = "小明"
const name3 = `小明`
console.log(name1 === name2 && name2 === name3) // true
字符串值不可变;调用 toUpperCase()、slice() 等方法会返回新字符串,不会修改原字符串。JavaScript 字符串以 UTF-16 代码元表示,因此 .length 统计的是代码元数量,不一定是用户看到的字符数:
const text = 'A😀'
console.log(text.length) // 3:A 1 个代码元,😀 2 个代理项代码元
console.log([...text].length) // 2:按 Unicode 码点迭代
console.log(Array.from(text)) // ['A', '😀']
涉及用户可见字符、组合字符或复杂文字时,应根据产品需求使用 Intl.Segmenter 等工具,而不是只依赖 .length。
1. 转义序列
常见转义序列包括:
| 写法 | 含义 |
|---|---|
\n | 换行 |
\t | 制表符 |
\b | 退格符,不是普通空格 |
\r | 回车 |
\f | 换页 |
\\ | 反斜杠 |
\' | 单引号 |
\" | 双引号 |
\` | 反引号 |
\xNN | 两位十六进制代码元 |
\uNNNN | 四位十六进制 Unicode 代码元 |
\u{1F600} | Unicode 码点转义,需使用支持该语法的现代环境 |
原文的 \Xnn 写法不正确,标准写法是小写 \xNN;\uNNNN 和 \u{1F600} 这类转义表示代码元或码点,不应简单等同于完整 Unicode 字符。
2. 字符串转换
String(value) 可以转换所有 JavaScript 值,包括 null 和 undefined;value.toString() 需要先有一个非空值,并且可能被对象自定义覆盖:
console.log(String(null)) // 'null'
console.log(String(undefined)) // 'undefined'
console.log((123).toString()) // '123'
// null.toString() 会抛出 TypeError
3. 模板字面量
模板字面量支持换行和 ${} 插值。插值表达式会先按普通 JavaScript 规则求值,再转换成字符串:
const message = `你好,${name1}
今天是 ${2020 + 6} 年`
console.log(message)
如果要把不可信内容插入 HTML,模板字符串不会自动转义,必须使用安全的 DOM API 或可信的 HTML 清洗方案。
四、Boolean
Boolean 只有 true 和 false 两个值。条件判断会把其他值转换为布尔语义。常见假值包括:
false;0、-0、0n;NaN;''(空字符串);null;undefined。
数组、普通对象和包装对象即使内容为空,也是真值:
console.log(Boolean('false')) // true
console.log(Boolean([])) // true
console.log(Boolean({})) // true
console.log(Boolean(new Boolean(false))) // true:这是对象
const input = ''
if (!input) console.log('没有输入')
Boolean(value) 适合显式转换,!!value 是常见的简写。不要把 true 和 1、false 和 0 当成相同类型;宽松相等 == 还会触发额外的类型转换。
五、Undefined
undefined 是 Undefined 类型的唯一值,常见于:
- 声明变量但没有赋值;
- 访问不存在的对象属性;
- 函数没有显式返回值;
- 缺少函数参数;
void表达式的结果。
let pending
const object = {}
function noReturn() {}
console.log(pending) // undefined
console.log(object.missing) // undefined
console.log(noReturn()) // undefined
console.log(void 0) // undefined
let/const 声明但尚未执行初始化的变量处于暂时性死区(TDZ),不能把它简单说成“已经是 undefined”:
try {
console.log(value)
let value = 1
} catch (error) {
console.log(error.name) // ReferenceError
}
对未声明标识符使用 typeof 是一个特殊安全例外:
var declared
console.log(typeof declared) // 'undefined'
console.log(typeof neverDeclared) // 'undefined'
// console.log(neverDeclared) // ReferenceError
六、Null
null 是有意表示“没有对象值”的原始值。typeof null 返回 'object' 是 JavaScript 早期遗留的历史行为,不表示 null 真的是对象:
const car = null
console.log(typeof car) // 'object',历史遗留行为
console.log(car === null) // true
判断可能为对象的值时,通常需要同时排除 null:
function isObject(value) {
return value !== null && typeof value === 'object'
}
console.log(isObject({})) // true
console.log(isObject(null)) // false
七、Symbol
Symbol() 创建原始 Symbol 值。即使描述相同,两个独立创建的 Symbol 也不相等:
const first = Symbol('id')
const second = Symbol('id')
console.log(first === second) // false
console.log(first.description) // 'id'
console.log(typeof first) // 'symbol'
Symbol 很适合用作对象属性键,减少与普通字符串键冲突:
const id = Symbol('id')
const user = {
name: 'Ada',
[id]: 42
}
console.log(user[id]) // 42
console.log(user.id) // undefined:点语法中的 id 是字符串
console.log(Object.keys(user)) // ['name']:不会列出 Symbol 键
console.log(Reflect.ownKeys(user)) // ['name', Symbol(id)]
1. 全局 Symbol 注册表
Symbol.for(key) 会在当前 Realm 的全局 Symbol 注册表中查找或创建值;它与 Symbol() 不同:
const first = Symbol.for('app.id')
const second = Symbol.for('app.id')
console.log(first === second) // true
console.log(Symbol.keyFor(first)) // 'app.id'
console.log(Symbol.keyFor(Symbol('app.id'))) // undefined
全局注册表不是跨 iframe、Worker 或进程的全局通信机制。Symbol 也不能被隐式转换成字符串:
const token = Symbol('token')
console.log(String(token)) // 'Symbol(token)'
// 'token: ' + token // TypeError
八、BigInt
BigInt 用于任意精度整数,字面量以 n 结尾,也可以调用 BigInt():
const small = 10n
const large = 9007199254740993n
const fromString = BigInt('0x1fffffffffffff')
console.log(small + 2n) // 12n
console.log(large) // 9007199254740993n
console.log(fromString) // 9007199254740991n
Number.MAX_SAFE_INTEGER 以内的整数可以被 Number 精确表示;更大的整数应根据需求改用 BigInt 或十进制方案。
1. 运算规则
BigInt 支持大多数整数算术运算,但不能和 Number 直接进行算术混合:
console.log(10n + 10n) // 20n
console.log(11n - 10n) // 1n
console.log(2n * 10n) // 20n
console.log(110n / 10n) // 11n
console.log(5n / 2n) // 2n:向零截断
console.log(2n ** 3n) // 8n
console.log(10n > 2) // true:关系比较可以混用
// 10n + 2 会抛出 TypeError
BigInt 不支持 Math 方法和一元加号。为了演示一元加号的限制,可以在运行时捕获错误,而不要把非法的 +10n 直接写进普通代码块:
try {
BigInt('+10')
console.log('字符串可以被转换为 BigInt')
} catch (error) {
console.log(error.name)
}
BigInt 不能直接被 JSON.stringify() 序列化,传输前需要约定字符串、数字或自定义编码格式。转换为 Number 可能丢失精度:
const value = 9007199254740993n
console.log(Number(value)) // 9007199254740992,精度可能丢失
console.log(JSON.stringify({ value: value.toString() })) // {"value":"9007199254740993"}
九、Object
对象是属性键和值的集合,并带有原型、属性描述符等内部语义。属性键只能是字符串或 Symbol;数组、函数、日期、正则表达式、Map 和 Set 都是对象。
1. 创建对象
对象字面量通常最清晰;new Object() 仍然有效,但没有必要作为首选:
const person = {
name: '小明',
age: 20,
5: true
}
console.log(person['5']) // true:数值属性键会转成字符串
Object.create(proto) 可以创建指定原型的对象:
const prototype = {
greet() {
return 'hello'
}
}
const object = Object.create(prototype)
console.log(object.greet()) // hello
console.log(Object.getPrototypeOf(object) === prototype) // true
如果需要字典而不是普通对象,可以使用 Object.create(null),但它没有 toString、hasOwnProperty 等继承方法:
const dictionary = Object.create(null)
dictionary.name = 'Ada'
console.log(dictionary.name) // Ada
console.log(Object.getPrototypeOf(dictionary)) // null
2. 属性读取和动态键
点语法适合固定且合法的标识符属性名;变量键、包含空格的键和 Symbol 键应使用中括号:
const propertyName = 'name'
const user = { name: '小明' }
console.log(user.name) // 小明
console.log(user[propertyName]) // 小明
user['display name'] = '小明'
需要检查自有属性时,优先使用 Object.hasOwn();为了兼容旧环境,可以使用安全调用形式:
const object = Object.create({ inherited: true })
object.own = true
console.log('inherited' in object) // true:包含原型链
console.log(Object.hasOwn(object, 'inherited')) // false:只检查自有属性
console.log(Object.hasOwn(object, 'own')) // true
不要直接调用 object.hasOwnProperty() 来处理不可信对象,因为该方法可能被覆盖,也可能在 Object.create(null) 对象上不存在。
3. 常见对象操作
const user = { name: 'Ada' }
console.log(Object.keys(user)) // ['name']
console.log(Object.values(user)) // ['Ada']
console.log(Object.entries(user)) // [['name', 'Ada']]
const copy = { ...user } // 浅复制
const frozen = Object.freeze(copy) // 浅冻结
console.log(frozen.name) // Ada
对象展开、Object.assign() 和数组展开都主要是浅复制。嵌套对象仍然共享引用:
const original = { profile: { name: 'Ada' } }
const copy = { ...original }
copy.profile.name = 'Grace'
console.log(original.profile.name) // Grace
需要深复制时,可以根据数据类型和运行环境选择 structuredClone();它不复制函数、DOM 节点或任意原型行为,也可能无法处理某些宿主对象。不要把 JSON 序列化当作通用深复制方案。
十、类型判断与相等比较
typeof 适合判断大多数原始类型,但有两个常见例外:typeof null === 'object',函数返回 'function'(函数本质仍是对象值):
function describe(value) {
if (value === null) return 'null'
if (Array.isArray(value)) return 'array'
return typeof value
}
console.log(describe(null)) // null
console.log(describe([])) // array
console.log(describe(() => {})) // function
console.log(describe(1n)) // bigint
常用判断方式:
value === null判断null;Array.isArray(value)判断数组;typeof value === 'object' && value !== null判断普通对象范围;Number.isNaN(value)判断真正的NaN;value instanceof Constructor判断原型链关系,但跨 Realm 时需谨慎;Object.prototype.toString.call(value)可作为某些旧环境的补充,但不能替代对具体 API 的判断。
严格相等 === 通常不进行类型转换;Object.is() 与 === 的两个显著差别是能区分 -0 和 0,并把 NaN 视为自身相等:
console.log(1 === '1') // false
console.log(Object.is(NaN, NaN)) // true
console.log(Object.is(-0, 0)) // false
console.log(-0 === 0) // true
十一、原始值包装对象
读取原始字符串的属性时,JavaScript 会在需要时临时进行包装,因此可以调用方法;这不意味着应该主动使用 new String()、new Number() 或 new Boolean():
const primitive = 'hello'
const wrapper = new String('hello')
console.log(primitive.toUpperCase()) // HELLO
console.log(typeof primitive) // string
console.log(typeof wrapper) // object
console.log(Boolean(new Boolean(false))) // true
包装对象会带来类型判断和布尔转换陷阱。业务代码应优先使用原始值。
十二、总结
记忆八种类型可以先抓住这些关键点:
undefined和null都只有一个值,但含义和typeof行为不同;Number是 binary64 浮点数,安全整数范围有限;NaN不等于自身,优先使用Number.isNaN();- 字符串按 UTF-16 代码元存储,
.length不一定等于用户看到的字符数; Symbol是唯一原始值,可作为不会与普通字符串冲突的属性键;BigInt适合大整数,但不能和Number混合算术,除法向零截断;- 数组和函数都是对象;所谓“引用类型”是教学简称;
- 类型判断要结合
typeof、Array.isArray、Object.hasOwn和具体 API,而不要只背一张表。