JavaScript: JavaScript 数组进阶

上一课学了数组的基础操作,这课来学数组的"大招"——高阶方法。这些方法能让你用更少的代码做更多的事,是现代 JavaScript 开发的核心技能。

1. map — 映射

map 对数组每个元素执行函数,返回一个新数组,原数组不变。就像一条流水线:每个元素进去,加工后出来,组成新产品。

HTML
<script>
let prices = [10, 20, 30];
let doubled = prices.map(p => p * 2);   // [20, 40, 60]
</script>

map 的回调函数接收三个参数:(元素, 索引, 原数组),最常用的是第一个。


▶ 示例

JAVASCRIPT
// 本节代码示例
const message = "你好,世界!";
console.log(message);
▶ 试一试

这是最简单的示例,后续章节会详细解释。## 2. filter — 过滤

filter 筛选满足条件的元素,返回新数组,原数组不变。像安检:符合条件的放行,不符合的拦下。

HTML
<script>
let scores = [45, 80, 92, 55, 78, 98];
let passed = scores.filter(s => s >= 60);  // [80, 92, 78, 98]
</script>

回调函数返回 true 的元素保留,返回 false 的丢弃。


3. reduce — 归并

reduce 把数组"压缩"成一个值。最经典的用法是求和:

HTML
<script>
let nums = [1, 2, 3, 4, 5];
let sum = nums.reduce((acc, cur) => acc + cur, 0);  // 15
</script>

两个参数的含义:

💡 提示: reduce 一开始最难理解,但它的本质就一句话:把数组一步步"压缩"成一个值。求和、求最大值、统计次数都能用它。

▶ 示例:map、filter、reduce 组合

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>map / filter / reduce</h2>
  <div id="output"></div>
  <script>
    let products = [
      { name: '笔记本', price: 4999, category: '电子' },
      { name: 'T恤', price: 89, category: '服装' },
      { name: '鼠标', price: 199, category: '电子' },
      { name: '牛仔裤', price: 299, category: '服装' },
      { name: '键盘', price: 399, category: '电子' }
    ];

    let html = '<strong>所有商品:</strong><br>';
    products.forEach(p => html += `${p.name} - ¥${p.price} (${p.category})<br>`);

    let electronics = products.filter(p => p.category === '电子');
    html += '<br><strong>电子类商品(filter):</strong><br>';
    electronics.forEach(p => html += `${p.name} - ¥${p.price}<br>`);

    let names = products.map(p => p.name);
    html += '<br><strong>商品名列表(map):</strong>' + names.join(', ') + '<br>';

    let total = products.reduce((sum, p) => sum + p.price, 0);
    html += '<br><strong>总价(reduce):</strong>¥' + total;

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>
▶ 试一试

4. find 和 findIndex

找不到时,find 返回 undefinedfindIndex 返回 -1

HTML
<script>
let users = [
  { name: '小明', age: 20 },
  { name: '小红', age: 17 },
  { name: '小刚', age: 25 }
];

users.find(u => u.age > 22);       // { name: '小刚', age: 25 }
users.findIndex(u => u.age < 18);  // 1(小红的索引)
</script>

5. sort — 排序

sort 对数组原地排序(会改变原数组!)。默认按字符串排序,这坑了不少人:

HTML
<script>
let nums = [10, 1, 21, 2];
nums.sort();           // [1, 10, 2, 21]  ← 字符串排序!"10" < "2"

// 正确:数字排序需要传比较函数
nums.sort((a, b) => a - b);   // [1, 2, 10, 21]  ← 从小到大
nums.sort((a, b) => b - a);   // [21, 10, 2, 1]  ← 从大到小
</script>
⚠️ 注意: sort 会修改原数组!不想改原数组,先拷贝一份再排序:[...arr].sort(...)

▶ 示例:学生成绩排序

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>学生成绩排序</h2>
  <div id="output"></div>
  <script>
    let students = [
      { name: '小明', score: 72 },
      { name: '小红', score: 95 },
      { name: '小刚', score: 88 },
      { name: '小丽', score: 61 },
      { name: '小华', score: 95 }
    ];

    let html = '<strong>原始顺序:</strong><br>';
    students.forEach(s => html += `${s.name} - ${s.score}分<br>`);

    let byScore = [...students].sort((a, b) => b.score - a.score);
    html += '<br><strong>按分数降序:</strong><br>';
    byScore.forEach((s, i) => {
      let medal = i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : '';
      html += `${medal} ${s.name} - ${s.score}分<br>`;
    });

    let topStudents = students.filter(s => s.score >= 90);
    html += '<br><strong>90分以上(filter):</strong><br>';
    topStudents.forEach(s => html += `${s.name} - ${s.score}分<br>`);

    let found = students.find(s => s.name === '小刚');
    html += `<br><strong>查找"小刚"(find):</strong>${found ? found.score + '分' : '未找到'}`;

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>
▶ 试一试

6. some 和 every

HTML
<script>
let scores = [60, 75, 82, 90];

scores.some(s => s >= 90);    // true  有人90分以上
scores.some(s => s === 100);  // false 没人满分
scores.every(s => s >= 60);   // true  全部及格
</script>
💡 提示: some 像在问"有没有人...",every 像在问"是不是所有人都..."。一问存在,一问全部。


7. 展开运算符(...)

展开运算符 ... 可以"拆开"数组,常用于复制和合并:

HTML
<script>
let a = [1, 2, 3];
let b = [...a];          // 复制:[1, 2, 3](新数组,不是引用)
let c = [...a, 4, 5];    // 合并:[1, 2, 3, 4, 5]
let d = [0, ...a, 4];    // 插入:[0, 1, 2, 3, 4]

let x = [1, 2], y = [3, 4];
let z = [...x, ...y];    // [1, 2, 3, 4]
</script>

8. 解构赋值

从数组中提取值,按位置赋给变量:

HTML
<script>
let [a, b, c] = [1, 2, 3];
// a = 1, b = 2, c = 3

let [first, ...rest] = [1, 2, 3, 4, 5];
// first = 1, rest = [2, 3, 4, 5]

let [name, score] = ['小明', 95];
// name = '小明', score = 95
</script>
💡 提示: 解构赋值让代码更简洁。函数返回多个值时,用数组包装再解构是常见模式。


9. 链式调用

高阶方法返回新数组,所以可以链式调用——一个接一个:

HTML
<script>
let result = students
  .filter(s => s.score >= 60)     // 先过滤及格的
  .map(s => s.name)               // 再取名字
  .sort();                         // 最后排序
</script>

▶ 示例:链式调用处理数据

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>链式调用</h2>
  <div id="output"></div>
  <script>
    let employees = [
      { name: '张三', dept: '技术部', salary: 15000 },
      { name: '李四', dept: '销售部', salary: 8000 },
      { name: '王五', dept: '技术部', salary: 20000 },
      { name: '赵六', dept: '销售部', salary: 12000 },
      { name: '孙七', dept: '技术部', salary: 18000 },
      { name: '周八', dept: '市场部', salary: 10000 }
    ];

    let html = '<strong>所有员工:</strong><br>';
    employees.forEach(e => html += `${e.name} | ${e.dept} | ¥${e.salary}<br>`);

    let techHighSalary = employees
      .filter(e => e.dept === '技术部')
      .filter(e => e.salary >= 16000)
      .map(e => `${e.name}(¥${e.salary})`);
    html += '<br><strong>技术部高薪(链式filter+map):</strong><br>' + techHighSalary.join(', ');

    let avgSalary = employees
      .filter(e => e.dept === '技术部')
      .map(e => e.salary)
      .reduce((sum, s) => sum + s, 0);
    let count = employees.filter(e => e.dept === '技术部').length;
    html += `<br><br><strong>技术部平均薪资:</strong>¥${(avgSalary / count).toFixed(0)}`;

    let allAbove5k = employees.every(e => e.salary > 5000);
    let has20k = employees.some(e => e.salary >= 20000);
    html += `<br><br><strong>全员薪资>5000?</strong>${allAbove5k ? '是' : '否'}`;
    html += `<br><strong>有人薪资≥20000?</strong>${has20k ? '是' : '否'}`;

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>
▶ 试一试

▶ 示例:展开运算符与解构

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>展开运算符与解构</h2>
  <div id="output"></div>
  <script>
    let a = [1, 2, 3];
    let b = [...a];
    b.push(4);
    let html = `<strong>展开运算符复制:</strong><br>`;
    html += `原数组 a: [${a}]<br>`;
    html += `副本 b: [${b}]<br>`;
    html += `修改 b 不影响 a<br><br>`;

    let x = [1, 2], y = [3, 4];
    let merged = [...x, ...y, 5];
    html += `<strong>合并数组:</strong>[${merged}]<br><br>`;

    let [first, second, ...rest] = [10, 20, 30, 40, 50];
    html += `<strong>解构赋值:</strong><br>`;
    html += `first = ${first}<br>`;
    html += `second = ${second}<br>`;
    html += `rest = [${rest}]<br><br>`;

    let scores = [85, 92, 78, 95, 60];
    let [highest, ...others] = [...scores].sort((a, b) => b - a);
    html += `<strong>解构+排序:</strong><br>`;
    html += `最高分 = ${highest}<br>`;
    html += `其余 = [${others}]`;

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>
▶ 试一试

❓ 常见问题

Q mapforEach 有什么区别?
A map 返回新数组,forEach 返回 undefined。需要用结果就选 map,只是执行操作就选 forEach。别在 forEach 里手动 push 到外部数组——那该用 map
Q sort 为什么会改变原数组?
A 这是历史设计。sort 采用原地排序(in-place),修改原数组而不是返回新数组。不想改原数组就先拷贝:[...arr].sort(...)
Q reduce 的初始值可以省略吗?
A 可以,省略时以数组第一个元素作为初始值,从第二个元素开始遍历。但空数组省略初始值会报错,所以建议始终写上初始值,更安全。

📖 小节

📝 作业

  1. 给定数组 [3, 8, 12, 5, 9, 17, 4],用 filter 筛选出大于 7 的数,再用 map 将它们翻倍,输出结果
  2. reduce 统计一个字符串数组中每个单词出现的次数,如 ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']{ apple: 3, banana: 2, cherry: 1 }
  3. 给定学生数组(含 name、score),用链式调用实现:过滤及格的学生 → 按分数降序排序 → 取出名字生成新数组,将结果显示在页面上
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏