资讯专栏INFORMATION COLUMN

JavaScript .filter() 方法全解析

lidashuang / 1785人阅读

摘要:迭代方法是指接受针对数组的每个项运行的函数。值是强制转换为布尔值时计算为的任何值。值得注意的是,这个数组是不变。由于布尔值只有和,这意味着很容易翻转谓词的值。检查数组的每个成员是否传递谓词,而检查数组的成员是否通过谓词。

.filter是一个内置的数组迭代方法,它接受一个“谓词(译者注: 指代一个过滤条件的函数)”,该“谓词”针对每个值进行调用,并返回一个符合该条件(“truthy值”)的数组。

上面那句话包含了很多信息,让我们来逐一解答一下。

“内置”只是意味着它是语言的一部分 - 您不需要添加任何库来访问此功能。

“迭代方法”是指接受针对数组的每个项运行的函数。.map和.reduce都是迭代方法的示例。

“谓词”是指.fiflter中接受的的函数。

“truthy值”是强制转换为布尔值时计算为true的任何值。几乎所有值都是真实的,除了:undefined,null,false,0,NaN或“”(空字符串)。

让我们来看看下面这个例子,看一下.filter是怎么运行的。

const restaurants = [
    {
        name: "Dan"s Hamburgers",
        price: "Cheap",
        cuisine: "Burger",
    },
    {
        name: "Austin"s Pizza",
        price: "Cheap",
        cuisine: "Pizza",
    },
    {
        name: "Via 313",
        price: "Moderate",
        cuisine: "Pizza",
    },
    {
        name: "Bufalina",
        price: "Expensive",
        cuisine: "Pizza",
    },
    {
        name: "P. Terry"s",
        price: "Cheap",
        cuisine: "Burger",
    },
    {
        name: "Hopdoddy",
        price: "Expensive",
        cuisine: "Burger",
    },
    {
        name: "Whataburger",
        price: "Moderate",
        cuisine: "Burger",
    },
    {
        name: "Chuy"s",
        cuisine: "Tex-Mex",
        price: "Moderate",
    },
    {
        name: "Taquerias Arandina",
        cuisine: "Tex-Mex",
        price: "Cheap",
    },
    {
        name: "El Alma",
        cuisine: "Tex-Mex",
        price: "Expensive",
    },
    {
        name: "Maudie"s",
        cuisine: "Tex-Mex",
        price: "Moderate",
    },
];

这是很多信息。我现在想要一个汉堡,所以让我们过滤掉一下这个数组。

const isBurger = ({cuisine}) => cuisine === "Burger";
const burgerJoints = restaurants.filter(isBurger);

isBurger是谓词,而burgerJoints是new数组,它是餐馆的子集。值得注意的是,restaurants 这个数组是不变。

下面是两个正在呈现的列表的简单示例 - 一个原始的餐馆数组,以及一个过滤的burgerJoints数组。

See the Pen .filter - isBurger by Adam Giese (@AdamGiese) on CodePen.

否定谓词

对于每个谓词,都有一个相反的否定谓词。

谓词是一个返回布尔值的函数。由于布尔值只有true 和 false,这意味着很容易“翻转”谓词的值。

我吃了汉堡已经过了几个小时,现在又饿了。这一次,我想过滤out汉堡尝试新的东西。一种选择是从头开始编写新的isNotBurger谓词。

const isBurger = ({cuisine}) => cuisine === "Burger";
const isNotBurger = ({cuisine}) => cuisine !== "Burger";

但是,请查看两个谓词之间的相似程度。这不是 DRY code。另一种选择是调用isBurger谓词并翻转结果。

const isBurger = ({cuisine}) => cuisine === "Burger";
const isNotBurger = restaurant => !isBurger(restaurant);

这个更好!如果汉堡的定义发生变化,您只需要在一个地方更改逻辑。但是,如果我们想要一些否定的谓词呢?由于这是我们可能经常想要做的事情,因此编写否定函数可能是个好主意。

const negate = predicate => function() {
  return !predicate.apply(null, arguments);
}

const isBurger = ({cuisine}) => cuisine === "Burger";
const isNotBurger = negate(isBurger);

const isPizza = ({cuisine}) => cuisine === "Pizza";
const isNotPizza = negate(isPizza);

你可能有一些问题。

什么是.apply?

MDN:

apply()方法调用具有给定this的函数,并将参数作为数组(或类数组对象)提供。
什么是arguments?

MDN:

arguments对象是所有(非箭头)函数中可用的局部变量。您可以使用参数在函数内引用函数的参数object.
为什么要使用旧的function,而不使用更酷的箭头函数?

在这种情况下,使用传统函数是必要的,因为arguments对象在传统函数上是_唯一_可用的。

到2018年8月20日。正如一些评论家所正确指出的那样, 你可以使用rest参数用[箭头函数写 negate ](https://css-tricks.com/level-...。

返回谓词

正如我们在使用negate函数看到的那样,函数很容易在JavaScript中返回一个新函数。这对于编写“谓词”非常有用。例如,让我们回顾一下我们的isBurger和isPizza谓词。

const isBurger = ({cuisine}) => cuisine === "Burger";
const isPizza  = ({cuisine}) => cuisine === "Pizza";

这两个谓词具有相同的逻辑;他们只是在比较上有所不同。因此,我们可以将共享逻辑包装在isCuisine函数中。

const isCuisine = comparison => ({cuisine}) => cuisine === comparison;
const isBurger  = isCuisine("Burger");
const isPizza   = isCuisine("Pizza");

现在,如果我们想开始检查价格怎么办?

const isPrice = comparison => ({price}) => price === comparison;
const isCheap = isPrice("Cheap");
const isExpensive = isPrice("Expensive");

现在isCheap和isExpensive 都是DRY(译者注:Don"t repeat yourself ,一种编程原则,不也要写重复的代码),isPizza和isBurger都是DRY,但isPrice和isCuisine可以公用他们的逻辑!

const isKeyEqualToValue = key => value => object => object[key] === value;

// these can be rewritten
const isCuisine = isKeyEqualToValue("cuisine");
const isPrice = isKeyEqualToValue("price");

// these don"t need to change
const isBurger = isCuisine("Burger");
const isPizza = isCuisine("Pizza");
const isCheap = isPrice("Cheap");
const isExpensive = isPrice("Expensive");

对我来说,这就是箭头功能之美。在一行中,您可以优雅地创建三阶函数。

看看从原始餐馆阵列创建多个筛选列表是多么容易?

See the Pen .filter - returning predicates by Adam Giese (@AdamGiese) on CodePen.

撰写谓词

我们现在可以通过汉堡或廉价的价格过滤我们的阵列......但是如果你想要cheap burgers怎么办?一种选择是将两个过滤器链接在一起。

const cheapBurgers = restaurants.filter(isCheap).filter(isBurger);

另一个选择是将两个谓词“组合”成一个谓词。

const isCheapBurger = restaurant => isCheap(restaurant) && isBurger(restaurant);
const isCheapPizza = restaurant => isCheap(restaurant) && isPizza(restaurant);

看看所有重复的代码。我们绝对可以将它包装成一个新功能!

const both = (predicate1, predicate2) => value =>
  predicate1(value) && predicate2(value);

const isCheapBurger = both(isCheap, isBurger);
const isCheapPizza = both(isCheap, isPizza);

const cheapBurgers = restaurants.filter(isCheapBurger);
const cheapPizza = restaurants.filter(isCheapPizza);

如果你没有披萨或汉堡包怎么办?

const either = (predicate1, predicate2) => value =>
  predicate1(value) || predicate2(value);

const isDelicious = either(isBurger, isPizza);
const deliciousFood = restaurants.filter(isDelicious);

这是朝着正确方向迈出的一步,但是如果您想要包含两种以上的食物呢?这不是一种可扩展的方法。有两种内置的数组方法在这里派上用场。.every和.some都是谓词方法,也接受谓词。.every检查数组的每个成员是否传递谓词,而.some检查数组的any成员是否通过谓词。

const isDelicious = restaurant =>
  [isPizza, isBurger, isBbq].some(predicate => predicate(restaurant));

const isCheapAndDelicious = restaurant =>
  [isDelicious, isCheap].every(predicate => predicate(restaurant));

并且,像往常一样,让我们​​将它们包装成一些有用的抽象。

const isEvery = predicates => value =>
  predicates.every(predicate => predicate(value));

const isAny = predicates => value =>
  predicates.some(predicate => predicate(value));

const isDelicious = isAny([isBurger, isPizza, isBbq]);
const isCheapAndDelicious = isEvery([isCheap, isDelicious]);

isEvery和isAny都接受一个谓词数组并返回一个谓词。

由于所有这些谓词都可以通过高阶函数轻松创建,因此根据用户的交互创建和应用这些谓词并不困难。综合我们学到的所有课程,这里是一个应用程序示例,通过应用基于按钮点击的过滤器来搜索餐馆。

See the Pen .filter - dynamic filters by Adam Giese (@AdamGiese) on CodePen.

总结
过滤器是JavaScript开发的重要组成部分。无论您是从API响应中挑选出错误数据还是响应用户交互,您都会无数次想要数组值的子集。我希望这个概述有助于您可以操作谓词来编写更易读和可维护的代码。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/52953.html

相关文章

  • JavaScript .filter() 方法解析

    摘要:迭代方法是指接受针对数组的每个项运行的函数。值是强制转换为布尔值时计算为的任何值。值得注意的是,这个数组是不变。由于布尔值只有和,这意味着很容易翻转谓词的值。检查数组的每个成员是否传递谓词,而检查数组的成员是否通过谓词。 .filter是一个内置的数组迭代方法,它接受一个谓词(译者注: 指代一个过滤条件的函数),该谓词针对每个值进行调用,并返回一个符合该条件(truthy值)的数组。 ...

    sherlock221 评论0 收藏0
  • XSS解析—初探

    摘要:持久型相当于存储型跨站脚本。这玩意儿涉及到一点和浏览器引擎解析的问题。说正事,是支持码形式的。的调用有几个概念我们先来了解一下。本来说的只用天完成的,没想到各种事儿拖了两天,。 前些时间准备面试的时候一直会遇到这个XSS,寥寥几句话好像很简单,后来看到同学的《XSS跨站脚本攻击剖析与防御》这本书,稍微翻看了一下,其中的学问还是挺多的。这系列的文章就当做读书笔记吧。 什么是XSS 听过很...

    Lucky_Boy 评论0 收藏0
  • Javascript 数组方法解析(包含es6,es7)

    摘要:数组方法全解析包含数组自带属性返回创建数组对象的原型函数返回数组对象的长度这个是老熟人了,可以增加数组的原型方法和属性,这个放在后面的继承中讲数组的方法首先让我们看看数组的对象属性。 Javascript 数组方法全解析(包含es6) 1. 数组自带属性 constructor //返回创建数组对象的原型函数 length //返回数组对象的长度 prototype //这个是老...

    zxhaaa 评论0 收藏0
  • V8 JavaScript 引擎:高性能的 ES2015+

    摘要:最后,客户端只是依赖于引擎的环境之一。新的编译器管道利用来实现,并生成可以转换生成器控制流到简单的本地控制流的字节码。可以更容易地优化所得到的字节码,因为它不需要知道关于生成器控制流的任何具体内容,只是如何保存和恢复函数的状态。 本文转载自:众成翻译译者:smartsrh链接:http://www.zcfy.cc/article/2978原文:https://v8project.blo...

    songze 评论0 收藏0
  • Day06 - Fetch、filter、正则表达式实现快速古诗匹配

    摘要:正则表达式实现快速古诗匹配作者简介是推出的一个天挑战。数据匹配操作使用基础参考文档项目源码分析正则找出匹配的诗句替换高亮的标签构造值会返回带搜索关键字的新数组。执行对大小写不敏感的匹配。 Day06 - Fetch、filter、正则表达式实现快速古诗匹配 作者:©liyuechun 简介:JavaScript30 是 Wes Bos 推出的一个 30 天挑战。项目免费提供了 30 ...

    Warren 评论0 收藏0

发表评论

0条评论

lidashuang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<