JavaScript 是动态类型语言,变量本身没有类型,值才有类型。理解数据类型是编程的第一步。
🎯 数据类型概览#
JavaScript 有 8 种数据类型,分为两大类:
原始类型(Primitive):
number- 数字string- 字符串boolean- 布尔值undefined- 未定义null- 空值symbol- 符号(ES6)bigint- 大整数(ES2020)
引用类型(Reference):
object- 对象(包括数组、函数等)
// 原始类型const num = 42const str = 'hello'const bool = trueconst undef = undefinedconst empty = nullconst sym = Symbol('id')const big = 9007199254740991n
// 引用类型const obj = { name: '张三' }const arr = [1, 2, 3]const fn = function () {}Number 类型#
JavaScript 的数字类型遵循 IEEE 754 双精度浮点数标准。
基本用法#
// 整数const int = 42const negative = -10const hex = 0xff // 十六进制,值为 255const binary = 0b1010 // 二进制,值为 10const octal = 0o77 // 八进制,��为 63
// 浮点数const float = 3.14const scientific = 2.5e6 // 2.5 × 10^6 = 2500000
// 特殊值const inf = Infinity // 无穷大const negInf = -Infinity // 负无穷大const notNum = NaN // 非数字精度问题#
🔶 浮点数运算存在精度问题:
console.log(0.1 + 0.2) // 0.30000000000000004console.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 = 9007199254740993nconsole.log(bigNum) // 9007199254740993nNaN 的特殊性#
// NaN 是唯一��等于自身的值console.log(NaN === NaN) // false
// 正确判断 NaNconsole.log(Number.isNaN(NaN)) // trueconsole.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') // 9str.includes('Hello') // truestr.startsWith(' Hello') // truestr.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 类型#
布尔值只有两个:true 和 false。
假值(Falsy)#
// 这 6 个值转布尔都是 falseBoolean(false) // falseBoolean(0) // falseBoolean('') // falseBoolean(null) // falseBoolean(undefined) // falseBoolean(NaN) // false
// 其他所有值都是 trueBoolean('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 aconsole.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 查询返回 nullconst 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 = nullconsole.log(value === null) // true🤔 typeof null 返回 "object" 是 JavaScript 的历史设计缺陷,已经无法修复。
Symbol 类型#
Symbol 是 ES6 引入的原始类型,每个 Symbol 都是唯一的。
// 创建 Symbolconst sym1 = Symbol()const sym2 = Symbol()console.log(sym1 === sym2) // false
// 带描述的 Symbolconst 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 = 9007199254740993nconsole.log(big) // 9007199254740993n
// 或使用 BigInt() 函数const big2 = BigInt('9007199254740993')
// BigInt 不能和 Number 混合运算// console.log(big + 1) // TypeError
// 必须显式转换console.log(big + 1n) // 9007199254740994nconsole.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 = 26person.city = '北京' // 添加新属性
// 删除属性delete person.city
// 检查属性console.log('name' in person) // trueconsole.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]) // 1console.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 = 10let b = a // 复制值b = 20console.log(a) // 10(不受影响)
// 引用类型:引用存储在栈中,对象存储在堆中let obj1 = { name: '张三' }let obj2 = obj1 // 复制引用obj2.name = '李四'console.log(obj1.name) // "李四"(被影响了!)比较方式#
// 原始类型比较值console.log(10 === 10) // trueconsole.log('hello' === 'hello') // true
// 引用类型比较引用console.log({} === {}) // false(不同的对象)console.log([] === []) // false
const arr1 = [1, 2, 3]const arr2 = arr1console.log(arr1 === arr2) // true(同一个引用)深拷贝浅拷贝#
const original = { name: '张三', info: { age: 25 },}
// 浅拷贝:只复制第一层const shallow = { ...original }shallow.info.age = 30console.log(original.info.age) // 30(被影响了)
// 深拷贝:递归复制所有层const deep = JSON.parse(JSON.stringify(original))deep.info.age = 35console.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” | 可调用的对象 |
核心要点:
- 原始类型存值,引用类型存引用
- 使用
===严格比较,避免隐式转换 typeof有局限,复杂类型判断用Object.prototype.toString