用户工具

站点工具


javascript:第六章数组

第六章 数组

6.1 数组概述

数组是JavaScript中最重要的数据结构之一,用于存储有序的元素集合。JavaScript数组是动态的异构的,可以存储任意类型的元素。

6.2 创建数组

6.2.1 数组字面量

// 空数组
const empty = [];
 
// 带有元素的数组
const numbers = [1, 2, 3, 4, 5];
const mixed = [1, "two", true, null, { a: 1 }, [1, 2]];
 
// 多行格式
const fruits = [
    "apple",
    "banana",
    "orange",
    "grape"
];
 
// 数组长度
console.log(numbers.length);  // 5

6.2.2 Array构造函数

// 使用Array构造函数(不推荐)
const arr1 = new Array();           // []
const arr2 = new Array(5);          // [empty × 5] - 5个空槽
const arr3 = new Array(1, 2, 3);    // [1, 2, 3]
 
// 注意:单参数时会被视为长度
new Array(3);   // [empty, empty, empty]
new Array("3"); // ["3"]
 
// Array.of()(ES6)- 更安全的构造函数
Array.of(3);      // [3]
Array.of(1, 2, 3); // [1, 2, 3]

6.2.3 Array.from()(ES6)

// 从类数组对象创建数组
const fromString = Array.from("hello");  // ["h", "e", "l", "l", "o"]
 
// 从Set创建
const fromSet = Array.from(new Set([1, 2, 3]));  // [1, 2, 3]
 
// 从Map创建
const fromMap = Array.from(new Map([[1, "a"], [2, "b"]]));  // [[1, "a"], [2, "b"]]
 
// 带映射函数
const doubled = Array.from([1, 2, 3], x => x * 2);  // [2, 4, 6]
 
// 创建序列
const range = Array.from({ length: 5 }, (_, i) => i);  // [0, 1, 2, 3, 4]

6.3 数组基础操作

6.3.1 访问和修改元素

const arr = ["a", "b", "c"];
 
// 访问元素
arr[0];     // "a"
arr[1];     // "b"
arr[arr.length - 1];  // "c"(最后一个)
 
// 修改元素
arr[0] = "A";
console.log(arr);  // ["A", "b", "c"]
 
// 添加元素(索引超出当前长度)
arr[5] = "f";
console.log(arr);  // ["A", "b", "c", empty, empty, "f"]
 
// 负数索引(ES2022)
arr.at(-1);  // "f"(最后一个元素)
arr.at(-2);  // undefined(empty槽)

6.3.2 添加和删除元素

const arr = [1, 2, 3];
 
// push - 末尾添加,返回新长度
arr.push(4);           // 4(新长度)
arr.push(5, 6);        // 6(可添加多个)
 
// pop - 末尾删除,返回被删除的元素
arr.pop();             // 6
 
// unshift - 开头添加,返回新长度
arr.unshift(0);        // 5
arr.unshift(-1, -2);   // 7
 
// shift - 开头删除,返回被删除的元素
arr.shift();           // -1
 
// splice - 任意位置添加/删除
const colors = ["red", "green", "blue"];
 
// 删除
colors.splice(1, 1);              // ["green"](从索引1删除1个)
// colors现在是 ["red", "blue"]
 
// 添加
colors.splice(1, 0, "yellow");    // [](从索引1删除0个,添加"yellow")
// colors现在是 ["red", "yellow", "blue"]
 
// 替换
colors.splice(1, 1, "green", "purple");  // ["yellow"]
// colors现在是 ["red", "green", "purple", "blue"]

6.4 数组方法大全

6.4.1 查找方法

const arr = [5, 12, 8, 130, 44];
 
// indexOf - 查找元素索引(严格相等)
arr.indexOf(8);        // 2
arr.indexOf(100);      // -1(不存在)
arr.indexOf(5, 1);     // -1(从索引1开始查找)
 
// lastIndexOf - 从后向前查找
arr.lastIndexOf(44);   // 4
 
// includes - 检查是否包含(ES2016)
arr.includes(8);       // true
arr.includes(100);     // false
 
// find - 返回第一个满足条件的元素(ES6)
arr.find(x => x > 10);       // 12
arr.find(x => x > 200);      // undefined
 
// findIndex - 返回第一个满足条件的索引(ES6)
arr.findIndex(x => x > 10);  // 1
arr.findIndex(x => x > 200); // -1
 
// findLast / findLastIndex(ES2023)
arr.findLast(x => x > 10);       // 44
arr.findLastIndex(x => x > 10);  // 4

6.4.2 遍历方法

const arr = ["a", "b", "c"];
 
// forEach - 遍历每个元素
arr.forEach((item, index, array) => {
    console.log(`${index}: ${item}`);
});
 
// map - 映射,返回新数组
const doubled = [1, 2, 3].map(x => x * 2);  // [2, 4, 6]
 
// filter - 过滤,返回满足条件的元素
const evens = [1, 2, 3, 4, 5].filter(x => x % 2 === 0);  // [2, 4]
 
// reduce - 归约
const sum = [1, 2, 3, 4].reduce((acc, x) => acc + x, 0);  // 10
 
// reduceRight - 从右向左归约
const flattened = [[0, 1], [2, 3], [4, 5]].reduceRight(
    (a, b) => a.concat(b), []
);  // [4, 5, 2, 3, 0, 1]
 
// every - 是否所有元素都满足条件
[1, 2, 3].every(x => x > 0);   // true
 
// some - 是否有元素满足条件
[1, 2, 3].some(x => x > 2);    // true

reduce的高级用法

// 分组
const people = [
    { name: "Alice", age: 21 },
    { name: "Bob", age: 25 },
    { name: "Carol", age: 21 }
];
 
const groupedByAge = people.reduce((acc, person) => {
    const key = person.age;
    if (!acc[key]) acc[key] = [];
    acc[key].push(person);
    return acc;
}, {});
// { 21: [{Alice}, {Carol}], 25: [{Bob}] }
 
// 统计
const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
const count = fruits.reduce((acc, fruit) => {
    acc[fruit] = (acc[fruit] || 0) + 1;
    return acc;
}, {});
// { apple: 3, banana: 2, orange: 1 }
 
// 管道
const pipeline = (...fns) => x => fns.reduce((v, f) => f(v), x);

6.4.3 排序和反转

// sort - 原地排序(默认按字符串Unicode)
const arr = ["banana", "apple", "cherry"];
arr.sort();  // ["apple", "banana", "cherry"]
 
// 数字排序需要比较函数
const nums = [10, 2, 30, 1];
nums.sort();           // [1, 10, 2, 30](错误!)
nums.sort((a, b) => a - b);   // [1, 2, 10, 30](升序)
nums.sort((a, b) => b - a);   // [30, 10, 2, 1](降序)
 
// 对象排序
const users = [
    { name: "Alice", age: 25 },
    { name: "Bob", age: 20 }
];
users.sort((a, b) => a.age - b.age);
 
// 字符串排序(localeCompare)
["café", "cafe", "calzone"].sort((a, b) => a.localeCompare(b));
 
// toSorted(ES2023)- 不修改原数组
const original = [3, 1, 2];
const sorted = original.toSorted((a, b) => a - b);
console.log(original);  // [3, 1, 2]
console.log(sorted);    // [1, 2, 3]
 
// reverse - 原地反转
[1, 2, 3].reverse();    // [3, 2, 1]
 
// toReversed(ES2023)- 不修改原数组
[1, 2, 3].toReversed(); // [3, 2, 1]

6.4.4 截取和连接

const arr = [1, 2, 3, 4, 5];
 
// slice - 截取(不修改原数组)
arr.slice(1, 3);        // [2, 3](从索引1到3,不包括3)
arr.slice(2);           // [3, 4, 5](从索引2到末尾)
arr.slice(-2);          // [4, 5](最后两个)
arr.slice();            // [1, 2, 3, 4, 5](浅拷贝)
 
// concat - 连接数组
[1, 2].concat([3, 4]);          // [1, 2, 3, 4]
[1, 2].concat(3, 4);            // [1, 2, 3, 4]
[1, 2].concat([3], [4, 5]);     // [1, 2, 3, 4, 5]
 
// 使用展开运算符(推荐)
[...arr1, ...arr2, item];

6.4.5 填充和复制

// fill - 填充
const arr1 = new Array(5).fill(0);           // [0, 0, 0, 0, 0]
[1, 2, 3, 4, 5].fill(0, 2, 4);               // [1, 2, 0, 0, 5]
 
// copyWithin - 复制数组的一部分到另一部分
[1, 2, 3, 4, 5].copyWithin(0, 3);            // [4, 5, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(1, 3, 4);         // [1, 4, 3, 4, 5]

6.5 ES6+数组新特性

6.5.1 展开运算符

// 展开数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];    // [1, 2, 3, 4, 5]
 
// 复制数组(浅拷贝)
const copy = [...arr1];
 
// 合并数组
const merged = [...arr1, ...arr2];
 
// 函数调用时展开
const nums = [1, 2, 3];
Math.max(...nums);  // 3(等价于 Math.max(1, 2, 3))

6.5.2 解构赋值

// 数组解构
const [a, b, c] = [1, 2, 3];     // a=1, b=2, c=3
 
// 跳过元素
const [first, , third] = [1, 2, 3];  // first=1, third=3
 
// 剩余元素
const [head, ...tail] = [1, 2, 3, 4];  // head=1, tail=[2, 3, 4]
 
// 默认值
const [x = 0, y = 0] = [1];      // x=1, y=0
 
// 嵌套解构
const [a, [b, c]] = [1, [2, 3]];  // a=1, b=2, c=3
 
// 交换变量
let m = 1, n = 2;
[m, n] = [n, m];  // m=2, n=1

6.5.3 Array新方法(ES2022+)

const arr = [1, 2, 3, 4, 5];
 
// at() - 支持负数索引
arr.at(0);     // 1
arr.at(-1);    // 5(最后一个)
arr.at(-2);    // 4(倒数第二个)
 
// 不可变方法(ES2023)- 返回新数组
arr.toReversed();   // [5, 4, 3, 2, 1]
arr.toSorted((a, b) => b - a);  // [5, 4, 3, 2, 1]
arr.toSpliced(1, 2, "a", "b");   // [1, "a", "b", 4, 5]
arr.with(2, "three");            // [1, 2, "three", 4, 5]
 
// 原数组保持不变
console.log(arr);  // [1, 2, 3, 4, 5]

6.6 数组高级操作

6.6.1 扁平化数组

const nested = [1, [2, 3], [4, [5, 6]]];
 
// flat() - 扁平化(ES2019)
nested.flat();        // [1, 2, 3, 4, [5, 6]](默认深度1)
nested.flat(2);       // [1, 2, 3, 4, 5, 6]
nested.flat(Infinity); // 完全扁平化
 
// flatMap() = map() + flat(1)
const sentences = ["Hello world", "How are you"];
const words = sentences.flatMap(s => s.split(" "));
// ["Hello", "world", "How", "are", "you"]

6.6.2 分组(ES2024)

// group() / groupToMap()(ES2024)
const inventory = [
    { name: "apple", type: "fruit", quantity: 5 },
    { name: "carrot", type: "vegetable", quantity: 3 },
    { name: "banana", type: "fruit", quantity: 2 }
];
 
// 按类型分组
const byType = Object.groupBy(inventory, item => item.type);
// {
//   fruit: [{apple}, {banana}],
//   vegetable: [{carrot}]
// }
 
// 按数量分组
const byQuantity = Object.groupBy(inventory, 
    item => item.quantity > 3 ? "enough" : "restock"
);

6.6.3 数组去重

const arr = [1, 2, 2, 3, 3, 3, 4];
 
// 使用Set(最简单)
[...new Set(arr)];              // [1, 2, 3, 4]
Array.from(new Set(arr));       // [1, 2, 3, 4]
 
// 使用filter
arr.filter((item, index) => arr.indexOf(item) === index);
 
// 对象数组去重
const users = [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" },
    { id: 1, name: "Alice" }
];
 
const unique = users.filter((user, index, self) => 
    index === self.findIndex(u => u.id === user.id)
);

6.7 类型化数组(TypedArray)

类型化数组用于处理二进制数据,在WebGL、文件操作、网络通信等场景中非常有用。

// 创建ArrayBuffer
const buffer = new ArrayBuffer(16);  // 16字节的内存空间
 
// 创建视图
const int32View = new Int32Array(buffer);
int32View[0] = 42;
console.log(int32View[0]);  // 42
 
// 直接创建类型化数组
const int8 = new Int8Array(4);           // 4个8位整数
const uint8 = new Uint8Array(4);         // 4个无符号8位整数
const int16 = new Int16Array(4);         // 4个16位整数
const uint16 = new Uint16Array(4);       // 4个无符号16位整数
const int32 = new Int32Array(4);         // 4个32位整数
const uint32 = new Uint32Array(4);       // 4个无符号32位整数
const float32 = new Float32Array(4);     // 4个32位浮点数
const float64 = new Float64Array(4);     // 4个64位浮点数
const bigInt64 = new BigInt64Array(4);   // 4个64位大整数
 
// 从数组创建
const arr = new Int32Array([1, 2, 3, 4]);
 
// 类型化数组方法(类似普通数组)
arr.forEach(x => console.log(x));
const doubled = arr.map(x => x * 2);
const sum = arr.reduce((a, b) => a + b, 0);
 
// 设置和获取
arr.set([10, 20], 1);  // 从索引1开始设置值
const sub = arr.subarray(1, 3);  // 创建子视图

6.8 练习题

练习1:数组基础

// 1. 实现一个函数chunk(arr, size),将数组分割成指定大小的小数组
// chunk([1,2,3,4,5,6], 2) => [[1,2], [3,4], [5,6]]
 
// 2. 实现一个函数compact(arr),移除数组中所有假值
// compact([0, 1, false, 2, '', 3]) => [1, 2, 3]
 
// 3. 实现一个函数difference(arr1, arr2),返回在arr1中但不在arr2中的元素
// difference([1,2,3], [2,3,4]) => [1]

练习2:数组方法

// 1. 使用reduce实现map、filter、find
 
// 2. 实现一个函数flattenDeep(arr),深度扁平化数组
// flattenDeep([1, [2, [3, [4]]]]) => [1, 2, 3, 4]
 
// 3. 实现一个函数zip(...arrays),将多个数组合并
// zip([1,2], ['a','b'], [true,false]) => [[1,'a',true], [2,'b',false]]

练习3:综合应用

// 1. 给定一个用户数组,按年龄段分组(18-25,26-35,36+)
 
// 2. 实现一个函数,找出数组中出现次数最多的元素
 
// 3. 实现一个函数,对对象数组进行多字段排序
// sortBy(users, ['age', 'name']) // 先按age排序,age相同按name排序

参考答案

练习1答案

// 1. chunk
function chunk(arr, size) {
    const result = [];
    for (let i = 0; i < arr.length; i += size) {
        result.push(arr.slice(i, i + size));
    }
    return result;
}
 
// 或使用reduce
const chunk = (arr, size) => 
    arr.reduce((acc, _, i) => 
        i % size === 0 ? [...acc, arr.slice(i, i + size)] : acc, 
    []);
 
// 2. compact
function compact(arr) {
    return arr.filter(Boolean);
}
 
// 3. difference
function difference(arr1, arr2) {
    const set2 = new Set(arr2);
    return arr1.filter(item => !set2.has(item));
}

练习2答案

// 1. 用reduce实现map/filter/find
const map = (arr, fn) => arr.reduce((acc, item, i) => [...acc, fn(item, i, arr)], []);
const filter = (arr, fn) => arr.reduce((acc, item) => fn(item) ? [...acc, item] : acc, []);
const find = (arr, fn) => arr.reduce((acc, item) => acc === undefined && fn(item) ? item : acc, undefined);
 
// 2. flattenDeep
function flattenDeep(arr) {
    return arr.reduce((acc, item) => 
        acc.concat(Array.isArray(item) ? flattenDeep(item) : item), 
    []);
}
 
// 或使用flat
const flattenDeep = arr => arr.flat(Infinity);
 
// 3. zip
function zip(...arrays) {
    const minLength = Math.min(...arrays.map(arr => arr.length));
    return Array.from({ length: minLength }, (_, i) => 
        arrays.map(arr => arr[i])
    );
}

练习3答案

// 1. 按年龄段分组
function groupByAgeRange(users) {
    return Object.groupBy(users, user => {
        if (user.age <= 25) return '18-25';
        if (user.age <= 35) return '26-35';
        return '36+';
    });
}
 
// 2. 出现次数最多的元素
function mostFrequent(arr) {
    const count = arr.reduce((acc, item) => {
        acc[item] = (acc[item] || 0) + 1;
        return acc;
    }, {});
 
    return Object.entries(count)
        .sort((a, b) => b[1] - a[1])[0][0];
}
 
// 3. 多字段排序
function sortBy(arr, keys) {
    return [...arr].sort((a, b) => {
        for (let key of keys) {
            if (a[key] < b[key]) return -1;
            if (a[key] > b[key]) return 1;
        }
        return 0;
    });
}

本章小结

本章全面学习了JavaScript数组:

  • 数组的创建和基础操作
  • 丰富的数组方法(查找、遍历、排序、截取)
  • ES6+新特性(展开运算符、解构、新方法)
  • 高级操作(扁平化、分组、去重)
  • 类型化数组简介

数组是日常开发中最常用的数据结构,熟练掌握数组操作能大大提高开发效率。

下一章将学习字符串和正则表达式。

javascript/第六章数组.txt · 最后更改: 127.0.0.1