JavaScript 数据类型完全指南
数据类型是 JavaScript 的基础,也是每个开发者必须掌握的核心概念。本文将系统介绍 JS 的数据类型,包含代码示例、常用方法和高频面试题,帮助你建立完整的知识体系。
前言
JavaScript 是一门弱类型语言,但这并不意味着我们可以忽视类型的重要性。理解数据类型不仅能帮助我们写出更健壮的代码,还能在面试中游刃有余。本文将从基础到进阶,全面解析 JS 数据类型。
一、基本数据类型(Primitive Types)
基本数据类型是 JavaScript 中最基础的数据单元,它们的特点是不可变------一旦创建,值就固定了,想改只能重新赋值。这些类型直接存储在栈内存中,访问速度快。
1. String 字符串
字符串用于表示文本数据,可以使用单引号、双引号或反引号声明。
js
// 声明方式
const str1 = '单引号字符串'
const str2 = "双引号字符串"
const str3 = `模板字符串,支持变量插值:${str1}`
// 常用方法
const text = 'Hello World'
console.log(text.length) // 11 - 获取长度
console.log(text.toUpperCase()) // 'HELLO WORLD' - 转大写
console.log(text.toLowerCase()) // 'hello world' - 转小写
console.log(text.includes('World')) // true - 包含检查
console.log(text.startsWith('Hello')) // true - 开头检查
console.log(text.endsWith('World')) // true - 结尾检查
console.log(text.split(' ')) // ['Hello', 'World'] - 分割
console.log(text.substring(0, 5)) // 'Hello' - 截取
console.log(text.replace('World', 'JS')) // 'Hello JS' - 替换
console.log(text.trim()) // 去除首尾空格
注意:模板字符串(反引号)支持变量插值,是现代 JavaScript 的推荐写法。
2. Number 数字
JavaScript 只有一种数字类型,包含整数和浮点数。还有特殊值 Infinity、-Infinity 和 NaN。
js
// 声明方式
const num1 = 42 // 整数
const num2 = 3.14 // 浮点数
const num3 = 1e6 // 科学计数法,等于 1000000
// 特殊值
console.log(Infinity) // 正无穷
console.log(-Infinity) // 负无穷
console.log(NaN) // Not a Number
console.log(0 / 0) // NaN
// 常用方法
const price = 123.456
console.log(price.toFixed(2)) // '123.46' - 保留小数位
console.log(Math.floor(price)) // 123 - 向下取整
console.log(Math.ceil(price)) // 124 - 向上取整
console.log(Math.round(price)) // 123 - 四舍五入
console.log(Math.max(1, 5, 3)) // 5 - 最大值
console.log(Math.min(1, 5, 3)) // 1 - 最小值
console.log(Math.random()) // 0-1 之间的随机数
console.log(parseInt('123abc')) // 123 - 转整数
console.log(parseFloat('3.14')) // 3.14 - 转浮点数
console.log(Number('123')) // 123 - 转数字
3. Boolean 布尔值
布尔值只有两个值:true 和 false,用于逻辑判断。
js
// 声明方式
const isActive = true
const isDone = false
// 常见的 falsy 值(会被转为 false)
// false, 0, '', null, undefined, NaN
// 逻辑运算
console.log(true && false) // false - 与
console.log(true || false) // true - 或
console.log(!true) // false - 非
4. Undefined 未定义
变量声明但未赋值时的默认值。
js
let a // 声明了,但没赋值
console.log(a) // undefined
console.log(typeof a) // 'undefined'
// 函数无返回值时也是 undefined
function noReturn() {}
console.log(noReturn()) // undefined
5. Null 空值
表示"无"、"空"或"未知"的值,是开发者主动声明的值。
js
const emptyValue = null
console.log(typeof emptyValue) // 'object' (历史遗留问题)
// 清空对象引用
let obj = { name: 'test' }
obj = null // 清除引用,便于垃圾回收
6. Symbol (ES6)
创建唯一标识符,主要用于对象属性名。
js
// 声明方式
const sym1 = Symbol('description')
const sym2 = Symbol('description')
console.log(sym1 === sym2) // false - 每次都是唯一的
// 作为对象属性名
const obj = {
[sym1]: '私有数据',
name: '公开数据'
}
console.log(obj[sym1]) // '私有数据'
console.log(Object.keys(obj)) // ['name'] - 不包含 Symbol 属性
// 常用方法
const sym = Symbol('test')
console.log(sym.toString()) // 'Symbol(test)'
console.log(sym.description) // 'test' - 获取描述
7. BigInt (ES2020)
表示大于 2^53 - 1 的整数。
js
// 声明方式
const big1 = 9007199254740991n // n 后缀
const big2 = BigInt(9007199254740991)
// 超大整数运算
const maxSafe = Number.MAX_SAFE_INTEGER // 9007199254740991
console.log(maxSafe + 1) // 9007199254740992 (精度丢失)
console.log(big1 + 1n) // 9007199254740992n (正确)
// 不能与普通数字混用
// console.log(big1 + 1) // TypeError
console.log(big1 + BigInt(1)) // 正确
// 常用场景
const bigNum = 123456789012345678901234567890n
console.log(bigNum.toString()) // 转字符串
二、引用数据类型(Object Types)
引用数据类型存储的是内存地址的引用。与基本类型不同,引用类型是可变的。
1. Object 对象
键值对的集合,最常用的引用类型。
js
// 声明方式
const obj1 = {}
const obj2 = new Object()
const obj3 = { name: 'Alice', age: 25 }
// 常用操作
const person = {
name: 'Bob',
age: 30,
sayHello: function() {
return `Hello, I'm ${this.name}`
}
}
// 访问属性
console.log(person.name) // 'Bob'
console.log(person['age']) // 30
// 添加/修改属性
person.job = 'Developer'
person['age'] = 31
// 删除属性
delete person.job
// 常用方法
console.log(Object.keys(person)) // ['name', 'age'] - 获取键名
console.log(Object.values(person)) // ['Bob', 30] - 获取键值
console.log(Object.entries(person)) // [['name', 'Bob'], ['age', 30]] - 键值对
console.log(Object.assign({}, person)) // 浅拷贝
console.log({...person}) // 扩展运算符浅拷贝
console.log(JSON.stringify(person)) // 转 JSON 字符串
console.log(JSON.parse('{"name":"Bob"}')) // JSON 转对象
2. Array 数组
有序的值的集合。
js
// 声明方式
const arr1 = [1, 2, 3]
const arr2 = new Array(1, 2, 3)
// 常用方法
const fruits = ['apple', 'banana', 'orange']
// 访问和修改
console.log(fruits[0]) // 'apple'
fruits[1] = 'grape' // 修改
// 增删元素
fruits.push('mango') // 末尾添加
fruits.pop() // 末尾删除
fruits.unshift('pear') // 开头添加
fruits.shift() // 开头删除
fruits.splice(1, 1) // 从索引1删除1个元素
// 遍历和转换
fruits.forEach((item, index) => console.log(index, item))
const upper = fruits.map(f => f.toUpperCase()) // 映射
const filtered = fruits.filter(f => f.length > 5) // 过滤
const found = fruits.find(f => f === 'apple') // 查找
const some = fruits.some(f => f.length > 5) // 是否有满足条件的
// 其他常用
console.log(fruits.includes('apple')) // true
console.log(fruits.join('-')) // 'apple-banana-orange'
console.log(fruits.slice(1, 3)) // ['banana', 'orange']
console.log([...fruits].reverse()) // 反转
console.log([...fruits].sort()) // 排序
3. Function 函数
可执行的代码块,也是对象。
js
// 声明方式
function func1() { return 1 }
const func2 = () => 2
const func3 = new Function('return 3')
// 函数属性
func1.name // 函数名
func1.length // 参数个数
// 高阶函数
const multiplier = (x) => (y) => x * y
const double = multiplier(2)
console.log(double(5)) // 10
// 立即执行函数
(function() {
console.log('立即执行')
})()
4. Date 日期
处理日期和时间。
js
const now = new Date()
const specific = new Date('2024-01-15')
const timestamp = new Date(1705315200000)
// 常用方法
console.log(now.getFullYear()) // 年
console.log(now.getMonth()) // 月 (0-11)
console.log(now.getDate()) // 日
console.log(now.getHours()) // 时
console.log(now.getMinutes()) // 分
console.log(now.getSeconds()) // 秒
console.log(now.getTime()) // 时间戳
console.log(now.toISOString()) // ISO 格式字符串
console.log(now.toLocaleString()) // 本地化字符串
5. RegExp 正则表达式
用于模式匹配。
js
const pattern1 = /abc/i // 字面量
const pattern2 = new RegExp('abc', 'i')
// 常用方法
const text = 'Hello 123 World'
console.log(pattern1.test(text)) // true - 测试匹配
console.log(text.match(/\d+/)) // ['123'] - 提取匹配
console.log(text.replace(/\d+/g, 'X')) // 'Hello X World' - 替换
console.log(text.split(/\s+/)) // ['Hello', '123', 'World'] - 分割
6. Map 和 Set (ES6)
Map: 键值对集合,键可以是任意类型。
js
const map = new Map()
// 增删改查
map.set('key1', 'value1')
map.set(123, 'number key')
map.set({id: 1}, 'object key')
console.log(map.get('key1')) // 'value1'
console.log(map.has('key1')) // true
map.delete('key1')
console.log(map.size) // 2
// 遍历
map.forEach((value, key) => console.log(key, value))
for (const [key, value] of map) {
console.log(key, value)
}
Set: 唯一值的集合。
js
const set = new Set([1, 2, 2, 3, 3])
console.log(set) // Set(3) {1, 2, 3}
// 操作
set.add(4)
set.delete(2)
console.log(set.has(1)) // true
console.log(set.size) // 3
// 数组去重
const arr = [1, 2, 2, 3, 3, 4]
const unique = [...new Set(arr)] // [1, 2, 3, 4]
三、类型判断方法
typeof 操作符
js
// 基本类型
console.log(typeof 'hello') // 'string'
console.log(typeof 42) // 'number'
console.log(typeof true) // 'boolean'
console.log(typeof undefined) // 'undefined'
console.log(typeof Symbol()) // 'symbol'
console.log(typeof 123n) // 'bigint'
// 引用类型(注意)
console.log(typeof null) // 'object' (历史遗留)
console.log(typeof {}) // 'object'
console.log(typeof []) // 'object'
console.log(typeof function(){}) // 'function'
console.log(typeof new Date()) // 'object'
instanceof 操作符
js
console.log([] instanceof Array) // true
console.log({} instanceof Object) // true
console.log(new Date() instanceof Date) // true
console.log(/abc/ instanceof RegExp) // true
// 无法判断基本类型
console.log('hello' instanceof String) // false
console.log(42 instanceof Number) // false
Array.isArray() 判断是否为数组
js
console.log(Array.isArray([])) // true
console.log(Array.isArray({})) // false
console.log(Array.isArray('[]')) // false
Object.prototype.toString.call()
最准确的类型判断方法:
js
console.log(Object.prototype.toString.call('hello')) // '[object String]'
console.log(Object.prototype.toString.call(42)) // '[object Number]'
console.log(Object.prototype.toString.call(true)) // '[object Boolean]'
console.log(Object.prototype.toString.call(null)) // '[object Null]'
console.log(Object.prototype.toString.call(undefined))// '[object Undefined]'
console.log(Object.prototype.toString.call([])) // '[object Array]'
console.log(Object.prototype.toString.call({})) // '[object Object]'
console.log(Object.prototype.toString.call(new Date()))// '[object Date]'
四、高频面试题
1. 基本类型 vs 引用类型的区别
答案:
- 存储位置:基本类型存储在栈内存,引用类型存储在堆内存(栈内存存储引用地址)
- 赋值行为:基本类型赋值是复制值,引用类型赋值是复制引用地址
- 比较方式:基本类型比较值,引用类型比较引用地址
- 不可变性:基本类型不可变,引用类型可变
js
// 基本类型
let a = 10
let b = a
b = 20
console.log(a) // 10 (不变)
// 引用类型
let arr1 = [1, 2, 3]
let arr2 = arr1
arr2.push(4)
console.log(arr1) // [1, 2, 3, 4] (一起变)
2. 判断数组的正确方法
答案 :Array.isArray() 是最可靠的方法
js
// 推荐
Array.isArray([])
// 不推荐
[] instanceof Array // 跨 iframe 有问题
Object.prototype.toString.call([]) === '[object Array]' // 太繁琐
3. null 和 undefined 的区别
答案:
-
undefined表示"未定义",是变量的默认值 -
null表示"空值",是主动赋值的值 -
两者都是 falsy 值
-
typeof null是 'object'(历史遗留问题)
4. 0.1 + 0.2 !== 0.3 的原因
答案:JavaScript 使用 IEEE 754 标准存储浮点数,存在精度问题
js
console.log(0.1 + 0.2) // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3) // false
// 解决方案
console.log((0.1 + 0.2).toFixed(1)) // '0.3'
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON) // true
// EPSILON 是 ES6 的一种安全最小值,如果两数相差小于此值,则说明是正确的
5. Symbol 的用途
答案:
- 创建唯一标识符,避免属性名冲突
- 实现私有属性(Symbol 属性不会被常规方法遍历)
- 用于元编程(如 Symbol.iterator)
js
const sym1 = Symbol('key')
const sym2 = Symbol('key')
console.log(sym1 === sym2) // false
const obj = {
[sym1]: '私有数据',
name: '公开数据'
}
console.log(Object.keys(obj)) // ['name'] - Symbol 不会被遍历
6. BigInt 的使用场景
答案:处理超出 Number 安全范围的整数
js
// Number 最大安全整数
const max = Number.MAX_SAFE_INTEGER // 9007199254740991
// BigInt 可以处理更大
const big = 9007199254740991n
console.log(big + 1n) // 正确
// 常用于金融计算、ID 生成等
7. Map 和 Object 的区别
答案:
| 特性 | Object | Map |
|---|---|---|
| 键的类型 | 只能是 String/Symbol | 任意类型 |
| 顺序 | ES6 后保持插入顺序 | 保持插入顺序 |
| 大小 | 需手动计算 | .size 属性 |
| 性能 | 频繁增删性能较差 | 频繁增删性能更好 |
| 迭代 | 需用 Object.keys() | 可直接迭代 |
js
const obj = {}
const map = new Map()
// Object 的键只能是字符串
obj[1] = 'one'
obj['1'] = 'one' // 覆盖上面的值
// Map 可以保持类型
map.set(1, 'one')
map.set('1', 'two')
console.log(map.size) // 2
8. 类型转换陷阱
答案:
js
// 隐式类型转换
console.log('5' + 1) // '51' (字符串拼接)
console.log('5' - 1) // 4 (数学运算)
console.log([] + {}) // '[object Object]'
console.log({} + []) // '[object Object]'
// 比较陷阱
console.log(0 == '0') // true
console.log(0 === '0') // false
console.log(null == undefined) // true
console.log(null === undefined) // false
// 建议始终使用 === 进行比较
9. 如何判断一个值是否为 NaN
答案:
js
// 错误方法
console.log(NaN === NaN) // false
// 正确方法
console.log(Number.isNaN(NaN)) // true
console.log(Object.is(NaN, NaN)) // true
console.log(typeof NaN === 'number') // true
// 注意区别
console.log(Number.isNaN('abc')) // false
console.log(isNaN('abc')) // true (会先转换为数字)
10. 基本类型包装对象
答案:基本类型在调用方法时会临时包装成对象
js
const str = 'hello'
console.log(str.length) // 5
console.log(str.toUpperCase()) // 'HELLO'
// 等价于
const temp = new String('hello')
console.log(temp.length)
console.log(temp.toUpperCase())
// 但 temp 会被销毁
// 但不能给基本类型添加属性
str.customProp = 'test'
console.log(str.customProp) // undefined (不会报错,但无效)
五、总结
数据类型速查表
| 类型 | typeof | 说明 | 可变性 |
|---|---|---|---|
| String | string | 字符串 | 不可变 |
| Number | number | 数字 | 不可变 |
| Boolean | boolean | 布尔值 | 不可变 |
| Undefined | undefined | 未定义 | 不可变 |
| Null | object | 空值 | 不可变 |
| Symbol | symbol | 唯一标识 | 不可变 |
| BigInt | bigint | 大整数 | 不可变 |
| Object | object | 对象 | 可变 |
| Array | object | 数组 | 可变 |
| Function | function | 函数 | 可变 |
| Date | object | 日期 | 可变 |
| RegExp | object | 正则 | 不可变 |
| Map | object | 映射 | 可变 |
| Set | object | 集合 | 可变 |
最佳实践
- 始终使用
const声明变量,除非需要重新赋值 - 使用
===进行严格比较,避免类型转换陷阱 - 使用
Array.isArray()判断数组 - 处理浮点数时注意精度问题
- 使用 Map 替代 Object 当需要任意类型键时
- 使用 BigInt 处理超大整数
- 理解引用类型和基本类型的区别,避免意外的副作用
数据类型是 JavaScript 的基础,掌握它们对于编写高质量代码至关重要。希望本文能帮助你建立完整的知识体系。