🧠 引言
你有没有想过:
- JS 中
arr[i]背后到底干了什么? - 为啥 C 语言里数组名就像个指针?
- JS 没有指针,但我们真就不需要理解指针了吗?
本篇文章用一种**“双语对照”**(JavaScript vs C)的方式,带你从底层视角看清楚:数组的本质其实就是“地址 + 偏移”,而理解它,会对你掌握更高阶算法(如:内存操作、位运算、动态数组)大有裨益!
🔬 一、数组是啥?从底层说起
数组的本质:
一段连续内存,按固定步长排列的变量集合。
🎯 JavaScript 中的数组
const arr = [10, 20, 30];
console.log(arr[1]); // 输出:20
JS 的数组是对象的“特殊变种”,可以动态扩容,性能与底层实现相关(稀疏 vs 密集数组)。
🎯 C 语言中的数组
int arr[3] = {10, 20, 30};
printf("%d\n", arr[1]); // 输出:20
C 语言中,arr 实质是指向第一个元素的地址。
*(arr + 1) == arr[1]; // 完全等价
🧱 二、JS 模拟数组底层行为
我们可以自己模拟数组的底层机制,帮助理解。
✅ 构建一个“定长数组类”:
class ArrayLike {
constructor(size) {
this.buffer = new Array(size);
}
set(index, value) {
if (index >= this.buffer.length) throw new Error('越界访问');
this.buffer[index] = value;
}
get(index) {
if (index >= this.buffer.length) throw new Error('越界访问');
return this.buffer[index];
}
}
const arr = new ArrayLike(3);
arr.set(0, 10);
arr.set(1, 20);
arr.set(2, 30);
console.log(arr.get(1)); // 输出:20
🧠 思考:
- JS 封装后就像“安全的 C 数组”
index实质就是地址偏移(地址 + index * size)
🧪 三、C 中的数组 VS 指针本质
int arr[3] = {10, 20, 30};
int *p = arr;
printf("%d\n", *(p + 1)); // 输出 20
| 表达式 | 含义 |
|---|---|
arr | 首地址(int* 类型) |
arr[i] | *(arr + i) |
p[i] | *(p + i) |
&arr[i] | 获取第 i 个元素地址 |
你以为是数组,实际是“偏移指针+地址”结构!
📚 四、为什么 JS 没有指针,也要懂它?
虽然 JS 没有暴露指针语法,但底层仍依赖:
- TypedArray(如:Uint8Array) → 内存块操作
- ArrayBuffer + DataView → 模拟内存读写
- WebAssembly / WebGPU / 图像处理 → 离不开地址操作
✅ 示例:用 TypedArray 写一个内存块
const buffer = new ArrayBuffer(8); // 8字节
const view = new Int32Array(buffer); // 每个占4字节
view[0] = 100;
view[1] = 200;
console.log(view); // 输出:Int32Array(2) [100, 200]
⚠️ 五、易错点提示
| 场景 | 错误理解 | 正确理解 |
|---|---|---|
| JS数组是连续内存? | 不一定(取决于底层优化) | 对象变种,可能稀疏或优化成稠密 |
| C 的数组是对象? | 不是,对象和数组完全不同 | 是内存地址的指针偏移结构 |
arr[i] 本质? | 表面是索引访问 | 实际是 *(arr + i) |
🔄 六、拓展任务
- 手动实现一个基于
ArrayBuffer的 Matrix 类 - 写一个 C 函数,将数组逆序输出(用指针实现)
- 尝试在 JS 中模拟实现
memcpy()函数
🧩 总结一句话
前端工程师理解数组背后的“指针模型”,将为你进入更高性能的计算与算法世界打开大门!
下一篇预告:
📘 第4篇:【图解哈希表】从手写 Map 到哈希冲突解决策略!