我们先来简单了解一下内存

在计算机中,内存是有许多个小格子组成的,每个格子都有一个唯一的编号,这个编号就被称为内存地址。我们可以使用连续的数字来表示这些内存地址,例如 0x000000000x000000010x00000002 等。简化的示例图如下所示

一个格子代表一个空间,每个空间都有一个唯一的内存地址。

在 C 语言中,由于各自有具体的语法支持,因此我们需要对变量、指针、引用等进行非常明确的区分。

1、变量

变量是存储数据的容器,它直接存储具体的值,例如整数、浮点数、字符等。因此变量代表的就是空间本身。

如下代码所示,我们声明了一个变量 a,它的值是 20。对应的内存示意图如下所示

int a = 20;

此时,如果我们另外声明了一个变量 b,并将 a 赋值给他

1
int a = 20;
+
2
int b = a;

由于变量代表的是空间本身,因此,此时新增了一个变量 b,就需要在内存中新增一个空间。赋值过程则是将 a 的值复制到 b 中。在内存中的表现为

由于是代表了不同的内存空间,因此,变量 a 和 b 的变量是两个不同的变量,当他们任意一个的值发生改变时,都不会影响到另外一个变量。

1
int a = 20;
2
int b = a;
+
3
b = 30; // 修改 b 的值,不会影响 a 的值

2、指针

指针是一种特殊的变量,它存储的是另一个变量的内存地址。因此,指针代表的是一个内存地址,而不是一个值本身。例如,我们使用如下代码来定义一个指针变量 *ptr,并将其赋值为变量 a 的地址。

1
int a = 10;
2
// 定义一个指针变量
3
int *ptr = &a; // ptr 存储的是 a 的地址

由于是指向的同一个内存地址,因此,当我们修改 ptr 的值时,实际上是修改了 a 的值。

1
int a = 10;
2
int *ptr = &a; // ptr 存储的是 a 的地址
+
3
*ptr = 20; // 修改 ptr 指向的值,会影响 a 的值

3、引用

引用和指针表示类似的含义,它是变量的别名,是另外一种间接访问变量的方式。

在内存中,引用和指针有相同的表现形式。只不过在 c 语言上,对他们在语法和使用规则上进行了区分和不同的设定。通过如下的案例简单了解即可

1
int main() {
2
int x = 10;
3
4
// 引用
5
int& ref = x; // 必须初始化
6
ref = 20; // 直接使用,不需要解引用
7
8
// 指针
9
int* ptr = &x; // 可以稍后初始化
10
*ptr = 30; // 需要解引用
11
ptr = nullptr; // 可以指向NULL
12
13
// 引用不能重新绑定
14
int y = 40;
15
// ref = y; // 错误:这会将y的值赋给x,而不是重新绑定ref
16
17
return 0;
18
}

c 语言中的细节比较多,有需要区分的地方。

尽管如此,他们所代表的含义实际上也是相似的。因此,在 JavaScript 中,从语法上做了统一与简化,我们只需要学习变量的声明与使用即可。以及分清楚基础数据类型和引用数据类型在内存中的表现形式。

JavaScript 中的变量

JS 中的变量是一种非常灵活的概念,它可以存储不同类型的值,包括基本数据类型和引用数据类型。我们可以使用 var let const 等关键字来声明变量。

1
var a = 10; // 基本数据类型
2
var b = { name: 'John' }; // 引用数据类型
3
4
let c = 20; // 基本数据类型
5
let d = { name: 'Jane' }; // 引用数据类型
6
7
const e = 30; // 基本数据类型
8
const f = { name: 'Jim' }; // 引用数据类型

在内存表现中,基本数据类型的复制是直接将值复制到新的内存空间中,因此,当我们修改其中一个变量的值时,另一个变量的值不会发生改变。

1
var a = 20; // 基本数据类型
2
var b = a

而引用数据类型的复制是将内存地址复制到新的内存空间中,但是它们指向的是同一个对象。因此,当我们修改其中一个变量的值时,另一个变量的值也会发生改变。

1
var a = { name: 'John' }; // 引用数据类型
2
var b = a;

其他基础知识,例如 var let const 的区别,变量对象,变量提升等大家可以在 《JavaScript 核心进阶》中进一步学习

变量值置换

在算法中,我们经常需要对变量的值进行置换,例如交换两个变量的值。在 JavaScript 中,我们可以使用如下代码来实现变量值的置换。

1
var a = 10;
2
var b = 20;
3
// 变量值置换
4
var temp = a;
5
a = b;
6
b = temp;
7
console.log(a); // 20
8
console.log(b); // 10

置换的思路就是通过一个临时变量来存储其中一个变量的值,然后再将另外一个变量的值赋值给第一个变量,最后再将临时变量的值赋值给第二个变量。内存中的表现如下所示

第一步,使用 a 赋值给临时变量 temp var temp = a;

第二步,使用 b 赋值给 a: a = b;

第三步,使用 temp 赋值给 b: b = temp;

此时,变量 ab 的值已经发生了置换。

在后续我们要学习各种排序、链表等内容的过程中,会大量使用到变量值置换的操作。

除此之外,在 JS 中,我们还可以使用数组的解构来实现变量值的置换。

1
var a = 10;
2
var b = 20;
3
// 变量值置换
4
[a, b] = [b, a];
5
console.log(a); // 20
6
console.log(b); // 10

在面试场景中,如果遇到面试官追问还有什么更好的方式时,我们还可以使用按位异或运算来实现变量值的置换。但是要注意:此时,ab 必须是整数

此方法可以不用理解,背下来即可。

1
var a = 10;
2
var b = 20;
3
// 变量值置换
4
a = a ^ b;
5
b = a ^ b;
6
a = a ^ b;
7
console.log(a); // 20
8
console.log(b); // 10

当然,当 ab 是数字时,我们还可以使用 a + ba - b 来实现变量值的置换。

1
var a = 10;
2
var b = 20;
3
// 变量值置换
4
a = a + b;
5
b = a - b;
6
a = a - b;
7
console.log(a); // 20
8
console.log(b); // 10

后面这两种方式,可以少用一个临时变量。在面试场景非常有用

专栏首页
到顶
专栏目录