7. 数据存储—栈(stack)和堆(heap)
-
前面我讲过, 数据类型分为基本数据类型(简单数据类型)和复杂数据类型(引用数据类型)
-
基本数据类型和复杂数据类型最大的区别就是存储上的区别
-
下图里的灰色区域可以看做我们的内存空间 , 黄色是栈空间 , 橘黄色是堆空间
-
简单数据类型存储在栈空间里,比如变量名函数名 , 复杂数据类型存储在堆空间里
-
栈内存: 按序排列, 先来的在栈底, 后来的在栈顶 , 就像是堆叠盘子一样的
-
在堆内存中, 数据是无序排列的, 根据地址来查找(堆空间地址)
1) 基本数据类型存储
值存储在栈中,直接操作值,赋值时是值的副本
下面看一段代码感受一下:
因为栈区和堆区存储的数据类型是不一样的, 所以有些操作也是不一样的, 比如说赋值
let a=10;//基本类型,值存储在栈内存
let b=a;// b 得到的是 a 的值的副本
a=20;//改变 a 不会影响 b,因为 b 和 a 是独立的副本
console.log(b);//结果是10, a的值改变不会影响b的值
图解:
+-------------------+
| b: 10 | (不变)
+-------------------+
| a:10 ---> a: 20 | (修改了 a 的值)
+-------------------+
不能重复声明变量
let a = 10;
let b = a;
a = 20;
console.log(b);
let a//会报错 先let a后var a 是可以的 但是不能用let
//如果是var a再let a也是有问题的
//总之有let就不能重复
2) 复杂数据类型存储
- 复杂数据类型的存储
- 在堆里面开辟一个存储空间
- 把数据存储到存储空间内
- 把存储空间的地址赋值给栈里面的变量
let a = 10;
let b = a;
a = 20;
console.log(b);
let obj1 = { a: 1 };
let obj2 = obj1;
//obj1.a = 10;
obj1 = { a: 10 }; //重新赋值了,有一个obj1指向了一个新的地址
console.log(obj2);//a:1
图解:
矩形是栈区 椭圆形是堆区
用椭圆形表示也是为了说明堆区的内存是可以动态分配的 栈区是固定的
obj1的值通过指针指向堆区的内存地址
obj1 = { a: 10 };重新赋值了,有一个obj1指向了一个新的地址
而obj2还是指向原来的地址 所以a:1
var obj = {
name: 'Jack',
age: 18,
gender: '男'
}
总结
栈:
值存储在栈中,直接操作值,赋值时是值的副本
堆:
实际对象存储在堆中,栈中存储的是指向堆内存的引用。
重新赋值会改变引用,但不会影响原来的对象。
3) 数据类型赋值
- 基本数据类型赋值
- 赋值的时候, 就是直接值的复制
- 赋值以后, 两个变量互相之间没有任何关系
- 改变一个变量, 另一个不会发生变化
var n = 10
// 把 n 存储的值赋值给了 m 变量
var m = n
console.log(m)
console.log(n)
m = 20
console.log(m)
console.log(n)
- 复杂数据类型赋值
- 复杂数据类型赋值的时候, 是把变量内存储的地址进行赋值
- 赋值以后, 两个变量操作的是一个存储空间
- 任意一个变量去改变存储空间内的数据, 另一个变量看到的一样是改变以后的
var obj = { name: '武松' }
// 把 obj 存储的值复制了一份给了 obj2
var obj2 = obj
console.log(obj)
console.log(obj2)
obj2.name = '宋江'
console.log(obj)
console.log(obj2)
4) 数据类型比较
- 基本数据类型, 就是 值 和 值 之间的比较
var n = 10
var m = 10
console.log(n == m)
- 复杂数据类型, 是 地址 和 地址 之间的比较
var o1 = {
name: 'Jack'
}
var o2 = {
name: 'Jack'
}
console.log(o1 == o2) //false
- 因为我们创建了两个对象,那么就会在 堆空间 里面开辟两个存储空间存储数据(两个地址)
- 虽然存储的内容是一样的,那么也是两个存储空间,两个地址
- 复杂数据类型之间就是地址的比较,所以 obj 和 obj2 两个变量的地址不一样
- 所以我们得到的就是 false
8作业题
1. 购物车
假设你正在开发一个购物网站,需要计算用户购物车中商品的总价。每个商品有一个名称、单价和数量。请编写一个函数,接收一个包含多个商品对象的数组,计算购物车中所有商品的总价,并显示每个商品的名称、单价、数量以及该商品的总价。
要求:
商品对象包含 name、price 和 quantity 属性。
返回每个商品的总价,以及所有商品的总价。
提示:
function calculateCartTotal(cart) {
let total = 0;
cart.forEach(item => {
// 计算每个商品的总价
// 输出每个商品的详细信息
});
// 返回购物车的总价
}
2. 银行账户余额管理
问题描述:你正在开发一个银行账户管理系统。每个账户有一个账户余额,你需要实现存款、取款、转账等功能。请编写一个函数,模拟用户向账户存款,取款以及查询账户余额。
要求:
提供 deposit、withdraw 和 checkBalance 函数。
确保取款金额不能大于账户余额。
提示
class BankAccount {
constructor(balance) {
this.balance = balance;
}
deposit(amount) {
// 存款功能
}
withdraw(amount) {
// 取款功能,判断余额是否足够
}
checkBalance() {
// 输出账户余额
}
}
3. 温度转换
问题描述:编写一个温度转换工具,能够将摄氏度转换为华氏度,或者将华氏度转换为摄氏度。要求用户输入温度的数值和类型(摄氏度或华氏度),然后输出转换后的结果。
要求:
实现两个函数:一个将摄氏度转为华氏度,另一个将华氏度转为摄氏度。
提供输入和输出的示例。
提示:
function celsiusToFahrenheit(celsius) {
// 将摄氏度转换为华氏度
}
function fahrenheitToCelsius(fahrenheit) {
// 将华氏度转换为摄氏度
}
4. 学生成绩排名
问题描述:你正在设计一个学生成绩管理系统,要求实现一个函数,能够输入一组学生的成绩,并按照成绩从高到低排序,然后输出排名。
要求:
输入一个包含学生名字和成绩的数组。
输出按成绩排序后的学生名单和对应成绩。
框架
function rankStudents(students) {
// 按照成绩从高到低排序
// 输出排序后的学生名单和成绩
}
9 答案
1
function calculateCartTotal(cart) {
let total = 0;
cart.forEach(item => {
const itemTotal = item.price * item.quantity;
total += itemTotal;
console.log(`${item.name}: 单价 ${item.price}, 数量 ${item.quantity}, 总价 ${itemTotal}`);
});
console.log(`购物车总价: ${total}`);
}
// 示例使用
const cart = [
{ name: "苹果", price: 3, quantity: 5 },
{ name: "香蕉", price: 2, quantity: 3 },
{ name: "橙子", price: 4, quantity: 2 }
];
calculateCartTotal(cart);
结果:
苹果: 单价 3, 数量 5, 总价 15
香蕉: 单价 2, 数量 3, 总价 6
橙子: 单价 4, 数量 2, 总价 8
购物车总价: 29
- 银行
class BankAccount {
constructor(balance) {
this.balance = balance;
}
deposit(amount) {
if (amount > 0) {
this.balance += amount;
console.log(`成功存款 ${amount} 元`);
} else {
console.log("存款金额必须大于 0");
}
}
withdraw(amount) {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
console.log(`成功取款 ${amount} 元`);
} else if (amount > this.balance) {
console.log("余额不足");
} else {
console.log("取款金额必须大于 0");
}
}
checkBalance() {
console.log(`当前账户余额: ${this.balance} 元`);
}
}
// 示例使用
const account = new BankAccount(1000);
account.deposit(500); // 存款 500 元
account.withdraw(200); // 取款 200 元
account.checkBalance(); // 查看余额
结果:
成功存款 500 元
成功取款 200 元
当前账户余额: 1300 元
- 温度转换
function celsiusToFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
function fahrenheitToCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
// 示例使用
const celsius = 25;
const fahrenheit = celsiusToFahrenheit(celsius);
console.log(`${celsius} °C = ${fahrenheit} °F`);
const fahrenheitInput = 77;
const celsiusConverted = fahrenheitToCelsius(fahrenheitInput);
console.log(`${fahrenheitInput} °F = ${celsiusConverted.toFixed(2)} °C`);
结果
25 °C = 77 °F
77 °F = 25.00 °C
- 学生成绩排名
function rankStudents(students) {
students.sort((a, b) => b.score - a.score);
students.forEach((student, index) => {
console.log(`${index + 1}. ${student.name} - 成绩: ${student.score}`);
});
}
// 示例使用
const students = [
{ name: "张三", score: 85 },
{ name: "李四", score: 92 },
{ name: "王五", score: 78 },
{ name: "赵六", score: 88 }
];
rankStudents(students);
结果
1. 李四 - 成绩: 92
2. 赵六 - 成绩: 88
3. 张三 - 成绩: 85
4. 王五 - 成绩: 78