Skip to content

变量与数据类型

JavaScript 是动态类型语言,变量本身没有类型,值才有类型。理解数据类型是编程的第一步。

🎯 数据类型概览#

JavaScript 有 8 种数据类型,分为两大类:

原始类型(Primitive)

引用类型(Reference)

// 原始类型
const num = 42
const str = 'hello'
const bool = true
const undef = undefined
const empty = null
const sym = Symbol('id')
const big = 9007199254740991n
// 引用类型
const obj = { name: '张三' }
const arr = [1, 2, 3]
const fn = function () {}

Number 类型#

JavaScript 的数字类型遵循 IEEE 754 双精度浮点数标准。

基本用法#

// 整数
const int = 42
const negative = -10
const hex = 0xff // 十六进制,值为 255
const binary = 0b1010 // 二进制,值为 10
const octal = 0o77 // 八进制,��为 63
// 浮点数
const float = 3.14
const scientific = 2.5e6 // 2.5 × 10^6 = 2500000
// 特殊值
const inf = Infinity // 无穷大
const negInf = -Infinity // 负无穷大
const notNum = NaN // 非数字

精度问题#

🔶 浮点数运算存在精度问题:

console.log(0.1 + 0.2) // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3) // false
// 解决方案1:转为整数运算
const result = (0.1 * 10 + 0.2 * 10) / 10 // 0.3
// 解决方案2:使用 toFixed 后转回数字
const fixed = Number((0.1 + 0.2).toFixed(10)) // 0.3
// 解决方案3:判断差值是否足够小
function equal(a, b) {
return Math.abs(a - b) < Number.EPSILON
}
console.log(equal(0.1 + 0.2, 0.3)) // true

安全整数范围#

// 最大安全整数
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
// 超出范围会丢失精度
console.log(9007199254740992 === 9007199254740993) // true!
// 使用 BigInt 处理大整数
const bigNum = 9007199254740993n
console.log(bigNum) // 9007199254740993n

NaN 的特殊性#

// NaN 是唯一��等于自身的值
console.log(NaN === NaN) // false
// 正确判断 NaN
console.log(Number.isNaN(NaN)) // true
console.log(Number.isNaN('hello')) // false(不会强制转换)
console.log(isNaN('hello')) // true(会强制转换,不推荐)

String 类型#

字符串是不可变的字符序列。

创建字符串#

// 三种引号
const single = '单引号'
const double = '双引号'
const template = `模板字符串`
// 模板字符串支持多行和插值
const name = '张三'
const greeting = `你好,${name}
今天是星期一。`
// 转义字符
const escaped = '第一行\n第二行\t缩进'
const path = 'C:\\Users\\name' // 反斜杠需转义

字符串操作#

const str = 'Hello, World!'
// 长度
console.log(str.length) // 13
// 访问字符
console.log(str[0]) // 'H'
console.log(str.charAt(0)) // 'H'
// 字符串不可变
str[0] = 'h' // 静默失败
console.log(str) // 'Hello, World!'(没变)
// ��须创建新字符串
const newStr = 'h' + str.slice(1)
console.log(newStr) // 'hello, World!'

常用方法#

const str = ' Hello, World! '
// 查找
str.indexOf('World') // 9
str.includes('Hello') // true
str.startsWith(' Hello') // true
str.endsWith('! ') // true
// 提取
str.slice(2, 7) // 'Hello'
str.substring(2, 7) // 'Hello'
// 转换
str.toUpperCase() // ' HELLO, WORLD! '
str.toLowerCase() // ' hello, world! '
str.trim() // 'Hello, World!'
// 分割与连接
'a,b,c'.split(',') // ['a', 'b', 'c']
;['a', 'b', 'c'].join('-') // 'a-b-c'
// 替换
str.replace('World', 'JavaScript') // ' Hello, JavaScript! '
'aaa'.replaceAll('a', 'b') // 'bbb'

Boolean 类型#

布尔值只有两个:truefalse

假值(Falsy)#

// 这 6 个值转布尔都是 false
Boolean(false) // false
Boolean(0) // false
Boolean('') // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(NaN) // false
// 其他所有值都是 true
Boolean('0') // true(非空字符串)
Boolean([]) // true(空数组)
Boolean({}) // true(空对象)
Boolean(function () {}) // true

实际应用#

// 条件判断
const value = ''
if (value) {
console.log('有值')
} else {
console.log('空值') // 执行这里
}
// 默认值(旧写法)
function greet(name) {
name = name || '游客'
return `你好,${name}`
}
// 🔶 注意:0 和空字符串会被误判
greet('') // "你好,游客"(可能不是预期)
greet(0) // "你好,游客"
// 使用 ?? 更安全(ES2020)
function greetSafe(name) {
name = name ?? '游客'
return `你好,${name}`
}
greetSafe('') // "你好,"(保留空字符串)

Undefined 与 Null#

这两个类型都表示”没有值”,但��义不同。

undefined#

表示变量已声明但未赋值,或者不存在的属性:

// 未赋值的变量
let a
console.log(a) // undefined
// 不存在的属性
const obj = { name: '张三' }
console.log(obj.age) // undefined
// 无返回值的函数
function fn() {}
console.log(fn()) // undefined
// 未传递的参数
function greet(name) {
console.log(name) // undefined
}
greet()

null#

表示”故意为空”,需要显式赋值:

// 表示空值
let user = null // 明确表示"没有用户"
// 清空引用
let data = { name: '张三' }
data = null // 释放对象引用,帮助垃圾回收
// DOM 查询返回 null
const element = document.querySelector('.not-exist')
console.log(element) // null

比较#

console.log(undefined == null) // true(宽松相等)
console.log(undefined === null) // false(严格相等)
console.log(typeof undefined) // "undefined"
console.log(typeof null) // "object"(历史遗留 bug)
// 判断 null 的正确方式
const value = null
console.log(value === null) // true

🤔 typeof null 返回 "object" 是 JavaScript 的历史设计缺陷,已经无法修复。

Symbol 类型#

Symbol 是 ES6 引入的原始类型,每个 Symbol 都是唯一的。

// 创建 Symbol
const sym1 = Symbol()
const sym2 = Symbol()
console.log(sym1 === sym2) // false
// 带描述的 Symbol
const id = Symbol('user_id')
console.log(id.description) // "user_id"
// 作为对象属性键
const user = {
name: '张三',
[id]: 12345,
}
console.log(user[id]) // 12345
// Symbol 属性不会被常规方法枚举
console.log(Object.keys(user)) // ['name']
console.log(Object.getOwnPropertySymbols(user)) // [Symbol(user_id)]

BigInt 类型#

BigInt 可以表示任意大的整数,解决了 Number 的精度限制:

// 创建 BigInt(数字后加 n)
const big = 9007199254740993n
console.log(big) // 9007199254740993n
// 或使用 BigInt() 函数
const big2 = BigInt('9007199254740993')
// BigInt 不能和 Number 混合运算
// console.log(big + 1) // TypeError
// 必须显式转换
console.log(big + 1n) // 9007199254740994n
console.log(Number(big) + 1) // 可能丢失精度

Object 类型#

对象是键值对的集合,是 JavaScript 中最重要的数据结构。

// 创建对象
const person = {
name: '张三',
age: 25,
greet() {
return `你好,我是${this.name}`
},
}
// 访问属性
console.log(person.name) // "张三"
console.log(person['age']) // 25
// 修改属性
person.age = 26
person.city = '北京' // 添加新属性
// 删除属性
delete person.city
// 检查属性
console.log('name' in person) // true
console.log(person.hasOwnProperty('name')) // true

数组#

数组是特殊的对象,索引作为键:

const arr = [1, 2, 3]
console.log(typeof arr) // "object"
console.log(Array.isArray(arr)) // true
console.log(arr[0]) // 1
console.log(arr.length) // 3
arr.push(4) // [1, 2, 3, 4]
arr.pop() // [1, 2, 3]

函数#

函数也是对象,但有可调用的特性:

function add(a, b) {
return a + b
}
console.log(typeof add) // "function"
// 函数也可以有属性
add.description = '加法函数'
console.log(add.description) // "加法函数"

typeof 运算符#

typeof 用于判断值的类型,返回字符串:

// 原始类型
typeof 42 // "number"
typeof 'hello' // "string"
typeof true // "boolean"
typeof undefined // "undefined"
typeof Symbol() // "symbol"
typeof 42n // "bigint"
// 特殊情况
typeof null // "object"(历史 bug)
typeof function () {} // "function"
typeof [] // "object"
typeof {} // "object"

🔶 typeof 无法区分 null、数组和普通对象。

完整的类型判断#

function getType(value) {
// 处理 null
if (value === null) return 'null'
// 处理原始类型
const type = typeof value
if (type !== 'object') return type
// 使用 Object.prototype.toString 区分对象类型
const objectType = Object.prototype.toString.call(value)
// "[object Array]" -> "array"
return objectType.slice(8, -1).toLowerCase()
}
// 测试
console.log(getType(42)) // "number"
console.log(getType('hello')) // "string"
console.log(getType(null)) // "null"
console.log(getType(undefined)) // "undefined"
console.log(getType([])) // "array"
console.log(getType({})) // "object"
console.log(getType(function () {})) // "function"
console.log(getType(new Date())) // "date"
console.log(getType(/regex/)) // "regexp"
console.log(getType(new Map())) // "map"
console.log(getType(new Set())) // "set"

原始类型 vs 引用类型#

存储方式#

// 原始类型:值存储在栈中
let a = 10
let b = a // 复制值
b = 20
console.log(a) // 10(不受影响)
// 引用类型:引用存储在栈中,对象存储在堆中
let obj1 = { name: '张三' }
let obj2 = obj1 // 复制引用
obj2.name = '李四'
console.log(obj1.name) // "李四"(被影响了!)

比较方式#

// 原始类型比较值
console.log(10 === 10) // true
console.log('hello' === 'hello') // true
// 引用类型比较引用
console.log({} === {}) // false(不同的对象)
console.log([] === []) // false
const arr1 = [1, 2, 3]
const arr2 = arr1
console.log(arr1 === arr2) // true(同一个引用)

深拷贝浅拷贝#

const original = {
name: '张三',
info: { age: 25 },
}
// 浅拷贝:只复制第一层
const shallow = { ...original }
shallow.info.age = 30
console.log(original.info.age) // 30(被影响了)
// 深拷贝:递归复制所有层
const deep = JSON.parse(JSON.stringify(original))
deep.info.age = 35
console.log(original.info.age) // 30(不受影响)
// 🔶 JSON 方法的限制:不支持函数、undefined、Symbol、循环引用

常见陷阱#

🙋 为什么 typeof null 是 “object”?#

这是 JavaScript 最初的实现 bug。在 JS 第一版中,值以 32 位存储,类型标签占低 3 位。对象的标签是 000,而 null 被表示为空指针(全是 0),所以被误判为对象。

🙋 空数组和空对象为什么是真值?#

// [] 和 {} 虽然"空",但它们是对象引用,不是 falsy 值
if ([]) console.log('空数组是真值')
if ({}) console.log('空对象是真值')
// 判断数组是否为空
const arr = []
if (arr.length === 0) console.log('数组为空')
// 判断对象是否为空
const obj = {}
if (Object.keys(obj).length === 0) console.log('对象为空')

🙋 字符串比较的坑#

// 字符串按 Unicode 码点比较
console.log('10' > '9') // false!因为 '1' < '9'
console.log('10' > 9) // true(类型转换后数值比较)
// 比较数字字符串要先转换
console.log(Number('10') > Number('9')) // true

总结#

类型typeof 结果特点
number”number”双精度浮点,有精度问题
string”string”不可变字符序列
boolean”boolean”true 或 false
undefined”undefined”未赋值
null”object”故意为空,typeof 有 bug
symbol”symbol”唯一标识符
bigint”bigint”任意精度整数
object”object”键值对集合,包括数组函数
function”function”可调用的对象

核心要点