前言
在前端开发中,处理数据是非常常见的需求。JavaScript 提供了多种方法来遍历和操作数组,每种方法都有其独特的用途和优势。本文将深入探讨 for、while、forEach、map、reduce、filter、some、every、find、for...in、for...of 等方法的最佳实践和应用场景,并结合一些常用的算法和高级应用场景。
1. for 循环
它提供了最大的灵活性,可以随时控制循环的开始、结束和步进。 continue 语句用于跳过当前循环的本次迭代,直接进入下一次迭代。 break和 retrun 语句用于完全终止循环。
应用场景
for 循环是最基本的循环方式,是我们平常使用最多的方法,适用于几乎所有的遍历场景。
示例代码
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (let i = 0; i < array.length; i++) {
if (i % 2 === 0) { //打印偶数
continue;//过当前循环的本次迭代,直接进入下一次迭代。
}
if (i >= 8) { //打印到第八个元素
break; //跳出循环 也可以用 return 终止循环, 但是使用 return ‘到达循环结尾’ 不会被打印
}
console.log(array[i]); //输出 2 4 6 8
}
console.log('到达循环结尾');
2. for...in
for...in 用于遍历对象的可枚举属性(包括原型链上的属性)。 它不保证遍历属性的顺序。所以不推荐用于数组的遍历。
应用场景
- 当需要遍历对象的属性来执行某些操作时,例如将对象的属性值进行格式化输出。
- 在处理配置对象或包含多个相关属性的对象时,获取并处理每个属性。
示例代码
const car = {
brand: "BMW",
model: "X5",
year: 2023
};
for (let property in car) {
if (car.hasOwnProperty(property)) {
console.log(`${property}: ${car[property]}`); //输出 brand: BMW model: X5 year: 2023
}
}
3. for...of
for...of 用于遍历数组、字符串、Map、Set 等具有迭代器接口的数据结构。它返回的是值而不是索引或键。
- 它只能用于可迭代对象,不能直接用于普通对象(除非对象实现了
@@iterator方法)。 - 相比于传统的
for循环,for...of语法更加简洁和直观。
应用场景
- 遍历数组并对每个元素进行操作,无需关注数组的索引。
- 读取字符串中的每个字符。
示例代码
//1. 遍历数组
const fruits = ['apple', 'banana', 'cherry'];
for (let fruit of fruits) {
console.log(fruit); //输出 apple banana cherry
}
//2. 遍历字符串
const str = 'Hello';
for (let char of str) {
console.log(char); //输出 H e l l o
}
//3. 遍历 Set
const mySet = new Set([1, 2, 3, 3, 4, 4, 5]);
for (let num of mySet) {
console.log(num); //输出 1 2 3 4 5
}
4. while 循环
while 循环用于需要基于某个条件持续执行的场景,特别是在循环次数不确定时。
- 只要条件为真,循环就会一直执行。
- 循环体中的代码至少会执行一次,如果初始条件就为假,那么循环体不会被执行。
应用场景
- 当不知道确切的循环次数,但知道循环结束的条件时,适合使用
while循环。 - 例如,等待某个异步操作完成后再停止循环。
示例代码
let i = 0;
const array = [1, 2, 3, 4, 5];
while (i < 3) {
console.log(array[i]); //输出 1 2 3
i++;
}
do...while 循环至少执行一次,然后检查条件是否为真。
let j = 0;
const array = [1, 2, 3, 4, 5];
do {
console.log(array[j]); //输出 1
j++;
} while (j <= 0);
5. forEach
forEach 用于对数组的每个元素执行一次给定的函数,更加简洁和语义化。
- 简洁直观,无需手动管理索引和循环条件。
- 不能使用
break或continue或者return来中断循环。
应用场景
- 当需要简单地对数组中的每个元素执行一个操作,而不需要关心索引或中途停止时,
forEach非常方便。 - 例如,对数组中的每个元素进行数据转换或输出。
示例代码
const users = [
{ name: 'name1', age: 25 },
{ name: 'name2', age: 30 },
{ name: 'name3', age: 35 }
];
users.forEach((user,index) => {
console.log(`${index}-${user.name} is ${user.age} years old.`);
//输出 0-name1 is 25 years old. 1-name2 is 30 years old. 2-name3 is 35 years old.
});
6. map
map 用于创建一个新数组,其结果是对原数组每个元素执行一个提供的函数后的返回值。
map方法会返回一个新的数组,原数组不会被修改。- 它会按照原始数组的顺序依次处理每个元素。
应用场景
- 对数组中的元素进行统一的转换或计算,生成一个新的数组。
- 例如,将数值数组转换为字符串数组,或者对价格数组进行打折计算。
示例代码
//示例1 处理价格
const prices = [100, 200, 300, 400, 500];
const discountedPrices = prices.map(price => price * 0.8);
console.log(discountedPrices) //[80, 160, 240, 320, 400]
// 示例2 处理数组对象
const users = [
{name: 'name1', age: 25},
{name: 'name2', age: 30},
{name: 'name3', age: 35}
];
const names = users.map(user => user.name);
console.log(names); //输出 ['name1', 'name2', 'name3']
7. reduce
reduce 用于将数组中的所有元素通过一个累积函数合并为单一值,方法接受一个回调函数和一个初始值作为参数,并对数组中的每个元素依次执行回调函数,最终返回一个累积的结果。常用于求和、求积等操作。
reduce方法提供了强大的灵活性,可以根据具体的需求自定义累积的逻辑。- 可以在回调函数中添加更多的逻辑判断和处理。
应用场景
- 求和:如上述示例,计算数组中所有数字的总和。
- 求积:计算数组元素的乘积。
- 也可以用来数据去重。
示例代码
//示例一
const numbers = [1, 2, 3, 4, 5];
// 计算数组元素的总和
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 输出: 15
// 示例二
const users = [
{name: 'Alice', age: 25},
{name: 'Bob', age: 30},
{name: 'Charlie', age: 35}
];
const totalAge = users.reduce((accumulator, user) => accumulator + user.age, 0);
console.log(totalAge); //输出 90
// 示例三 去重
const array = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = array.reduce((accumulator, currentValue) => {
if (!accumulator.includes(currentValue)) {
accumulator.push(currentValue);
}
return accumulator;
}, []);
console.log(uniqueArray); //输出 [1, 2, 3, 4, 5]
代码解析
reduce 方法通过一个累积函数迭代数组元素,并将其合并为单一值。accumulator 是累加器,currentValue 是当前元素。示例中,0 是初始值。
8. filter
filter 方法创建一个新数组,新数组中的元素是通过提供的函数实现的测试的原数组元素的子集。
filter方法不会修改原始数组,而是返回一个新的数组。- 用于根据指定的条件筛选过滤出数组中的元素。
- 与
map方法不同,filter方法侧重于筛选元素,而map侧重于对元素进行转换。 - 与
reduce方法相比,filter主要用于产生一个符合条件的新数组,而reduce用于将数组元素累积为一个单一的值
应用场景
1.筛选出符合特定条件的元素,例如筛选出大于某个值的数字、包含特定字符串的对象等。 2.也可以用来数据去重
示例代码
// 示例一 过滤能整除2 的数据
const array = [1, 2, 3, 4, 5];
const evenArray = array.filter(element => element % 2 === 0);
console.log(evenArray); // [2, 4]
// 示例二 过滤年龄大于=30 的数据
const users = [
{name: 'name1', age: 25},
{name: 'name2', age: 30},
{name: 'name3', age: 35}
];
const adults = users.filter(user => user.age >= 30);
console.log(adults);
// 输出 [{name: 'name2', age: 30}, {name: 'name3', age: 35}]
//示例三 对象数组去重
const objects = [
{ id: 1, name: 'name1' },
{ id: 2, name: 'name2' },
{ id: 1, name: 'name3' },
{ id: 3, name: 'name4' },
{ id: 2, name: 'name5' }
];
const uniqueObjects = objects.filter((obj, index, self) =>
index === self.findIndex(item => item.id === obj.id)
);
console.log(uniqueObjects); // 输出 [{"id":1,"name":"name1"},{"id":2,"name":"name2"},{"id":3,"name":"name4"}]
9. some
some 用于测试数组中的至少一个元素是否通过了提供的函数的测试。只要找到一个满足条件的元素,方法就会返回 true,否则返回 false。
- 只要有一个元素满足条件,
some方法就返回true,否则返回false。 - 它会在找到第一个满足条件的元素后立即停止遍历数组。
- 与
every方法相反,every要求所有元素都满足条件才返回true。而some只要有一个满足即可。
应用场景
- 快速检查数组中是否存在满足特定条件的元素。
示例代码
const users = [
{name: 'name1', age: 25},
{name: 'name2', age: 30},
{name: 'name3', age: 35}
];
const hasMinor = users.some(user => user.age < 18);
console.log(hasMinor); //输出 false
10. every
every 用于测试数组中的所有元素是否都通过了提供的函数的测试。如果所有元素都满足条件,则返回 true,否则返回 false。
应用场景
- 验证数组中的所有元素是否都符合某种严格的规则
示例代码
// 示例一 检查所有元素都满足 被2 整除
const array = [2, 4, 6, 8];
const allEven = array.every(element => element % 2 === 0);
console.log(allEven); // true
// 示例2
const users = [
{name: 'name1', age: 25},
{name: 'name2', age: 30},
{name: 'name3', age: 35}
];
const allAdults = users.every(user => user.age >= 18);
console.log(allAdults); // 输出 true
11. find
find 方法返回数组中满足提供的测试函数的第一个元素的值。如果没有找到匹配的元素,则返回 undefined。
- 只返回第一个满足条件的元素,而不是所有满足条件的元素。
- 如果没有找到满足条件的元素,则返回
undefined。 - 与
filter方法不同,find方法只返回第一个匹配的元素,而filter方法返回所有匹配的元素组成的新数组。
应用场景
- 在数组中查找具有特定属性值的对象。
示例代码
// 示例一
const array = [1, 2, 3, 4, 5];
const firstEven = array.find(element => element % 2 === 0);
console.log(firstEven); // 2
// 示例二
const users = [
{ id: 1, name: 'name1'},
{ id: 2, name: 'name2'},
{ id: 3, name: 'name3'}
];
const foundUser = users.find(user => user.id === 2);
console.log(foundUser) // 输出 {id: 2, name: 'name2'}
12. findIndex
findIndex 方法返回数组中满足提供的测试函数的第一个元素的索引。如果没有找到匹配的元素,则返回 -1 。
应用场景
- 确定满足特定条件的元素在数组中的位置,以便进行后续的操作。
示例代码
const users = [
{ id: 1, name: 'name1' },
{ id: 2, name: 'name2' },
{ id: 3, name: 'name3' }
];
const foundUserIndex = users.findIndex(user => user.id === 2);
if (foundUserIndex!== -1) {
console.log(users[foundUserIndex]) //输出 {id: 2, name: 'name2'}
}
一些常见高级应用场景
1.使用 filter 和 includes 计算两个数组的交集
const array1 = [1, 2, 3, 4, 5];
const array2 = [3, 4, 5, 6, 7];
const intersection = array1.filter(element => array2.includes(element));
console.log(intersection); //输出 [3, 4, 5]
2. 利用双端比较算法用于判断一个数组是否为回文(即从前往后和从后往前读都一样)
const isPalindrome = str => {
let left = 0;
let right = str.length - 1;
while (left < right) {
if (str[left] !== str[right]) {
return false;
}
left++;
right--;
}
return true;
};
console.log(isPalindrome('racecar')); //输出 true
console.log(isPalindrome('hello')); //输出 false
3.冒泡排序算法
const bubbleSort = array => {
let n = array.length;
let swapped;
do {
swapped = false;
for (let i = 0; i < n - 1; i++) {
if (array[i] > array[i + 1]) {
[array[i], array[i + 1]] = [array[i + 1], array[i]];
swapped = true;
}
}
n--;
} while (swapped);
return array;
};
const array = [5, 3, 8, 4, 2];
console.log(bubbleSort(array)); //输出 [2, 3, 4, 5, 8]
4.快速排序算法
const quickSort = array => {
if (array.length <= 1) return array;
const pivot = array[Math.floor(array.length / 2)];
const left = array.filter(x => x < pivot);
const right = array.filter(x => x > pivot);
return [...quickSort(left), pivot, ...quickSort(right)];
};
const array = [5, 3, 8, 4, 2];
console.log(quickSort(array)); //输出 [2, 3, 4, 5, 8]
5.洗牌算法随机排序
const shuffle = array => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
};
const array = [1, 2, 3, 4, 5];
console.log(shuffle(array)); // 随机顺序
6.优化查询-二分查找
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const target = 7;
// 二分查找示例
const binarySearch = (arr, target) => {
let left = 0, right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) return true;
else if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return false;
};
const exists = binarySearch(array, target);
console.log(exists); // true
总结
在我们前端日常开发中,合理选择数组遍历和处理方法可以大大提高代码的可读性和效率。for 和 while 循环提供了最大的灵活性,forEach 更加简洁和语义化,map、reduce、filter、some、every、find 等则提供了高层次的数据处理能力。for...in 和 for...of 则适用于对象属性和迭代器对象的遍历。了解每种方法的特点和应用场景,有助于编写更加高效和优雅的代码。
希望通过本篇文章能再次巩固你的基础知识,提升开发效率,如果觉得本篇文章还不错,恳请来个收藏点赞。