Appearance
数组
数组是一种有序的数据集合,可以存储多个值。JavaScript 数组是动态的,可以存储任意类型的数据。本章将详细介绍数组的创建、操作和常用方法。
创建数组
数组字面量
javascript
// 使用数组字面量创建数组(推荐)
let arr1 = [1, 2, 3, 4, 5];
let arr2 = ['苹果', '香蕉', '橙子'];
let arr3 = [1, 'hello', true, null, { name: '张三' }]; // 可以混合类型
// 空数组
let empty = [];
// 数组可以嵌套
let nested = [[1, 2], [3, 4], [5, 6]];
// 尾随逗号(允许)
let arr4 = [1, 2, 3,]; // 最后一个逗号会被忽略Array 构造函数
javascript
// 使用 Array 构造函数
let arr1 = new Array(1, 2, 3, 4, 5);
console.log(arr1); // [1, 2, 3, 4, 5]
// 单个数字参数:创建指定长度的空数组
let arr2 = new Array(5);
console.log(arr2.length); // 5
console.log(arr2[0]); // undefined
// 单个非数字参数:创建包含该元素的数组
let arr3 = new Array('5');
console.log(arr3); // ['5']
// Array.of() - ES6
// 解决单个数字参数的问题
let arr4 = Array.of(5);
console.log(arr4); // [5]
let arr5 = Array.of(1, 2, 3);
console.log(arr5); // [1, 2, 3]
// Array.from() - ES6
// 从类数组或可迭代对象创建数组
let arr6 = Array.from('hello');
console.log(arr6); // ['h', 'e', 'l', 'l', 'o']
// 从 arguments 创建数组
function test() {
let args = Array.from(arguments);
console.log(args);
}
test(1, 2, 3); // [1, 2, 3]
// 使用 map 函数
let arr7 = Array.from([1, 2, 3], x => x * 2);
console.log(arr7); // [2, 4, 6]访问和修改数组
访问元素
javascript
let fruits = ['苹果', '香蕉', '橙子', '葡萄'];
// 通过索引访问(索引从 0 开始)
console.log(fruits[0]); // 苹果
console.log(fruits[2]); // 橙子
// 访问不存在的索引返回 undefined
console.log(fruits[10]); // undefined
// 获取数组长度
console.log(fruits.length); // 4
// 访问最后一个元素
console.log(fruits[fruits.length - 1]); // 葡萄
// at() 方法 - ES2022
console.log(fruits.at(0)); // 苹果
console.log(fruits.at(-1)); // 葡萄(负索引从末尾开始)
console.log(fruits.at(-2)); // 橙子修改元素
javascript
let arr = [1, 2, 3, 4, 5];
// 修改元素
arr[0] = 10;
console.log(arr); // [10, 2, 3, 4, 5]
// 添加元素(通过索引)
arr[5] = 6;
console.log(arr); // [10, 2, 3, 4, 5, 6]
// 跳过索引会创建稀疏数组
arr[10] = 11;
console.log(arr); // [10, 2, 3, 4, 5, 6, empty × 4, 11]
console.log(arr.length); // 11
// 修改数组长度
arr.length = 3;
console.log(arr); // [10, 2, 3]
// 清空数组
arr.length = 0;
console.log(arr); // []数组方法
添加和删除元素
javascript
let arr = [1, 2, 3];
// push():末尾添加元素,返回新长度
arr.push(4);
console.log(arr); // [1, 2, 3, 4]
let len = arr.push(5, 6);
console.log(arr); // [1, 2, 3, 4, 5, 6]
console.log(len); // 6
// pop():删除末尾元素,返回被删除的元素
let last = arr.pop();
console.log(arr); // [1, 2, 3, 4, 5]
console.log(last); // 6
// unshift():开头添加元素,返回新长度
arr.unshift(0);
console.log(arr); // [0, 1, 2, 3, 4, 5]
let len2 = arr.unshift(-2, -1);
console.log(arr); // [-2, -1, 0, 1, 2, 3, 4, 5]
// shift():删除开头元素,返回被删除的元素
let first = arr.shift();
console.log(arr); // [-1, 0, 1, 2, 3, 4, 5]
console.log(first); // -2
// splice():添加/删除/替换元素
// splice(起始索引, 删除数量, 添加的元素...)
let arr2 = [1, 2, 3, 4, 5];
// 删除元素
let removed = arr2.splice(2, 2); // 从索引 2 开始删除 2 个
console.log(arr2); // [1, 2, 5]
console.log(removed); // [3, 4]
// 添加元素
arr2.splice(2, 0, 3, 4); // 在索引 2 处插入 3, 4
console.log(arr2); // [1, 2, 3, 4, 5]
// 替换元素
arr2.splice(1, 2, 'a', 'b'); // 删除 2 个,插入 'a', 'b'
console.log(arr2); // [1, 'a', 'b', 4, 5]
// 删除到末尾
arr2.splice(2);
console.log(arr2); // [1, 'a']查找元素
javascript
let arr = [1, 2, 3, 2, 4, 2, 5];
// indexOf():查找元素的第一个索引
console.log(arr.indexOf(2)); // 1
console.log(arr.indexOf(6)); // -1(不存在)
// 从指定位置开始查找
console.log(arr.indexOf(2, 2)); // 3
// lastIndexOf():从后往前查找
console.log(arr.lastIndexOf(2)); // 5
// includes():检查是否包含元素 - ES7
console.log(arr.includes(3)); // true
console.log(arr.includes(6)); // false
// find():查找满足条件的第一个元素 - ES6
let result = arr.find(x => x > 3);
console.log(result); // 4
let users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
];
let user = users.find(u => u.id === 2);
console.log(user); // { id: 2, name: '李四' }
// findIndex():查找满足条件的第一个索引 - ES6
let index = arr.findIndex(x => x > 3);
console.log(index); // 4
let userIndex = users.findIndex(u => u.name === '王五');
console.log(userIndex); // 2
// findLast() 和 findLastIndex() - ES2023
let lastEven = arr.findLast(x => x % 2 === 0);
console.log(lastEven); // 4
let lastEvenIndex = arr.findLastIndex(x => x % 2 === 0);
console.log(lastEvenIndex); // 5数组转换
javascript
let arr = [1, 2, 3, 4, 5];
// join():将数组转换为字符串
console.log(arr.join()); // '1,2,3,4,5'
console.log(arr.join('')); // '12345'
console.log(arr.join('-')); // '1-2-3-4-5'
console.log(arr.join(' | ')); // '1 | 2 | 3 | 4 | 5'
// toString():转换为字符串
console.log(arr.toString()); // '1,2,3,4,5'
// toLocaleString():转换为本地字符串
let dates = [new Date(), new Date()];
console.log(dates.toLocaleString());
// split():字符串转数组(字符串方法)
let str = 'a,b,c,d';
let arr2 = str.split(',');
console.log(arr2); // ['a', 'b', 'c', 'd']
// concat():合并数组
let arr3 = [1, 2];
let arr4 = [3, 4];
let merged = arr3.concat(arr4);
console.log(merged); // [1, 2, 3, 4]
// 合并多个数组
let merged2 = arr3.concat(arr4, [5, 6], 7);
console.log(merged2); // [1, 2, 3, 4, 5, 6, 7]
// 展开运算符合并 - ES6
let merged3 = [...arr3, ...arr4];
console.log(merged3); // [1, 2, 3, 4]
// flat():扁平化数组 - ES2019
let nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6]
console.log(nested.flat(Infinity)); // 完全扁平化
// flatMap():映射后扁平化 - ES2019
let sentences = ['Hello World', 'Good Morning'];
let words = sentences.flatMap(s => s.split(' '));
console.log(words); // ['Hello', 'World', 'Good', 'Morning']排序和反转
javascript
let arr = [3, 1, 4, 1, 5, 9, 2, 6];
// sort():排序(会修改原数组)
arr.sort();
console.log(arr); // [1, 1, 2, 3, 4, 5, 6, 9](按字符串排序)
// 数字排序需要比较函数
let nums = [3, 1, 4, 1, 5, 9, 2, 6];
nums.sort((a, b) => a - b); // 升序
console.log(nums); // [1, 1, 2, 3, 4, 5, 6, 9]
nums.sort((a, b) => b - a); // 降序
console.log(nums); // [9, 6, 5, 4, 3, 2, 1, 1]
// 对象数组排序
let users = [
{ name: '张三', age: 25 },
{ name: '李四', age: 20 },
{ name: '王五', age: 30 }
];
users.sort((a, b) => a.age - b.age);
console.log(users);
// [{ name: '李四', age: 20 }, { name: '张三', age: 25 }, { name: '王五', age: 30 }]
// toSorted():排序但不修改原数组 - ES2023
let arr2 = [3, 1, 4, 1, 5];
let sorted = arr2.toSorted((a, b) => a - b);
console.log(arr2); // [3, 1, 4, 1, 5](原数组不变)
console.log(sorted); // [1, 1, 3, 4, 5]
// reverse():反转数组(会修改原数组)
let arr3 = [1, 2, 3, 4, 5];
arr3.reverse();
console.log(arr3); // [5, 4, 3, 2, 1]
// toReversed():反转但不修改原数组 - ES2023
let arr4 = [1, 2, 3, 4, 5];
let reversed = arr4.toReversed();
console.log(arr4); // [1, 2, 3, 4, 5]
console.log(reversed); // [5, 4, 3, 2, 1]截取和填充
javascript
let arr = [1, 2, 3, 4, 5];
// slice():截取数组(不修改原数组)
console.log(arr.slice(1, 4)); // [2, 3, 4]
console.log(arr.slice(2)); // [3, 4, 5]
console.log(arr.slice(-3)); // [3, 4, 5]
console.log(arr.slice(1, -1)); // [2, 3, 4]
console.log(arr.slice()); // [1, 2, 3, 4, 5](复制数组)
// fill():填充数组 - ES6
let arr2 = [1, 2, 3, 4, 5];
arr2.fill(0);
console.log(arr2); // [0, 0, 0, 0, 0]
let arr3 = [1, 2, 3, 4, 5];
arr3.fill(0, 1, 4); // 从索引 1 到 4(不包括 4)填充 0
console.log(arr3); // [1, 0, 0, 0, 5]
// 创建指定长度的数组并填充
let arr4 = new Array(5).fill(1);
console.log(arr4); // [1, 1, 1, 1, 1]
// copyWithin():在数组内部复制 - ES6
let arr5 = [1, 2, 3, 4, 5];
arr5.copyWithin(0, 3, 5); // 将索引 3-5 的元素复制到索引 0 开始的位置
console.log(arr5); // [4, 5, 3, 4, 5]
// with():修改指定索引的元素(不修改原数组)- ES2023
let arr6 = [1, 2, 3, 4, 5];
let newArr = arr6.with(2, 'three');
console.log(arr6); // [1, 2, 3, 4, 5]
console.log(newArr); // [1, 2, 'three', 4, 5]迭代方法
forEach
javascript
// forEach():遍历数组
let arr = [1, 2, 3, 4, 5];
arr.forEach(function(item, index, array) {
console.log(`索引 ${index}: ${item}`);
});
// 使用箭头函数
arr.forEach((item, index) => {
console.log(item * 2);
});
// 无法使用 break 中断,需要抛出异常
// 使用 some 或 every 替代map
javascript
// map():映射数组,返回新数组
let arr = [1, 2, 3, 4, 5];
let doubled = arr.map(item => item * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// 映射对象数组
let users = [
{ name: '张三', age: 25 },
{ name: '李四', age: 20 }
];
let names = users.map(user => user.name);
console.log(names); // ['张三', '李四']
// 返回新对象
let userInfos = users.map(user => ({
...user,
isAdult: user.age >= 18
}));
console.log(userInfos);
// [{ name: '张三', age: 25, isAdult: true }, { name: '李四', age: 20, isAdult: true }]filter
javascript
// filter():过滤数组,返回满足条件的元素
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 过滤偶数
let evens = arr.filter(item => item % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
// 过滤大于 5 的数
let greater = arr.filter(item => item > 5);
console.log(greater); // [6, 7, 8, 9, 10]
// 过滤对象数组
let products = [
{ name: '苹果', price: 5 },
{ name: '香蕉', price: 3 },
{ name: '橙子', price: 8 }
];
let expensive = products.filter(p => p.price > 4);
console.log(expensive);
// [{ name: '苹果', price: 5 }, { name: '橙子', price: 8 }]reduce
javascript
// reduce():归约数组,累积计算
let arr = [1, 2, 3, 4, 5];
// 求和
let sum = arr.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 15
// 求乘积
let product = arr.reduce((acc, cur) => acc * cur, 1);
console.log(product); // 120
// 找最大值
let max = arr.reduce((acc, cur) => Math.max(acc, cur));
console.log(max); // 5
// 统计元素出现次数
let fruits = ['苹果', '香蕉', '苹果', '橙子', '香蕉', '苹果'];
let count = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(count); // { 苹果: 3, 香蕉: 2, 橙子: 1 }
// 数组去重
let arr2 = [1, 2, 2, 3, 3, 3, 4];
let unique = arr2.reduce((acc, cur) => {
if (!acc.includes(cur)) {
acc.push(cur);
}
return acc;
}, []);
console.log(unique); // [1, 2, 3, 4]
// 数组转对象
let users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
];
let userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
console.log(userMap);
// { 1: { id: 1, name: '张三' }, 2: { id: 2, name: '李四' }, 3: { id: 3, name: '王五' } }
// reduceRight():从右到左归约
let arr3 = ['a', 'b', 'c'];
let result = arr3.reduceRight((acc, cur) => acc + cur, '');
console.log(result); // 'cba'some 和 every
javascript
// some():检查是否至少有一个元素满足条件
let arr = [1, 2, 3, 4, 5];
let hasEven = arr.some(item => item % 2 === 0);
console.log(hasEven); // true
let hasNegative = arr.some(item => item < 0);
console.log(hasNegative); // false
// every():检查是否所有元素都满足条件
let allPositive = arr.every(item => item > 0);
console.log(allPositive); // true
let allEven = arr.every(item => item % 2 === 0);
console.log(allEven); // false
// 空数组的情况
console.log([].some(x => x)); // false
console.log([].every(x => x)); // true遍历数组
for 循环
javascript
let arr = ['苹果', '香蕉', '橙子'];
// 普通 for 循环
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// 倒序遍历
for (let i = arr.length - 1; i >= 0; i--) {
console.log(arr[i]);
}
// 性能优化:缓存长度
for (let i = 0, len = arr.length; i < len; i++) {
console.log(arr[i]);
}for...of
javascript
let arr = ['苹果', '香蕉', '橙子'];
// for...of 遍历值
for (let fruit of arr) {
console.log(fruit);
}
// 获取索引
for (let [index, fruit] of arr.entries()) {
console.log(`${index}: ${fruit}`);
}
// 遍历字符串
for (let char of 'hello') {
console.log(char);
}for...in
javascript
let arr = ['苹果', '香蕉', '橙子'];
// for...in 遍历索引(不推荐用于数组)
for (let index in arr) {
console.log(index, arr[index]);
}
// 注意:for...in 会遍历可枚举属性
arr.custom = '自定义属性';
for (let key in arr) {
console.log(key); // 0, 1, 2, custom
}迭代器方法
javascript
let arr = ['苹果', '香蕉', '橙子'];
// keys():遍历索引
for (let key of arr.keys()) {
console.log(key); // 0, 1, 2
}
// values():遍历值
for (let value of arr.values()) {
console.log(value); // 苹果, 香蕉, 橙子
}
// entries():遍历键值对
for (let [key, value] of arr.entries()) {
console.log(`${key}: ${value}`);
}数组去重
javascript
// 方法 1:Set(推荐)
let arr = [1, 2, 2, 3, 3, 3, 4];
let unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3, 4]
// 方法 2:filter
let unique2 = arr.filter((item, index) => arr.indexOf(item) === index);
console.log(unique2); // [1, 2, 3, 4]
// 方法 3:reduce
let unique3 = arr.reduce((acc, cur) => {
if (!acc.includes(cur)) {
acc.push(cur);
}
return acc;
}, []);
console.log(unique3); // [1, 2, 3, 4]
// 对象数组去重
let users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 1, name: '张三' }
];
// 根据 id 去重
let uniqueUsers = users.filter((user, index, self) =>
index === self.findIndex(u => u.id === user.id)
);
console.log(uniqueUsers);
// [{ id: 1, name: '张三' }, { id: 2, name: '李四' }]
// 使用 Map
let uniqueUsers2 = [...new Map(users.map(user => [user.id, user])).values()];
console.log(uniqueUsers2);数组排序算法
javascript
// 冒泡排序
function bubbleSort(arr) {
let len = arr.length;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // 交换
}
}
}
return arr;
}
console.log(bubbleSort([5, 3, 8, 4, 2])); // [2, 3, 4, 5, 8]
// 快速排序
function quickSort(arr) {
if (arr.length <= 1) return arr;
let pivot = arr[Math.floor(arr.length / 2)];
let left = arr.filter(x => x < pivot);
let middle = arr.filter(x => x === pivot);
let right = arr.filter(x => x > pivot);
return [...quickSort(left), ...middle, ...quickSort(right)];
}
console.log(quickSort([5, 3, 8, 4, 2])); // [2, 3, 4, 5, 8]
// 选择排序
function selectionSort(arr) {
let len = arr.length;
for (let i = 0; i < len - 1; i++) {
let minIndex = i;
for (let j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if (minIndex !== i) {
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
}
return arr;
}
console.log(selectionSort([5, 3, 8, 4, 2])); // [2, 3, 4, 5, 8]类数组对象
javascript
// 类数组对象:有 length 属性和索引,但没有数组方法
let arrayLike = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
// console.log(arrayLike.join('-')); // 错误:不是真正的数组
// 转换为数组
// 方法 1:Array.from()
let arr1 = Array.from(arrayLike);
console.log(arr1.join('-')); // 'a-b-c'
// 方法 2:展开运算符(需要对象有 Symbol.iterator)
// let arr2 = [...arrayLike]; // 错误:不是可迭代对象
// 方法 3:Array.prototype.slice.call()
let arr3 = Array.prototype.slice.call(arrayLike);
console.log(arr3); // ['a', 'b', 'c']
// arguments 是类数组
function test() {
console.log(arguments); // Arguments(3) [1, 2, 3]
// 转换为数组
let args = Array.from(arguments);
console.log(args); // [1, 2, 3]
// 或使用展开运算符
let args2 = [...arguments];
console.log(args2); // [1, 2, 3]
}
test(1, 2, 3);
// NodeList 是类数组
// let divs = document.querySelectorAll('div');
// let divArray = Array.from(divs);小结
本章学习了 JavaScript 数组的核心知识:
- 创建数组:数组字面量、Array 构造函数、Array.of()、Array.from()
- 访问和修改:索引访问、length 属性
- 添加删除:push、pop、shift、unshift、splice
- 查找元素:indexOf、lastIndexOf、includes、find、findIndex
- 数组转换:join、concat、flat、flatMap
- 排序反转:sort、reverse、toSorted、toReversed
- 迭代方法:forEach、map、filter、reduce、some、every
- 遍历数组:for、for...of、for...in、迭代器方法
- 数组去重:Set、filter、reduce
下一章我们将学习 对象,了解对象的创建和操作方法。
