大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
数组与常用方法
这一课你将能做到
- 创建数组,用下标读写,用
push追加,并说出下标从 0 开始的含义 - 会用
map/filter从列表「派生」新列表,知道它们不改原数组 - 知道
sort会改原数组,数字排序要写比较函数
为什么要学
课程序号、待办、搜索结果——都是一列东西。
数组(Array)就是有序清单:第 0 个、第 1 个……(下标从 0 开始!)
现代前端几乎处处在「把数组变成另一份数组再渲染」。
map/filter 用得越熟,后面学 React 列表渲染越轻松。
先建立图像
const arr = ["HTML", "CSS", "JS"];
下标: 0 1 2
长度:arr.length === 3
最后一个:arr[arr.length - 1]
两类方法要分清:
| 类型 | 例子 | 人话 |
|---|---|---|
| 可能改原数组 | push sort splice | 动原来的清单 |
| 返回新数组 | map filter slice toSorted(较新) | 派生一份,原清单可不动 |
scores ──filter(及格)──► pass ──map(加标签)──► labels
原数组是否被改?filter/map:通常不改
跟着做
步骤 1:基础操作
const list = ["HTML", "CSS"];
list.push("JS"); // 末尾加,改原数组
console.log(list[0]); // "HTML"
console.log(list.length); // 3
console.log(list.at(-1)); // "JS"(现代写法:倒数第一个)
你应看到长度变为 3。记住:越界读取得到 undefined,不会自动报错。
步骤 2:map / filter(超常用)
const scores = [90, 60, 80, 55];
const pass = scores.filter(function (s) {
return s >= 60; // 留下及格的
});
const labels = pass.map(function (s) {
return "分数:" + s; // 每一项变成新字符串
});
console.log(pass); // [90, 60, 80]
console.log(labels); // ["分数:90", ...]
console.log(scores); // 原数组仍在,未被 filter/map 改掉
箭头写法:
const pass2 = scores.filter((s) => s >= 60);
const doubled = scores.map((s) => s * 2);
步骤 3:sort 的坑与安全排序
const nums = [10, 2, 1];
nums.sort(); // 默认按字符串规则排 → 可能变成 [1, 10, 2]
console.log("默认 sort:", nums);
const nums2 = [10, 2, 1];
nums2.sort((a, b) => a - b); // 数字升序
console.log("比较函数:", nums2); // [1, 2, 10]
// 不想改原数据:先复制再排
const nums3 = [10, 2, 1];
const sorted = [...nums3].sort((a, b) => a - b);
console.log("原数组", nums3, "新数组", sorted);
sort 会改原数组。这是最常见的坑之一。
步骤 4:find / includes / join
const lessons = ["HTML", "CSS", "JS"];
console.log(lessons.includes("CSS")); // true
console.log(lessons.find((t) => t.startsWith("J"))); // "JS"
console.log(lessons.join(" → ")); // "HTML → CSS → JS"
includes 问「有没有」;find 找「第一个符合的元素」;join 拼成字符串做展示很好用。
步骤 5:渲染到页面
保存为 js-arrays.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>数组</title>
</head>
<body>
<ul id="ul"></ul>
<p id="summary"></p>
<script>
const lessons = ["HTML", "CSS", "JS"];
const ul = document.getElementById("ul");
for (const title of lessons) {
const li = document.createElement("li");
li.textContent = title;
ul.appendChild(li);
}
// 用 map + join 生成摘要文案
const summary = lessons.map((t, i) => `${i + 1}.${t}`).join(" / ");
document.getElementById("summary").textContent = summary;
</script>
</body>
</html>
你应看到列表和一行摘要。数据在数组里,界面由数组派生。
步骤 6:链式调用与 reduce 混脸熟
const scores = [90, 60, 80, 55];
const avgPass = scores
.filter((s) => s >= 60)
.map((s) => s) // 这里可换成更复杂的变换
.reduce((sum, s, _, arr) => sum + s / arr.length, 0);
console.log("及格平均分约:", avgPass);
// reduce 人话:把数组「折叠」成一个值(总和、对象、字符串…)
const total = scores.reduce((sum, s) => sum + s, 0);
console.log("总分", total);
链式从左到右读:先过滤,再映射,再折叠。初学先会 filter/map,reduce 见过能读即可。
讲清楚要点
- 因为下标从 0 开始,所以最后一个是
length - 1,循环条件常写i < length。 - 因为
map必须有返回值,所以忘了return会得到一堆undefined。 - 因为
forEach适合「做副作用」(打印、改 DOM),所以要得到新数组请用map。 - 因为
sort默认按字符串比较且会改原数组,所以数字排序写比较函数,必要时先复制。 - 因为链式调用从左到右,所以读代码时按管道理解:进什么、出什么。
- 因为稀疏空位会制造诡异行为,所以初学用
push正常添加,别手动制造空洞。
你可能会懵的地方
-
filter会改变原数组吗?
不会。它返回新数组。push/sort/splice才会动原数组。 -
回调函数的参数是什么?
常见是(当前项, 下标, 原数组)。先会用当前项即可。 -
map和for谁更好?
要「派生新数组」用map更清晰;要复杂控制流用for/for...of也完全合理。 -
toSorted/toReversed?
较新的「不改原数组」版本。环境支持时可用来减少复制样板代码;不支持时用扩展运算复制即可。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
数字 sort() 不写比较器 | 顺序错 | (a,b)=>a-b |
map 忘了 return | 全是 undefined | 写清返回值 |
用 for...in 遍历数组 | 键与顺序坑 | for...of / 数组方法 |
| 误以为 filter 会改原数组 | 改错数据源 | 分清突变 / 非突变方法 |
| 用下标硬编码长度 | 列表一变就崩 | 用 length 或 for...of |
自测三问
- 下标为什么从 0 开始要特别记住?最后一个元素怎么取?
filter和map各适合什么?会不会改原数组?- 为什么说
sort危险?如何数字升序且保留原数组?
小结一句话
数组是有序清单;读写靠下标,变换优先 filter/map,排序记得比较函数且注意会改原数组。