JavaScript中的Array对象及其方法详解
骚话王又来分享知识了!今天咱们来聊聊JavaScript中的Array对象,这个在前端开发中几乎天天都要打交道的老朋友。Array可以说是JavaScript中最常用的数据结构之一,从简单的数据存储到复杂的数据处理,都离不开它。
Array对象的基本概念
Array(数组)是JavaScript中的一种有序集合,可以存储任意类型的数据。它就像一个可以装各种东西的容器,而且这个容器是有序的,每个元素都有自己的位置编号(索引)。
创建数组的几种方式
// 1. 数组字面量(最常用)
const fruits = ['苹果', '香蕉', '橙子'];
// 2. Array构造函数
const numbers = new Array(1, 2, 3, 4, 5);
// 3. 创建指定长度的空数组
const emptyArray = new Array(5); // [undefined, undefined, undefined, undefined, undefined]
// 4. Array.of() - 创建包含单个元素的数组
const singleElement = Array.of(7); // [7]
// 5. Array.from() - 从类数组对象创建数组
const arrayFromString = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
const arrayFromSet = Array.from(new Set([1, 2, 2, 3])); // [1, 2, 3]
数组的基本特性
const arr = ['a', 'b', 'c'];
// 访问元素(索引从0开始)
console.log(arr[0]); // 'a'
console.log(arr[arr.length - 1]); // 'c' - 最后一个元素
// 修改元素
arr[1] = 'x';
console.log(arr); // ['a', 'x', 'c']
// 数组长度
console.log(arr.length); // 3
// 动态调整长度
arr.length = 5; // 扩展数组,新增的元素为undefined
console.log(arr); // ['a', 'x', 'c', undefined, undefined]
arr.length = 2; // 截断数组
console.log(arr); // ['a', 'x']
数组的静态方法
Array.isArray() - 判断是否为数组
这个方法特别实用,因为JavaScript中typeof对数组返回的是'object',所以我们需要专门的方法来判断:
const arr = [1, 2, 3];
const obj = { a: 1 };
console.log(typeof arr); // 'object' - 这就不太靠谱
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
console.log(Array.isArray(null)); // false
console.log(Array.isArray(undefined)); // false
// 实际应用场景
function processData(data) {
if (Array.isArray(data)) {
return data.map(item => item * 2);
} else {
throw new Error('数据必须是数组!');
}
}
Array.from() - 从类数组对象创建数组
这个方法特别强大,可以把很多类数组对象转换成真正的数组:
// 从字符串创建数组
const chars = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
// 从Set创建数组
const uniqueNumbers = Array.from(new Set([1, 2, 2, 3, 3, 4])); // [1, 2, 3, 4]
// 从Map创建数组
const map = new Map([['a', 1], ['b', 2]]);
const mapArray = Array.from(map); // [['a', 1], ['b', 2]]
// 从DOM节点列表创建数组
const divs = document.querySelectorAll('div');
const divArray = Array.from(divs); // 现在可以使用数组方法了
// 创建数字序列
const range = Array.from({length: 5}, (_, i) => i); // [0, 1, 2, 3, 4]
const range2 = Array.from({length: 5}, (_, i) => i * 2); // [0, 2, 4, 6, 8]
// 实际应用:生成随机数数组
const randomNumbers = Array.from({length: 10}, () => Math.floor(Math.random() * 100));
Array.of() - 创建包含指定元素的数组
这个方法解决了Array构造函数的一个小坑:
// Array构造函数的坑
console.log(new Array(3)); // [undefined, undefined, undefined]
console.log(new Array(3, 4)); // [3, 4]
// Array.of() 更直观
console.log(Array.of(3)); // [3]
console.log(Array.of(3, 4)); // [3, 4]
console.log(Array.of()); // []
// 实际应用
function createArray(...args) {
return Array.of(...args);
}
const result = createArray(1, 2, 3); // [1, 2, 3]
数组的实例方法
添加和删除元素
const arr = ['苹果', '香蕉'];
// push() - 在末尾添加元素
arr.push('橙子'); // 返回新长度:3
console.log(arr); // ['苹果', '香蕉', '橙子']
// pop() - 删除并返回最后一个元素
const lastFruit = arr.pop(); // '橙子'
console.log(arr); // ['苹果', '香蕉']
// unshift() - 在开头添加元素
arr.unshift('葡萄'); // 返回新长度:3
console.log(arr); // ['葡萄', '苹果', '香蕉']
// shift() - 删除并返回第一个元素
const firstFruit = arr.shift(); // '葡萄'
console.log(arr); // ['苹果', '香蕉']
// 实际应用:队列和栈的实现
class Queue {
constructor() {
this.items = [];
}
enqueue(item) {
this.items.push(item);
}
dequeue() {
return this.items.shift();
}
peek() {
return this.items[0];
}
isEmpty() {
return this.items.length === 0;
}
}
class Stack {
constructor() {
this.items = [];
}
push(item) {
this.items.push(item);
}
pop() {
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
}
数组的查找方法
const fruits = ['苹果', '香蕉', '橙子', '葡萄', '苹果'];
// indexOf() - 查找元素第一次出现的索引
console.log(fruits.indexOf('苹果')); // 0
console.log(fruits.indexOf('西瓜')); // -1 (没找到)
// lastIndexOf() - 查找元素最后一次出现的索引
console.log(fruits.lastIndexOf('苹果')); // 4
// includes() - 检查数组是否包含某个元素
console.log(fruits.includes('香蕉')); // true
console.log(fruits.includes('西瓜')); // false
// find() - 查找第一个满足条件的元素
const numbers = [1, 2, 3, 4, 5, 6];
const firstEven = numbers.find(num => num % 2 === 0); // 2
// findIndex() - 查找第一个满足条件的元素的索引
const firstEvenIndex = numbers.findIndex(num => num % 2 === 0); // 1
// findLast() - 查找最后一个满足条件的元素
const lastEven = numbers.findLast(num => num % 2 === 0); // 6
// findLastIndex() - 查找最后一个满足条件的元素的索引
const lastEvenIndex = numbers.findLastIndex(num => num % 2 === 0); // 5
// 实际应用:用户搜索
const users = [
{ id: 1, name: '张三', age: 25 },
{ id: 2, name: '李四', age: 30 },
{ id: 3, name: '王五', age: 28 }
];
function findUserByName(name) {
return users.find(user => user.name === name);
}
function findUsersByAge(age) {
return users.filter(user => user.age === age);
}
console.log(findUserByName('李四')); // { id: 2, name: '李四', age: 30 }
数组的转换方法
const numbers = [1, 2, 3, 4, 5];
// map() - 转换每个元素
const doubled = numbers.map(num => num * 2); // [2, 4, 6, 8, 10]
const strings = numbers.map(num => num.toString()); // ['1', '2', '3', '4', '5']
// filter() - 过滤元素
const evens = numbers.filter(num => num % 2 === 0); // [2, 4]
const odds = numbers.filter(num => num % 2 !== 0); // [1, 3, 5]
// reduce() - 归约数组
const sum = numbers.reduce((acc, num) => acc + num, 0); // 15
const product = numbers.reduce((acc, num) => acc * num, 1); // 120
// reduceRight() - 从右到左归约
const rightSum = numbers.reduceRight((acc, num) => acc + num, 0); // 15
// 实际应用:数据处理
const orders = [
{ id: 1, amount: 100, status: 'completed' },
{ id: 2, amount: 200, status: 'pending' },
{ id: 3, amount: 150, status: 'completed' },
{ id: 4, amount: 300, status: 'cancelled' }
];
// 计算已完成订单的总金额
const completedTotal = orders
.filter(order => order.status === 'completed')
.reduce((sum, order) => sum + order.amount, 0);
console.log(completedTotal); // 250
// 按状态分组
const groupedOrders = orders.reduce((groups, order) => {
if (!groups[order.status]) {
groups[order.status] = [];
}
groups[order.status].push(order);
return groups;
}, {});
console.log(groupedOrders);
// {
// completed: [{ id: 1, amount: 100, status: 'completed' }, { id: 3, amount: 150, status: 'completed' }],
// pending: [{ id: 2, amount: 200, status: 'pending' }],
// cancelled: [{ id: 4, amount: 300, status: 'cancelled' }]
// }
数组的排序和反转
const fruits = ['香蕉', '苹果', '橙子', '葡萄'];
// sort() - 排序(默认按字符串排序)
fruits.sort(); // ['苹果', '葡萄', '橙子', '香蕉']
// 数字排序
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
numbers.sort((a, b) => a - b); // [1, 1, 2, 3, 4, 5, 6, 9]
numbers.sort((a, b) => b - a); // [9, 6, 5, 4, 3, 2, 1, 1]
// 对象排序
const users = [
{ name: '张三', age: 25 },
{ name: '李四', age: 30 },
{ name: '王五', age: 20 }
];
users.sort((a, b) => a.age - b.age); // 按年龄升序
users.sort((a, b) => a.name.localeCompare(b.name)); // 按姓名排序
// reverse() - 反转数组
const reversed = fruits.slice().reverse(); // 创建副本再反转
console.log(reversed); // ['香蕉', '橙子', '葡萄', '苹果']
// 实际应用:多条件排序
const products = [
{ name: '手机', price: 2000, rating: 4.5 },
{ name: '电脑', price: 5000, rating: 4.8 },
{ name: '耳机', price: 200, rating: 4.2 },
{ name: '平板', price: 3000, rating: 4.5 }
];
// 按评分降序,评分相同时按价格升序
products.sort((a, b) => {
if (a.rating !== b.rating) {
return b.rating - a.rating; // 评分降序
}
return a.price - b.price; // 价格升序
});
数组的切片和拼接
const fruits = ['苹果', '香蕉', '橙子', '葡萄', '西瓜'];
// slice() - 切片(不修改原数组)
const slice1 = fruits.slice(1, 3); // ['香蕉', '橙子']
const slice2 = fruits.slice(-2); // ['葡萄', '西瓜']
const slice3 = fruits.slice(1, -1); // ['香蕉', '橙子', '葡萄']
// splice() - 删除和插入元素(修改原数组)
const fruitsCopy = [...fruits];
fruitsCopy.splice(1, 2); // 从索引1开始删除2个元素
console.log(fruitsCopy); // ['苹果', '葡萄', '西瓜']
const fruitsCopy2 = [...fruits];
fruitsCopy2.splice(1, 0, '梨子'); // 在索引1处插入'梨子'
console.log(fruitsCopy2); // ['苹果', '梨子', '香蕉', '橙子', '葡萄', '西瓜']
// concat() - 连接数组
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = arr1.concat(arr2); // [1, 2, 3, 4, 5, 6]
// 实际应用:分页功能
function paginate(array, page, pageSize) {
const start = (page - 1) * pageSize;
const end = start + pageSize;
return array.slice(start, end);
}
const allItems = Array.from({length: 100}, (_, i) => `Item ${i + 1}`);
const page1 = paginate(allItems, 1, 10); // 第1页,每页10条
const page2 = paginate(allItems, 2, 10); // 第2页,每页10条
数组的扁平化
const nested = [1, [2, 3], [4, [5, 6]]];
// flat() - 扁平化数组
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)); // [1, 2, 3, 4, 5, 6]
// flatMap() - 先map再flat
const sentences = ['Hello world', 'Good morning'];
const words = sentences.flatMap(sentence => sentence.split(' ')); // ['Hello', 'world', 'Good', 'morning']
// 实际应用:处理嵌套数据
const users = [
{ name: '张三', hobbies: ['读书', '游泳'] },
{ name: '李四', hobbies: ['跑步', '篮球', '足球'] },
{ name: '王五', hobbies: ['画画'] }
];
// 获取所有爱好
const allHobbies = users.flatMap(user => user.hobbies);
console.log(allHobbies); // ['读书', '游泳', '跑步', '篮球', '足球', '画画']
// 去重
const uniqueHobbies = [...new Set(allHobbies)];
console.log(uniqueHobbies); // ['读书', '游泳', '跑步', '篮球', '足球', '画画']
数组的迭代方法
const fruits = ['苹果', '香蕉', '橙子'];
// forEach() - 遍历数组
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
// every() - 检查所有元素是否满足条件
const allPositive = [1, 2, 3, 4, 5].every(num => num > 0); // true
const allEven = [2, 4, 6, 8, 9].every(num => num % 2 === 0); // false
// some() - 检查是否有元素满足条件
const hasEven = [1, 3, 5, 6, 7].some(num => num % 2 === 0); // true
const hasNegative = [1, 2, 3, 4, 5].some(num => num < 0); // false
// 实际应用:表单验证
const formData = {
username: 'john_doe',
email: 'john@example.com',
age: 25
};
const validators = {
username: value => value.length >= 3,
email: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
age: value => value >= 18 && value <= 100
};
function validateForm(data, rules) {
const errors = {};
Object.keys(rules).forEach(field => {
if (!rules[field](data[field])) {
errors[field] = `${field} 验证失败`;
}
});
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
const result = validateForm(formData, validators);
console.log(result);
数组的高级技巧
解构赋值
const fruits = ['苹果', '香蕉', '橙子', '葡萄', '西瓜'];
// 基本解构
const [first, second, third] = fruits;
console.log(first, second, third); // '苹果' '香蕉' '橙子'
// 跳过元素
const [first2, , third2] = fruits;
console.log(first2, third2); // '苹果' '橙子'
// 剩余元素
const [first3, second3, ...rest] = fruits;
console.log(rest); // ['橙子', '葡萄', '西瓜']
// 默认值
const [a, b, c, d, e, f = '默认值'] = fruits;
console.log(f); // '默认值'
// 交换变量
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1
展开运算符
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合并数组
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// 复制数组
const copy = [...arr1]; // [1, 2, 3]
// 在特定位置插入元素
const inserted = [...arr1.slice(0, 1), 'new', ...arr1.slice(1)]; // [1, 'new', 2, 3]
// 实际应用:不可变更新
const users = [
{ id: 1, name: '张三', age: 25 },
{ id: 2, name: '李四', age: 30 }
];
// 添加新用户
const newUser = { id: 3, name: '王五', age: 28 };
const updatedUsers = [...users, newUser];
// 更新用户
const updatedUsers2 = users.map(user =>
user.id === 1 ? { ...user, age: 26 } : user
);
// 删除用户
const filteredUsers = users.filter(user => user.id !== 1);
数组的空槽处理
// 稀疏数组
const sparse = [1, , 3, , 5];
console.log(sparse.length); // 5
console.log(sparse[1]); // undefined
// 检查空槽
console.log(1 in sparse); // false
console.log(0 in sparse); // true
// 处理空槽的方法
const dense = sparse.filter(() => true); // [1, 3, 5]
const dense2 = Array.from(sparse, x => x || 0); // [1, 0, 3, 0, 5]
// 实际应用:处理表单数据
const formFields = ['name', 'email', 'phone'];
const formValues = ['张三', , '13800138000']; // email字段为空
const formData = formFields.reduce((obj, field, index) => {
if (index in formValues) {
obj[field] = formValues[index];
}
return obj;
}, {});
console.log(formData); // { name: '张三', phone: '13800138000' }
性能优化技巧
避免在循环中修改数组
// 不好的做法
const numbers = [1, 2, 3, 4, 5];
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
numbers.splice(i, 1); // 这会改变数组长度和索引
i--; // 需要手动调整索引
}
}
// 好的做法
const numbers2 = [1, 2, 3, 4, 5];
const filtered = numbers2.filter(num => num % 2 !== 0);
// 或者从后往前遍历
const numbers3 = [1, 2, 3, 4, 5];
for (let i = numbers3.length - 1; i >= 0; i--) {
if (numbers3[i] % 2 === 0) {
numbers3.splice(i, 1);
}
}
使用Set进行去重
// 传统方法
function unique(arr) {
return arr.filter((item, index) => arr.indexOf(item) === index);
}
// 使用Set(更高效)
function unique2(arr) {
return [...new Set(arr)];
}
// 对象数组去重
const users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 1, name: '张三' }
];
function uniqueBy(arr, key) {
const seen = new Set();
return arr.filter(item => {
const value = item[key];
if (seen.has(value)) {
return false;
}
seen.add(value);
return true;
});
}
const uniqueUsers = uniqueBy(users, 'id');
如果觉得有用就收藏点赞,有什么问题欢迎在评论区讨论!骚话王下次再见!