我们先来简单了解一下内存。
在计算机中,内存是有许多个小格子组成的,每个格子都有一个唯一的编号,这个编号就被称为内存地址。我们可以使用连续的数字来表示这些内存地址,例如 0x00000000
、0x00000001
、0x00000002
等。简化的示例图如下所示
一个格子代表一个空间,每个空间都有一个唯一的内存地址。
在 C 语言中,由于各自有具体的语法支持,因此我们需要对变量、指针、引用等进行非常明确的区分。
1、变量
变量是存储数据的容器,它直接存储具体的值,例如整数、浮点数、字符等。因此变量代表的就是空间本身。
如下代码所示,我们声明了一个变量 a,它的值是 20。对应的内存示意图如下所示
int a = 20;
此时,如果我们另外声明了一个变量 b,并将 a 赋值给他
1int a = 20;+2int b = a;
由于变量代表的是空间本身,因此,此时新增了一个变量 b,就需要在内存中新增一个空间。赋值过程则是将 a 的值复制到 b 中。在内存中的表现为
由于是代表了不同的内存空间,因此,变量 a 和 b 的变量是两个不同的变量,当他们任意一个的值发生改变时,都不会影响到另外一个变量。
1int a = 20;2int b = a;+3b = 30; // 修改 b 的值,不会影响 a 的值
2、指针
指针是一种特殊的变量,它存储的是另一个变量的内存地址。因此,指针代表的是一个内存地址,而不是一个值本身。例如,我们使用如下代码来定义一个指针变量 *ptr
,并将其赋值为变量 a
的地址。
1int a = 10;2// 定义一个指针变量3int *ptr = &a; // ptr 存储的是 a 的地址
由于是指向的同一个内存地址,因此,当我们修改 ptr
的值时,实际上是修改了 a
的值。
1int a = 10;2int *ptr = &a; // ptr 存储的是 a 的地址+3*ptr = 20; // 修改 ptr 指向的值,会影响 a 的值
3、引用
引用和指针表示类似的含义,它是变量的别名,是另外一种间接访问变量的方式。
在内存中,引用和指针有相同的表现形式。只不过在 c 语言上,对他们在语法和使用规则上进行了区分和不同的设定。通过如下的案例简单了解即可
10int main() {20int x = 10;3040// 引用50int& ref = x; // 必须初始化60ref = 20; // 直接使用,不需要解引用7080// 指针90int* ptr = &x; // 可以稍后初始化10*ptr = 30; // 需要解引用11ptr = nullptr; // 可以指向NULL1213// 引用不能重新绑定14int y = 40;15// ref = y; // 错误:这会将y的值赋给x,而不是重新绑定ref1617return 0;18}
c 语言中的细节比较多,有需要区分的地方。
尽管如此,他们所代表的含义实际上也是相似的。因此,在 JavaScript 中,从语法上做了统一与简化,我们只需要学习变量的声明与使用即可。以及分清楚基础数据类型和引用数据类型在内存中的表现形式。
JS 中的变量是一种非常灵活的概念,它可以存储不同类型的值,包括基本数据类型和引用数据类型。我们可以使用 var
let
const
等关键字来声明变量。
1var a = 10; // 基本数据类型2var b = { name: 'John' }; // 引用数据类型34let c = 20; // 基本数据类型5let d = { name: 'Jane' }; // 引用数据类型67const e = 30; // 基本数据类型8const f = { name: 'Jim' }; // 引用数据类型
在内存表现中,基本数据类型的复制是直接将值复制到新的内存空间中,因此,当我们修改其中一个变量的值时,另一个变量的值不会发生改变。
1var a = 20; // 基本数据类型2var b = a
而引用数据类型的复制是将内存地址复制到新的内存空间中,但是它们指向的是同一个对象。因此,当我们修改其中一个变量的值时,另一个变量的值也会发生改变。
1var a = { name: 'John' }; // 引用数据类型2var b = a;
其他基础知识,例如
var let const
的区别,变量对象,变量提升等大家可以在 《JavaScript 核心进阶》中进一步学习
在算法中,我们经常需要对变量的值进行置换,例如交换两个变量的值。在 JavaScript 中,我们可以使用如下代码来实现变量值的置换。
1var a = 10;2var b = 20;3// 变量值置换4var temp = a;5a = b;6b = temp;7console.log(a); // 208console.log(b); // 10
置换的思路就是通过一个临时变量来存储其中一个变量的值,然后再将另外一个变量的值赋值给第一个变量,最后再将临时变量的值赋值给第二个变量。内存中的表现如下所示
第一步,使用 a
赋值给临时变量 temp
var temp = a;
第二步,使用 b
赋值给 a
: a = b;
第三步,使用 temp
赋值给 b
: b = temp;
此时,变量 a
和 b
的值已经发生了置换。
在后续我们要学习各种排序、链表等内容的过程中,会大量使用到变量值置换的操作。
除此之外,在 JS 中,我们还可以使用数组的解构来实现变量值的置换。
1var a = 10;2var b = 20;3// 变量值置换4[a, b] = [b, a];5console.log(a); // 206console.log(b); // 10
在面试场景中,如果遇到面试官追问还有什么更好的方式时,我们还可以使用按位异或运算来实现变量值的置换。但是要注意:此时,a
和 b
必须是整数。
此方法可以不用理解,背下来即可。
1var a = 10;2var b = 20;3// 变量值置换4a = a ^ b;5b = a ^ b;6a = a ^ b;7console.log(a); // 208console.log(b); // 10
当然,当 a
和 b
是数字时,我们还可以使用 a + b
和 a - b
来实现变量值的置换。
1var a = 10;2var b = 20;3// 变量值置换4a = a + b;5b = a - b;6a = a - b;7console.log(a); // 208console.log(b); // 10
后面这两种方式,可以少用一个临时变量。在面试场景非常有用