JavaScript .filter() 方法全解析
.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?
apply()方法调用具有给定this的函数,并将参数作为数组(或类数组对象)提供。
什么是arguments?
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响应中挑选出错误数据还是响应用户交互,您都会无数次想要数组值的子集。我希望这个概述有助于您可以操作谓词来编写更易读和可维护的代码。
JavaScript .filter() 方法全解析的更多相关文章
- Redis大 key的发现与删除方法全解析
个推作为国内第三方推送市场的早期进入者,专注于为开发者提供高效稳定的推送服务,经过9年的积累和发展,服务了包括新浪.滴滴在内的数十万APP.由于我们推送业务对并发量.速度要求很高,为此,我们选择了高性 ...
- Redis大key的发现与删除方法全解析
个推作为国内第三方推送市场的早期进入者,专注于为开发者提供高效稳定的推送服务,经过9年的积累和发展,服务了包括新浪.滴滴在内的数十万APP.由于我们推送业务对并发量.速度要求很高,为此,我们选择了高性 ...
- Android网络编程(三)Volley使用方法全解析
相关文章 Android网络编程(一)HTTP协议原理 Android网络编程(二)HttpClient与HttpURLConnection 前言 Volley想必许多人都用过,为了建立网络编程的知识 ...
- JavaScript数组方法reduce解析
Array.prototype.reduce() 概述 reduce()方法是数组的一个实例方法(共有方法),可以被数组的实例对象调用.reduce() 方法接收一个函数作为累加器(accumulat ...
- php异常及错误信息捕获并记录日志实现方法全解析
php异常处理 什么是异常? PHP 5 提供了一种新的面向对象的错误处理方法.异常处理用于在指定的错误(异常)情况发生时改变脚本的正常流程.这种情况称为异常. 当异常被触发时,通常会发生: 当前代码 ...
- javascript ajax 脚本跨域调用全解析
javascript ajax 脚本跨域调用全解析 今天终于有点时间研究了一下javsscript ajax 脚本跨域调用的问题,先在网上随便搜了一下找到一些解决的办法,但是都比较复杂.由是转到jqu ...
- Android图片载入框架最全解析(一),Glide的基本使用方法
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53759439 本文同步发表于我的微信公众号.扫一扫文章底部的二维码或在微信搜索 郭 ...
- [译]Javascript数列filter方法
本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...
- JavaScript Array filter() 方法
JavaScript Array filter() 方法 var ages = [32, 33, 16, 40]; function checkAdult(age) { return age > ...
随机推荐
- 搭建 Git 服务器(基于 CentOS 7)
服务器上的-Git-架设服务器-官网参考 对于规模比较小的团队,可以直接搭建 Git 服务器,逐个收集研发同学的证书配置进来即可.如果团队规模比较大,可以直接采用 GitLab.Drone 等现成的带 ...
- Python笔记(二十七)_魔法方法_容器
定制容器 容器类型的协议: 定制不可变容器,只需要定义__len__()和__getitem__()方法 定制可变容器,需要定义__len__().__getitem__().__setitem__( ...
- mac 添加mysql的环境变量和删除mysql
添加环境变量 1.创建 .bash_profile,已创建过忽略这步 (1)启动终端 (2)进入当前用户的home目录(默认就是): cd ~ 或 cd /Users/YourMacU ...
- chapter2
Chapter2 Tip1 静态工厂方法代替构造器 公有的静态方法,只是一个返回类实例的静态方法. 静态工厂方法的优势: 优势一: 有名称,如果构造器本身没有正确的描述被返回的对象,具有适当名称的静态 ...
- js 一道题目引发的正则的学习
正则表达式中的特殊字符 字符 含意 \ 做为转意,即通常在"\"后面的字符不按原来意义解释,如/b/匹配字符"b",当b前面加了反斜杆后/\b/,转意为匹配一个 ...
- oracle--角色权限
将不同权限赋予角色,再将角色赋予用户,起到管理权限的作用 SQL> create role myrole; 角色已创建. SQL> grant create session to myro ...
- Android之异步调用
概述 AsyncTask可以很好的,准确的使用UI线程,他可以将一个比较耗时(几秒钟)的动作运行在后台,并且能将结果返回至UI线程中,不需要通过(Thread操作和Handler操作). 使用时必须通 ...
- [AGC035F]Two Histograms
Description 你有一个 \(N\) 行.\(M\) 列的.每个格子都填写着 0 的表格.你进行了下面的操作: 对于每一行 \(i\) ,选定自然数 \(r_i\ (0 ≤ r i ≤ M ) ...
- Django之视图(V)
Django的View(视图) 一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一个404错误, ...
- js中return、return false 、return true各自代表什么含义
return语句代表需要返回一个值,如果不需要就不需要使用return语句.都类似一个出口,return 可以结束方法体中 return后面部分代码的执行.return false 或者 return ...