数组是JavaScript中最重要的数据结构之一,用于存储有序的元素集合。JavaScript数组是动态的、异构的,可以存储任意类型的元素。
// 空数组 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
// 使用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]
// 从类数组对象创建数组 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]
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槽)
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"]
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
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);
// 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]
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];
// 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]
// 展开数组 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))
// 数组解构 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
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]
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"]
// 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" );
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) );
类型化数组用于处理二进制数据,在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); // 创建子视图
// 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]
// 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]]
// 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数组:
数组是日常开发中最常用的数据结构,熟练掌握数组操作能大大提高开发效率。
下一章将学习字符串和正则表达式。