JavaScript
数据类型
一、两大类
JavaScript 数据类型分为两大类:
- 原始类型(Primitive):值存在栈上,赋值是值拷贝,共 7 种
- 引用类型(Reference):值存在堆上,变量保存的是指针,赋值是引用拷贝
二、原始类型
| 类型 | 示例 | typeof 返回 |
|---|---|---|
string | 'hello' | 'string' |
number | 42、NaN、Infinity | 'number' |
boolean | true、false | 'boolean' |
undefined | undefined | 'undefined' |
null | null | 'object' ← bug |
symbol | Symbol('id') | 'symbol' |
bigint | 9007199254740991n | 'bigint' |
原始值不可变——"修改"实际上是重新绑定变量:
let str = 'hello'
str[0] = 'H' // 静默失败
console.log(str) // 'hello'
str = 'Hello' // 这是重新赋值,不是修改原值三、引用类型
本质都是 Object,内置的常用子类型:
| 类型 | 创建方式 | 说明 |
|---|---|---|
Object | {}、new Object() | 普通键值对 |
Array | []、new Array() | 有序列表,下标从 0 开始 |
Function | function(){}、() => {} | 可调用的对象,有 prototype |
Date | new Date() | 时间对象 |
RegExp | /pattern/、new RegExp() | 正则表达式 |
Map | new Map() | 任意键的键值对 |
Set | new Set() | 不重复值的集合 |
WeakMap | new WeakMap() | 弱引用 Map,键必须是对象 |
WeakSet | new WeakSet() | 弱引用 Set,值必须是对象 |
Error | new Error('msg') | 错误对象 |
typeof [] // 'object'
typeof {} // 'object'
typeof function(){} // 'function' ← 特例,函数有自己的 typeof 返回值
typeof new Date() // 'object'四、赋值行为对比
// 原始类型:值拷贝,互不影响
let a = 1
let b = a
b = 2
console.log(a) // 1
// 引用类型:引用拷贝,共享同一对象
let obj1 = { x: 1 }
let obj2 = obj1
obj2.x = 2
console.log(obj1.x) // 2
// 浅拷贝:新对象,但嵌套引用仍共享
let obj3 = { ...obj1 }
obj3.x = 99
console.log(obj1.x) // 2,不影响
let obj4 = { nested: { y: 1 } }
let obj5 = { ...obj4 }
obj5.nested.y = 99
console.log(obj4.nested.y) // 99,嵌套仍共享五、包装对象
原始类型没有方法,JS 引擎访问属性时会临时包装成对应对象,用完即丢。
'hello'.toUpperCase() // 'HELLO'
// 内部等价于:new String('hello').toUpperCase()| 原始类型 | 包装构造函数 |
|---|---|
string | String |
number | Number |
boolean | Boolean |
不要用 new 创建包装对象:
typeof new String('hi') // 'object',不是 'string'
new Boolean(false) == true // true,对象始终 truthy六、null 和 undefined
// undefined:声明了但未赋值
let x
console.log(x) // undefined
// null:主动表示"空值"
let y = null
null == undefined // true(宽松相等的特殊规则)
null === undefined // false(类型不同)
typeof null // 'object' ← 历史 bug,不会修约定:null 表示"有意为空",undefined 表示"尚未初始化"。
七、number 特殊值
NaN === NaN // false,NaN 不等于自身
Number.isNaN(NaN) // true(正确用法)
isNaN('hello') // true(有坑,会先转换)
Number.isNaN('hello')// false(不转换,更安全)
1 / 0 // Infinity
-1 / 0 // -Infinity
isFinite(Infinity) // false
// 浮点精度
0.1 + 0.2 === 0.3 // false
0.1 + 0.2 // 0.30000000000000004
// 安全整数范围 ±2^53 - 1
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.isSafeInteger(9007199254740992) // false八、BigInt
超出 Number 安全范围时用 BigInt,字面量加 n 后缀。
9007199254740991n + 1n // 9007199254740992n,精确
// 不能与 number 直接混用
1n + 1 // TypeError
1n + 1n // 2n
Number(1n) // 1 ← 手动转换九、Symbol
每次调用产生唯一值,常用于对象私有键,避免命名冲突。
const id = Symbol('id')
const id2 = Symbol('id')
id === id2 // false
const obj = { [id]: 123 }
Object.keys(obj) // [],Symbol 不出现在普通枚举中
obj[id] // 123
// 全局共享
Symbol.for('key') === Symbol.for('key') // true十、Map 和 Set vs 对象和数组
Map vs Object:
const map = new Map()
map.set(1, 'number key') // 键可以是任意类型
map.set({}, 'object key')
map.size // 2,直接获取长度
const obj = {}
obj[1] = 'key' // 键会转为字符串
Object.keys(obj) // ['1']Set vs Array:
const set = new Set([1, 2, 2, 3])
set.size // 3,自动去重
// 数组去重常用手法
const arr = [...new Set([1, 2, 2, 3])] // [1, 2, 3]WeakMap / WeakSet:键/值对目标对象不持强引用,对象被 GC 后自动清除,适合存储附加元数据而不阻止垃圾回收。
const cache = new WeakMap()
let dom = document.querySelector('#app')
cache.set(dom, { count: 0 }) // dom 销毁后,cache 里的条目自动消失