个人博客:haichenyi.com。感谢关注
一. 目录
- 一–目录
- 二–分类
- 三–核心区别
- 四–实际场景中的问题
- 五–总结对比
二. 分类
前面说过这么判断数据类型,今天来说说基本数据类型和引用数据类型的区别。
基本数据类型 | 引用数据类型 |
---|---|
String | Object |
Number | Function |
null | Date |
undefined | Array |
Symbol(符号,ES6+) | RegExp |
Boolean | 其他内置对象 |
BigInt(大整数,ES11+) | |
三. 核心区别
内存分配方式
- 基本数据类型:直接存储在栈内存(Stack)中,变量直接保存数据值。
let a = 10; // 栈中存储值 10
let b = a; // 栈中拷贝一份新的值 10 给 b
- 引用数据类型:数据存储在堆内存(Heap)中,变量保存的是堆内存地址(指针)。
let obj1 = { name: 'Alice' }; // 堆中存储对象,obj1 保存地址
let obj2 = obj1; // obj2 拷贝的是地址,指向同一个对象
赋值行为的差异
- 基本数据类型:赋值是 值的拷贝,新旧变量完全独立。
let a = 10;
let b = a;
b = 20;
console.log(a); // 10(a 不受影响)
- 引用数据类型:赋值是 地址的拷贝,新旧变量共享同一数据。
let obj1 = { count: 0 };
let obj2 = obj1;
obj2.count = 100;
console.log(obj1.count); // 100(共享同一对象)
比较操作的差异
- 基本数据类型:直接比较 值 是否相等。
let a = 10;
let b = 10;
console.log(a === b); // true(值相同)
- 引用数据类型:比较的是 内存地址 是否相同,而非内容。
let arr1 = [1, 2];
let arr2 = [1, 2];
console.log(arr1 === arr2); // false(地址不同)
函数参数传递的差异
- 基本数据类型:传递的是 值的副本,函数内部修改不影响外部变量。
function changeValue(num) {
num = 100;
}
let a = 10;
changeValue(a);
console.log(a); // 10(不变)
- 引用数据类型:传递的是 地址的副本,函数内部修改会直接影响外部对象。
function updateObj(obj) {
obj.key = 'new value';
}
let myObj = { key: 'old value' };
updateObj(myObj);
console.log(myObj.key); // 'new value'(被修改)
可变性(Mutability)
- 基本数据类型:不可变(Immutable),任何操作都会创建新值。
let str = 'hello';
str.toUpperCase(); // 返回新字符串 'HELLO'
console.log(str); // 'hello'(原值未变)
- 引用数据类型:可变(Mutable),可以直接修改内容。
let arr = [1, 2];
arr.push(3);
console.log(arr); // [1, 2, 3](原数组改变)//有点牵强,毕竟数组操作方法也存在不改变原数组的方法
垃圾回收机制
- 基本数据类型:变量销毁时,栈内存直接释放。
- 引用数据类型:需要依赖垃圾回收器(GC)检查堆内存中的对象是否无引用,再决定回收。(具体可以看一下前面讲的垃圾回收机制)
四. 实际场景中的问题
- 深拷贝 vs 浅拷贝
引用类型的直接赋值(浅拷贝)会导致共享数据问题,需深拷贝解决:
// 浅拷贝
let obj = { a: 1 };
let shallowCopy = obj;
// 深拷贝(常用方法),后面再讲讲常用的深拷贝的方式吧
let deepCopy = JSON.parse(JSON.stringify(obj));
- 性能优化
基本类型操作更快(内存占用小),引用类型操作可能涉及堆内存访问。
五. 总结对比
特性 | 基本数据类型 | 引用数据类型 |
---|---|---|
存储位置 | 栈内存 | 堆内存 |
赋值行为 | 值拷贝 | 地址拷贝 |
比较方式 | 值比较 | 地址比较 |
参数传递 | 值传递 | 地址传递 |