MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript中数组的高级用法:map, filter, reduce等

2022-02-074.3k 阅读

JavaScript中数组的map方法

在JavaScript的数组操作中,map方法是一个非常实用且强大的工具。map方法会创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

map方法的语法

map方法的语法如下:

let newArray = arr.map(callback(currentValue[, index[, array]]) {
    // 返回新的值
}[, thisArg]);
  • callback:一个为数组中的每个元素执行的函数,该函数接收三个参数:
    • currentValue:数组中正在处理的当前元素。
    • index(可选):数组中正在处理的当前元素的索引。
    • array(可选):调用map方法的数组。
  • thisArg(可选):执行callback函数时使用的this值。

map方法的基本使用

假设我们有一个包含数字的数组,现在想要将数组中的每个数字都翻倍,就可以使用map方法。示例代码如下:

let numbers = [1, 2, 3, 4];
let doubledNumbers = numbers.map((number) => number * 2);
console.log(doubledNumbers); 

在上述代码中,我们定义了一个numbers数组,然后使用map方法对数组中的每个元素进行翻倍操作。map方法会遍历numbers数组,依次将每个元素传递给箭头函数(number) => number * 2,箭头函数将接收到的元素翻倍并返回,map方法将这些返回值收集到一个新数组doubledNumbers中。

map方法的复杂应用

map方法不仅仅局限于简单的数学运算。假设我们有一个包含用户对象的数组,每个用户对象都有nameage属性,现在我们想要创建一个新数组,新数组中每个元素是一个字符串,格式为“姓名:年龄”。示例代码如下:

let users = [
    {name: 'Alice', age: 25},
    {name: 'Bob', age: 30},
    {name: 'Charlie', age: 35}
];
let userInfoStrings = users.map((user) => `${user.name}:${user.age}`);
console.log(userInfoStrings); 

在这个例子中,map方法遍历users数组,将每个用户对象传递给箭头函数(user) => ${user.name}:${user.age}``,箭头函数根据用户对象的nameage属性生成特定格式的字符串并返回,map方法将这些字符串收集到userInfoStrings数组中。

map方法与thisArg的使用

map方法的回调函数中需要使用this时,thisArg参数就发挥作用了。例如:

let person = {
    multiplier: 2,
    multiplyNumbers: function (numbers) {
        return numbers.map(function (number) {
            return number * this.multiplier;
        }, this);
    }
};
let numbers = [1, 2, 3];
let result = person.multiplyNumbers(numbers);
console.log(result); 

在上述代码中,multiplyNumbers方法中的map回调函数需要访问person对象的multiplier属性。通过将this作为map方法的第二个参数(即thisArg)传入,确保了回调函数中的this指向person对象,从而实现正确的乘法操作。

JavaScript中数组的filter方法

filter方法是JavaScript数组操作中用于筛选元素的重要方法。它会创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

filter方法的语法

filter方法的语法如下:

let newArray = arr.filter(callback(currentValue[, index[, array]]) {
    // 返回true或false
}[, thisArg]);
  • callback:一个为数组中的每个元素执行的函数,该函数接收三个参数,与map方法中的callback参数类似:
    • currentValue:数组中正在处理的当前元素。
    • index(可选):数组中正在处理的当前元素的索引。
    • array(可选):调用filter方法的数组。
    • 该函数需要返回一个布尔值,true表示该元素符合条件,会被包含在新数组中;false则表示不符合条件,不会被包含。
  • thisArg(可选):执行callback函数时使用的this值。

filter方法的基本使用

假设我们有一个包含数字的数组,现在想要筛选出数组中的偶数。示例代码如下:

let numbers = [1, 2, 3, 4, 5, 6];
let evenNumbers = numbers.filter((number) => number % 2 === 0);
console.log(evenNumbers); 

在上述代码中,filter方法遍历numbers数组,将每个元素传递给箭头函数(number) => number % 2 === 0。箭头函数判断该元素是否为偶数,如果是则返回truefilter方法会将返回true对应的元素收集到新数组evenNumbers中。

filter方法的复杂应用

假设有一个包含商品对象的数组,每个商品对象有name(商品名称)、price(价格)和inStock(是否有库存)属性,现在我们想要筛选出价格小于100且有库存的商品。示例代码如下:

let products = [
    {name: 'Product A', price: 80, inStock: true},
    {name: 'Product B', price: 120, inStock: false},
    {name: 'Product C', price: 90, inStock: true}
];
let filteredProducts = products.filter((product) => product.price < 100 && product.inStock);
console.log(filteredProducts); 

在这个例子中,filter方法遍历products数组,将每个商品对象传递给箭头函数(product) => product.price < 100 && product.inStock。箭头函数根据商品的价格和库存情况返回布尔值,filter方法将返回true对应的商品对象收集到filteredProducts数组中。

filter方法与thisArg的使用

filter方法的回调函数中需要使用this时,thisArg同样重要。例如:

let store = {
    maxPrice: 100,
    filterProducts: function (products) {
        return products.filter(function (product) {
            return product.price < this.maxPrice;
        }, this);
    }
};
let products = [
    {name: 'Product X', price: 80},
    {name: 'Product Y', price: 120}
];
let result = store.filterProducts(products);
console.log(result); 

在上述代码中,filterProducts方法中的filter回调函数需要访问store对象的maxPrice属性。通过将this作为filter方法的第二个参数传入,确保了回调函数中的this指向store对象,从而实现根据maxPrice筛选商品的功能。

JavaScript中数组的reduce方法

reduce方法是JavaScript数组操作中功能最为强大和灵活的方法之一。它对数组中的每个元素执行一个由您提供的“reducer”函数,将其结果汇总为单个返回值。

reduce方法的语法

reduce方法的语法如下:

let accumulator = arr.reduce(callback(accumulator, currentValue[, index[, array]]) {
    // 返回累加器的新值
}[, initialValue]);
  • callback:一个为数组中的每个元素执行的函数,该函数接收四个参数:
    • accumulator:累加器,累积回调的返回值。它是上一次调用回调时返回的累积值,或者是提供的initialValue(如果有提供的话)。
    • currentValue:数组中正在处理的当前元素。
    • index(可选):数组中正在处理的当前元素的索引。如果提供了initialValue,则索引从0开始;否则从1开始。
    • array(可选):调用reduce方法的数组。
  • initialValue(可选):作为第一次调用callback函数时的第一个参数的值。如果没有提供初始值,则将使用数组中的第一个元素,并且从数组的第二个元素开始执行回调函数。

reduce方法的基本使用 - 求和

假设我们有一个包含数字的数组,现在想要计算数组中所有数字的总和。示例代码如下:

let numbers = [1, 2, 3, 4];
let sum = numbers.reduce((accumulator, number) => accumulator + number, 0);
console.log(sum); 

在上述代码中,reduce方法从初始值0开始,依次将数组中的每个元素number传递给箭头函数(accumulator, number) => accumulator + number。箭头函数将accumulator(初始值为0,后续为上一次计算的结果)与当前元素number相加,并返回新的accumulator值。最终,reduce方法返回的就是数组中所有数字的总和。

reduce方法的复杂应用 - 数组扁平化

reduce方法可以用来实现数组的扁平化。假设有一个嵌套数组,例如[[1, 2], [3, 4], [5]],我们想要将其扁平化为[1, 2, 3, 4, 5]。示例代码如下:

let nestedArray = [[1, 2], [3, 4], [5]];
let flatArray = nestedArray.reduce((accumulator, subArray) => {
    return accumulator.concat(subArray);
}, []);
console.log(flatArray); 

在这个例子中,reduce方法从一个空数组[](初始值)开始。对于nestedArray中的每个子数组subArray,箭头函数(accumulator, subArray) => accumulator.concat(subArray)accumulator(初始为空数组,后续为上一次连接后的数组)与subArray连接起来,并返回新的accumulator值。最终,reduce方法返回的就是扁平化后的数组。

reduce方法与对象累加

reduce方法还可以用于对对象进行累加操作。假设有一个包含商品对象的数组,每个商品对象有name(商品名称)和quantity(数量)属性,现在我们想要统计每种商品的总数量。示例代码如下:

let products = [
    {name: 'Apple', quantity: 3},
    {name: 'Banana', quantity: 2},
    {name: 'Apple', quantity: 1}
];
let quantityByProduct = products.reduce((accumulator, product) => {
    if (!accumulator[product.name]) {
        accumulator[product.name] = product.quantity;
    } else {
        accumulator[product.name] += product.quantity;
    }
    return accumulator;
}, {});
console.log(quantityByProduct); 

在上述代码中,reduce方法从一个空对象{}(初始值)开始。对于products数组中的每个商品对象product,箭头函数首先检查accumulator对象中是否已经有该商品名称作为属性。如果没有,则将该商品的数量作为该属性的值;如果有,则将该商品的数量累加到已有的值上。最终,reduce方法返回的quantityByProduct对象中,每个属性名是商品名称,属性值是该商品的总数量。

综合使用map、filter和reduce

在实际的编程中,经常会将mapfilterreduce方法结合使用,以实现更复杂的数据处理。

示例:处理学生成绩数据

假设有一个包含学生对象的数组,每个学生对象有name(姓名)、scores(成绩数组)属性。现在我们想要计算每个学生的平均成绩,并筛选出平均成绩大于等于60分的学生,最后将这些学生的姓名以字符串形式连接起来。示例代码如下:

let students = [
    {name: 'Alice', scores: [50, 60, 70]},
    {name: 'Bob', scores: [40, 50, 60]},
    {name: 'Charlie', scores: [70, 80, 90]}
];
let qualifiedStudents = students
   .map((student) => {
        let averageScore = student.scores.reduce((accumulator, score) => accumulator + score, 0) / student.scores.length;
        return {...student, averageScore };
    })
   .filter((student) => student.averageScore >= 60)
   .reduce((accumulator, student) => {
        if (accumulator === '') {
            return student.name;
        } else {
            return accumulator + ', ' + student.name;
        }
    }, '');
console.log(qualifiedStudents); 

在上述代码中:

  1. 首先使用map方法,为每个学生对象计算平均成绩,并创建一个新的包含平均成绩的学生对象。这里在map方法的回调函数中使用了reduce方法来计算成绩总和,进而得到平均成绩。
  2. 然后使用filter方法,筛选出平均成绩大于等于60分的学生对象。
  3. 最后使用reduce方法,将筛选出的学生姓名连接成一个字符串,以逗号分隔。

注意事项与性能考虑

  1. 返回新数组mapfilterreduce方法都不会改变原始数组,而是返回一个新的数组或值。这在某些情况下需要特别注意,尤其是当你期望修改原始数组时。
  2. 性能问题:虽然这些方法提供了简洁且强大的功能,但在处理大量数据时,性能可能成为一个问题。例如,mapfilter方法会创建新数组,这可能会占用额外的内存。reduce方法虽然灵活,但如果回调函数逻辑复杂,也可能导致性能下降。在性能敏感的场景下,可以考虑使用传统的for循环,因为它们通常在性能上更优,特别是在处理简单的数组遍历和操作时。
  3. 函数作用域与this绑定:在使用mapfilterreduce方法的回调函数时,要注意函数的作用域和this的绑定。如果需要在回调函数中访问外部对象的属性,确保正确设置thisArg参数,或者使用箭头函数(箭头函数本身没有自己的this,它会从外部作用域继承this)。

通过深入理解和灵活运用mapfilterreduce等数组的高级方法,开发者可以更加高效、简洁地处理数组数据,提升JavaScript代码的质量和可读性。在实际项目中,根据具体的需求和场景,合理选择和组合这些方法,能够大大提高编程效率。同时,也要注意性能问题,确保代码在处理大数据量时依然能够高效运行。