ES6中的高阶函数:如同 a => b => c 一样简单
作者:Sequoia McDowell
2016年01月16日
ES6来啦!随着越来越多的代码库和思潮引领者开始在他们的代码中使用ES6,以往被认为是“仅需了解”的ES6特性变成了必需的代码常识。这不仅仅是新的语法学习 - 在许多范例中, ES6中新的语言特性可以让在ES5中写起来非常麻烦的表达变得更加简单,进而鼓励了新表达方式的使用。下面我们将关注一个这样简洁表达的使用范例:ES6中的箭头函数如何使高阶函数的书写更加简便。
高阶函数是至少具有以下两种功能之一的函数:
- 使用一个或多个函数作为实参
- 返回一个函数作为结果
本文的目的并不是说服你立即采用这种新方式,尽管笔者非常鼓励你尝试使用!本文旨在让你熟悉这种表达方式,这样当你在遇到其他人基于ES6写的代码库时,不会如笔者当初一样看着这些陌生代码挠头不解。如果你在此之前需要先复习一下箭头语法的知识,请先查阅这篇文章。
希望你熟悉有返回值的箭头函数:
const square = x => x * x;
但是下面这两行代码是什么意思?
const has = p => o => o.hasOwnProperty(p);
const sortBy = p => (a, b) => a[p] > b[p];
“p 返回 o 返回 o.hasOwnProperty…”这句代码是什么意思?我们能这样用吗?
理解语法
为了说明带箭头的高阶函数的书写规则,让我们一起来看一个经典示例:加法函数。在ES5中会这样写:
function add(x){
return function(y){
return y + x;
};
}
var addTwo = add(2);
addTwo(3); // => 5
add(10)(11); // => 21
我们的加法函数输入x,返回了一个输入y返回值是y + x的函数。那我们应该如何用箭头函数表达这个函数呢?已知...
- 箭头函数定义是一个表达式,并且
- 箭头函数隐式返回单一表达式结果
那么我们所要做的就是让另一个箭头函数作为我们箭头函数的函数体,因此表达方式如下:
const add = x => y => y + x;
// outer function: x => [inner function, uses x]
// inner function: y => y + x;
现在我们就可以通过一个与变量x相关的返回值创建内部函数:
const add2 = add(2);// returns [inner function] where x = 2
add2(4); // returns 6: exec inner with y = 4, x = 2
add(8)(7); // 15
我们所写的加法函数并不是超级有用,但是它可以说明一个外函数如何输入一个以x为变量的参数函数,并在它返回的函数中引用此函数。
用户分类
假如你在github上看到了一个ES6代码库,并且遇到了下面这样的代码:
const has = p => o => o.hasOwnProperty(p);
const sortBy = p => (a, b) => a[p] > b[p];
let result;
let users = [
{ name: 'Qian', age: 27, pets : ['Bao'], title : 'Consultant' },
{ name: 'Zeynep', age: 19, pets : ['Civelek', 'Muazzam'] },
{ name: 'Yael', age: 52, title : 'VP of Engineering'}
];
result = users
.filter(has('pets'))
.sort(sortBy('age'));
这些代码又是什么意思?我们叫它箭头原型的排列和滤波方法,每一种方法都使用一个单功能参数,但是我们会调用返回函数的函数来筛选和排序,而不是编写表达式去做这些。
让我们来看一看,在下列每种情况下返回函数的表达方式。
无高阶函数:
result = users
.filter(x => x.hasOwnProperty('pets')) //pass Function to filter
.sort((a, b) => a.age > b.age); //pass Function to sort
有高阶函数:
result = users
.filter(has('pets')) //pass Function to filter
.sort(sortBy('age')); //pass Function to sort
在每个实例中,过滤过程都是通过检查对象是否含有名为“pets”的属性值的函数执行的。
为什么它是有用的?
它的有效性出于以下几个原因:
- 它减少了重复代码
- 它简化了代码的可重用性
- 它提升了代码含义的清晰度
想象一下我们只想过滤出有宠物和有职位的用户,我们可以在其中添加另一个函数:
result = users
.filter(x => x.hasOwnProperty('pets'))
.filter(x => x.hasOwnProperty('title'))
...
这里的重复只是徒增杂乱:它的简明度没有提升,只是写了更多代码妨碍视线。下面是可实现同样功能的使用了has函数的代码:
result = users
.filter(has('pets'))
.filter(has('title'))
...
这段代码更加短小精悍且易于书写,还可以减少打错字的情况。笔者同时认为这段代码大大增加了阅读清晰度,一眼就能读出代码的含义。
至于函数重用性,如果需要在很多地方过滤出有宠物的用户或者有职位的用户,你可以创建如下函数,并按需重用:
const hasPets = has('pets');
const isEmployed = has('title');
const byAge = sortBy('age');
let workers = users.filter(isEmployed);
let petOwningWorkers = workers.filter(hasPets);
let workersByAge = workers.sort(byAge);
我们也可以使用已给出的函数来获得单返回值,并不仅仅是用于过滤数组:
let user = {name: 'Assata', age: 68, title: 'VP of Operations'};
if(isEmployed(user)){ // true
//do employee action
}
hasPets(user); // false
has('age')(user); //true
更进一步
让我们来写一个能检查对象拥有一个有固定值的主键的过滤函数。has函数可检查主键,然而我们需要知道两项值(主键和键值),而不是一项,来同时检查值。下面请看一种方法:
//[p]roperty, [v]alue, [o]bject:
const is = p => v => o => o.hasOwnProperty(p) && o[p] == v;
// broken down:
// outer: p => [inner1 function, uses p]
// inner1: v => [inner2 function, uses p and v]
// inner2: o => o.hasOwnProperty(p) && o[p] = v;
此处,叫做“is”的函数可执行以下三件事:
- 取一个属性名并且返回一个函数,该函数……
- 取一个函数值返回一个函数,该函数……
- 取一个对象,并检测该对象是否有特定函数值的特定属性,最终返回一个布尔值。
下面是一个使用is函数来过滤用户的示例:
const titleIs = is('title');
// titleIs == v => o => o.hasOwnProperty('title') && o['title'] == v;
const isContractor = titleIs('Contractor');
// isContractor == o => o.hasOwnProperty('contractor') && o['title'] == 'Contractor';
let contractors = users.filter(isContractor);
let developers = users.filter(titleIs('Developer'));
let user = {name: 'Viola', age: 50, title: 'Actress', pets: ['Zak']};
isEmployed(user); // true
isContractor(user); // false
书写风格说明
看一眼下面这个函数,记下你弄清楚函数意义的时间:
const i = x => y => z => h(x)(y) && y[x] == z;
现在请再看一下同功能仅在书写风格中略微不同的函数:
const is = prop => val => obj => has(prop)(obj) && obj[prop] == val;
可见,书写一行越简洁越好的函数的编码趋势是以牺牲可读性为代价的。请克制自己这样做的冲动!简短却无意义的变量名的确看起来很漂亮,但是让人难以理解函数的功能。起一个包含多个词的有意义的变量名和函数名,其实是在帮你自己和同事理解函数功能。
还有一件事...
如果你想以年龄降序排序而不是升序排列,该怎么做呢?或者说查找不是雇员的用户该怎么做呢?我们需要去写一个新的functionssortByDesc 或者notHas程序吗?当然不用!我们可以将已有的返回布尔值的函数封装起来,用一个反转其布尔值的函数来实现上述功能,反之亦然。
//take args, pass them thru to function x, invert the result of x
const invert = x => (...args) => !x(...args);
const noPets = invert(hasPets);
let petlessUsersOldestFirst = users
.filter(noPets)
.sort(invert(sortBy('age')));
总结
函数式编程在编程界的势头越来越强劲,而ES6使得在JavaScript中采用这种编程思想更加容易。如果你在JavaScript编程过程中还没遇到过函数式编程风格的代码,你很可能在接下来的几个月里就能遇到。这意味着即便你不喜欢这种风格,理解它的基础知识也是非常重要的,有的知识在这里已经提到过了。希望这篇文章提出的概念能够帮你在实际遇到ES6代码时准备好前提知识,更希望它能鼓励你尝试这种编程风格!
OneAPM 助您轻松锁定 .NET 应用性能瓶颈,通过强大的 Trace 记录逐层分析,直至锁定行级问题代码。以用户角度展示系统响应速度,以地域和浏览器维度统计用户使用情况。想阅读更多技术文章,请访问 OneAPM 官方博客。
本文转自 OneAPM 官方博客
原文链接:https://strongloop.com/strongblog/higher-order-functions-in-es6easy-as-a-b-c/
ES6中的高阶函数:如同 a => b => c 一样简单的更多相关文章
- Python 函数式编程 & Python中的高阶函数map reduce filter 和sorted
1. 函数式编程 1)概念 函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念.wiki 我们知道,对象是面向对象的第一型,那么函数式编程也是一样,函数是函数 ...
- Python中的高阶函数与匿名函数
Python中的高阶函数与匿名函数 高阶函数 高阶函数就是把函数当做参数传递的一种函数.其与C#中的委托有点相似,个人认为. def add(x,y,f): return f( x)+ f( y) p ...
- JS中的高阶函数
JS中的高阶函数 高阶函数是指以函数作为参数的函数,并且可以将函数作为结果返回的函数. 1. 高阶函数 接受一个或多个函数作为输入 输出一个函数 至少满足以上一个条件的函数 在js的内置对象中同样存在 ...
- Java中的函数式编程(五)Java集合框架中的高阶函数
写在前面 随着Java 8引入了函数式接口和lambda表达式,Java 8中的集合框架(Java Collections Framework, JCF)也增加相应的接口以适应函数式编程. 本文的 ...
- JavaScript中的高阶函数
之前写的<JavaScript学习手册>,客户跟我说有些内容不适合初学者,让我删了,感觉挺可惜的,拿到这里和大家分享. JavaScript中的一切都是对象,这句话同样适用于函数.函数对象 ...
- javascript中的高阶函数, 和 类定义Function, 和apply的使用
参考: http://www.cnblogs.com/delin/archive/2010/06/17/1759695.html js中的类, 也是用function关键字来定义的: function ...
- python中的高阶函数
高阶函数英文叫Higher-order function.什么是高阶函数?我们以实际代码为例子,一步一步深入概念. 变量可以指向函数 以Python内置的求绝对值的函数abs()为例,调用该函数用以下 ...
- scala中的高阶函数
版权申明:转载请注明出处. 文章来源:http://bigdataer.net/?p=332 排版乱?请移步原文获得更好阅读体验 1.scala中的函数 scala是一门面向对象和函数式编程相结合的语 ...
- Javascript中的高阶函数介绍
高阶函数:高阶看上去就像是一种先进的编程技术的一个深奥术语,一开始我看到的时候我也这样认为的. Javascript的高阶函数 然而,高阶函数只是将函数作为参数或返回值的函数.以下面的Hello,Wo ...
随机推荐
- 4.css度量单位
在 CSS 长度设置中,我们经常需要使用到度量单位,即以什么样的单位设计我们的字 体或边框长度.而在 CSS 中长度单位又分为绝对长度和相对长度. 绝对长度指的是现实世界的度量单位,CSS 支持五种绝 ...
- Moses manual 中Basline System 2.3.4节用IRSTLM创建语言模型的命令有误
手册里写到: ~/irstlm/bin/compile-lm \ --text yes \ news-commentary-v8.fr-en.lm.en.gz \ news-commentary-v8 ...
- C# 编译JS -Evaluator
忘记哪里转过来的,自己mark一下 //// <summary> /// 动态求值 /// </summary> public class Evaluator { /// &l ...
- JavaWeb之 JSP基础
什么是JSP JSP的全称是java server page, java服务页面.是提供java服务的页面~ 那么和Servlet有什么区别呢?JSP的页面既可以写java代码~也可以写html代码哦 ...
- 在Windows下使用BAT调度存储在资源库中的KTR
描述: 在Windows下使用BAT调度存储在资源库中的KTR 准备环境: 1.ktr文件(该KTR必须是存储在资源管库中的) 2.bat文件 @echo off D: cd D:\software\ ...
- CSV 文件读取类
class CsvReader { private $csv_file; private $spl_object = null; private $error; public function __c ...
- 在DNS管理器——用局域网IP指定你所起的域名名称
在服务器上面,进行以下相关的操作: 第一步:打开DNS管理器; 第二步:在“正向查找区域”添加域名为:icanyin.net; 第三步:添加主机pm,将域名pm.icanyin.net解析为IP地址: ...
- VC++编程之对话框贴图
基于对话框的程序写好后,为对话框贴上个图片让界面更加美观(我承认做界面,MFC显得力不从心,不如QT). 其实很简单,我们以位图为例,选好我们需要的位图资源(bmp),假若自己的图片不是位图资源,可以 ...
- UML 中的用例图解析以及starUML详细介绍
UML中的用例(Use Case)概念分析及StarUML实例 在UML 中use case 似 乎最簡單的,用例建模的最主要功能就是用来表达系统的功能性需求或行为,依我的理解用例建模可分为用例图和用 ...
- PE格式的理解(待补充)
PE文件格式 一.基本结构 1.DOS头一般到节区头成为PE头部分,其下称为PE体.文件的内容一般可分为代码(.text).数据(.data).资源(.rsrc),分别保存. 2.PE头与各节区的尾部 ...