1. JavaScript 基本数据类型概述
JavaScript 作为一门动态弱类型语言,其数据类型系统与其他编程语言有着显著差异。在 JavaScript 中,数据类型可以分为两大类:原始类型(Primitive types)和对象类型(Object type)。理解这些数据类型及其区别是掌握 JavaScript 编程的基础。
原始类型包括:
- Undefined
- Null
- Boolean
- Number
- BigInt
- String
- Symbol
这些原始类型的值是不可变的(immutable),这意味着一旦创建就无法直接修改其值。当我们对原始类型变量进行操作时,实际上是在创建新的值,而不是修改原有值。
对象类型则是可变的(mutable),包括:
- Object
- Array
- Function
- Date
- RegExp 等
注意:虽然 typeof null 返回 "object",但这被认为是 JavaScript 的一个历史遗留错误,null 实际上是原始类型。
2. 原始数据类型详解
2.1 Undefined 类型
Undefined 类型只有一个值:undefined。它表示变量已声明但尚未被赋值,或者函数没有明确返回值时的默认返回值。
let x; console.log(x); // undefined function foo() {} console.log(foo()); // undefined在实际开发中,我们应尽量避免直接使用 undefined 值,而是通过变量声明但不赋值的方式让 JavaScript 引擎自动赋予 undefined。
2.2 Null 类型
Null 类型也只有一个值:null。它表示有意地缺少任何对象值。与 undefined 不同,null 需要显式赋值。
let y = null; console.log(y); // nullnull 常用于以下场景:
- 初始化一个可能被赋值为对象的变量
- 表示函数的参数不是对象
- 表示对象原型链的终点(如 Object.prototype.proto=== null)
2.3 Boolean 类型
Boolean 类型有两个值:true 和 false。它常用于条件判断和逻辑运算。
let isActive = true; let isCompleted = false; if (isActive) { console.log('Active'); }JavaScript 中所有值都可以转换为布尔值。以下值会被转换为 false(称为 falsy 值):
- false
- 0
- "" (空字符串)
- null
- undefined
- NaN
其他所有值都会被转换为 true(truthy 值)。
2.4 Number 类型
Number 类型采用 IEEE 754 64位双精度浮点数格式表示。它能够表示的范围大约是 ±5.0 × 10⁻³²⁴ 到 ±1.8 × 10³⁰⁸。
let integer = 42; let float = 3.14159; let scientific = 2.998e8; // 2.998 × 10^8特殊数值:
- Infinity 和 -Infinity:表示数学上的无穷大
- NaN(Not a Number):表示无效的数学运算结果
console.log(1 / 0); // Infinity console.log(Math.sqrt(-1)); // NaN注意:JavaScript 中所有数字都以浮点数形式存储,整数只是没有小数部分的特殊情况。
2.5 BigInt 类型
BigInt 是 ES2020 引入的新类型,用于表示任意精度的整数。通过在数字后加 n 后缀或调用 BigInt() 函数创建。
const bigInt = 9007199254740991n; const alsoBigInt = BigInt(9007199254740991); console.log(bigInt + 1n); // 9007199254740992nBigInt 主要用于超出 Number 安全整数范围(±(2⁵³ - 1))的计算。
2.6 String 类型
String 类型表示文本数据,使用 UTF-16 编码。字符串可以用单引号(')、双引号(")或反引号(`)表示。
let str1 = 'Hello'; let str2 = "World"; let template = `Hello ${str2}`; // 模板字符串字符串是不可变的,任何修改字符串的操作都会创建新字符串。
2.7 Symbol 类型
Symbol 是 ES6 引入的原始类型,表示唯一的标识符。常用于对象属性的键,以避免属性名冲突。
const sym1 = Symbol(); const sym2 = Symbol('description'); // 可选的描述字符串 console.log(sym1 === sym2); // falseSymbol 的主要用途:
- 创建唯一的对象属性键
- 实现自定义的迭代行为
- 定义众所周知的 Symbol(如 Symbol.iterator)
3. 对象类型(Object)
对象是属性的集合,属性由键值对组成。对象可以通过字面量、new 操作符或 Object.create() 创建。
// 对象字面量 const person = { name: 'Alice', age: 30, greet() { console.log(`Hello, I'm ${this.name}`); } }; // 使用 new 操作符 const date = new Date(); // 使用 Object.create() const protoObj = { x: 1 }; const obj = Object.create(protoObj);对象属性可以是数据属性或访问器属性(getter/setter)。数据属性有四个特性:
- value:属性的值
- writable:是否可修改
- enumerable:是否可枚举
- configurable:是否可删除或修改特性
4. 数据类型检测与转换
4.1 类型检测
typeof 操作符可以检测原始类型(除 null 外)和函数:
console.log(typeof undefined); // "undefined" console.log(typeof null); // "object" (历史遗留问题) console.log(typeof true); // "boolean" console.log(typeof 42); // "number" console.log(typeof 42n); // "bigint" console.log(typeof "hello"); // "string" console.log(typeof Symbol()); // "symbol" console.log(typeof {}); // "object" console.log(typeof []); // "object" console.log(typeof function(){}); // "function"检测数组可以使用 Array.isArray():
console.log(Array.isArray([])); // true console.log(Array.isArray({})); // false4.2 类型转换
JavaScript 是弱类型语言,会自动进行类型转换。我们也可以显式转换:
// 转换为字符串 console.log(String(123)); // "123" console.log(123 + ''); // "123" // 转换为数字 console.log(Number("123")); // 123 console.log(+"123"); // 123 console.log(parseInt("123px")); // 123 console.log(parseFloat("3.14")); // 3.14 // 转换为布尔值 console.log(Boolean(1)); // true console.log(!!1); // true console.log(Boolean(0)); // false5. 数据类型的内存管理
理解 JavaScript 数据类型在内存中的存储方式对于编写高效代码至关重要。
5.1 原始类型的存储
原始类型值直接存储在栈内存中,访问速度快但大小固定。当我们将原始类型赋值给变量或传递给函数时,实际上是传递值的副本。
let a = 10; let b = a; // b 是 a 的副本 a = 20; console.log(b); // 10 (不受 a 变化的影响)5.2 对象类型的存储
对象类型值存储在堆内存中,变量存储的是对象的引用(内存地址)。当复制对象变量或将其传递给函数时,传递的是引用而非实际对象。
let obj1 = { x: 10 }; let obj2 = obj1; // obj2 和 obj1 引用同一个对象 obj1.x = 20; console.log(obj2.x); // 20 (受 obj1 变化的影响)5.3 垃圾回收
JavaScript 使用自动垃圾回收机制。对于不再被引用的对象,垃圾回收器会自动释放其占用的内存。了解这一点有助于避免内存泄漏:
function createObjects() { const tempObj = {}; // 临时对象 // 函数执行完毕后,tempObj 不再被引用,会被回收 }6. 数据类型的最佳实践
6.1 选择合适的数据类型
- 对于简单的标志或开关,使用 Boolean
- 对于整数和小数,使用 Number
- 对于大整数,使用 BigInt
- 对于文本,使用 String
- 对于唯一标识符,使用 Symbol
- 对于复杂数据结构,使用 Object 或特定对象类型(如 Array、Map)
6.2 避免的类型陷阱
- 浮点数精度问题:
console.log(0.1 + 0.2 === 0.3); // false // 解决方案:使用整数运算或将结果四舍五入- NaN 的特殊性:
console.log(NaN === NaN); // false // 正确检测 NaN 的方法: console.log(Number.isNaN(NaN)); // true- null 和 undefined 的混淆:
// 明确区分变量未定义和有意为空 let missingValue = null; // 明确表示"没有值"6.3 性能考虑
- 对于大量相似数据的集合,考虑使用类型化数组(TypedArray)
- 避免频繁创建和销毁大型对象
- 对于键值对集合,根据需求选择 Object、Map 或 WeakMap
7. ES6+ 新增数据类型特性
7.1 Symbol 的进阶用法
Symbol 可以用于创建对象的私有属性:
const _private = Symbol('private'); class MyClass { constructor() { this[_private] = 'secret'; } getSecret() { return this[_private]; } } const instance = new MyClass(); console.log(instance.getSecret()); // "secret" console.log(instance[_private]); // 可以访问,但不是真正的私有7.2 BigInt 的运算限制
BigInt 不能与 Number 直接混合运算:
const big = 100n; const num = 50; // console.log(big + num); // 报错 console.log(big + BigInt(num)); // 150n7.3 模板字符串的高级用法
模板字符串支持标签函数,可以创建 DSL(领域特定语言):
function highlight(strings, ...values) { return strings.reduce((result, str, i) => { return `${result}${str}<mark>${values[i] || ''}</mark>`; }, ''); } const name = 'Alice'; const age = 30; const html = highlight`Hello ${name}, you are ${age} years old.`; console.log(html); // "Hello <mark>Alice</mark>, you are <mark>30</mark> years old."