本文是对《ES6函数式编程》的学习记录
组合的思想就是把小函数组合成大函数
const compose = (a,b)=>(c)=>a(b(c));
let splitIntoSpaces = (str)=>str.split(" ");
let count = (array)=>array.length;
//构建新函数计算字符串中单词的数量
const countWords = compose(count,splitIntoSpace);
countWords("Hello your reading about composition"); //5
const compose = (...fns)=>
(value)=>reduce(fns.reduce(),(acc,fn)=>fn(acc),value);
//判断一个字符串长度是奇数还是偶数
let oddOrEven = (ip)=>ip%2==0?"even":"odd";
const oddOrEvenWords = compose(oddOrEven,count,splitIntoSpaces);
oddOrEvenWords("Hello your reading about composition");//odd
compose的数据流是从右到左,因为最右侧的函数首先执行,将数据传递给下一个函数,最左侧的最后执行。pipe函数就是从左到右处理数据流的过程称为管道(pipeline)或者序列(sequence)
const pipe = (...fns)=>
(value)=>reduce(fns,(acc,fn)=>fn(acc),value);//fns没有使用reverse
//重新执行
const oddOrEvenWords = pipe(splitIntoSpace,count,oddOrEven);
oddOrEvenWords("Hello your reading about composition");//odd
compose(f,compose(g,h)) == compose(compose(f,g),h);
//compose(compose(f,g),h)
let oddOrEvenWords = compose(compose(oddOrEven,count),splitIntoSpaces);
let oddOrEvenWords("Hello your reading about composition");//odd
//compose(f,compose(g,h))
let oddOrEvenWords = compose(oddOrEven,compose(count,splitIntoSpaces));
let oddOrEvenWords("Hello your reading about composition");//odd
用一种纯函数式的方式帮助我们处理错误
::: tip
const Container = function(val){
this.val = val;
}
//使用箭头函数 由于this在箭头函数中没有 prototype和constructor 因此会报错
const Container = function(val)=>{
this.val = val;
}
//当new container的时候,将会报错如下: Container is not a constructor(...)(anonymous function)
//应用container
let testValue = new Container(3) //Container(value:3)
let testObj = new Container({a:1}) //Container(value:{a:1})
let testArray = new Container([1,2]) //Container(value:[1,2])
//of方法定义
Container.of = function(value){
return new Container(value);
}
//用of创建container
testValue = Container.of(3) //Container(value:3)
testObj = Container.of({a:1}) //Container(value:{a:1})
testArray = Container.of([1,2]) //Container(value:[1,2])
//container.of嵌套 将输出如下
Container{
value:Container{
value:3,
}
}
Container.prototype.map =function(fn){
return Container.of(fn(this.value));
}
let double =(x)=>x+x;
Container.of(3).map(double) //Container(value:6)
//container链式调用
Container.of(3).map(double).map(double).map(double) //Container{value:24}
::: tip
函子是一个普通对象(在其他语言中,可能是一个类)它实现了map函数,在遍历每个对象值的时候生成了一个新对象-函子是实现了map契约的对象
:::
处理函数中的代码
//MayBe定义
const MayBe = function(val){
this.val = val;
}
MayBe.of = function(val){
return new MayBe(val);
}
//MayBe的map函数定义
MayBe.prototype.isNothing = function(){
//应用传入函数之前先检查容器中的值是否为null或者undefined
return (this.value === null||this.value===undefined);
};
MayBe.prototype.map = function(fn){
//map把应用函数的返回值放回了容器
return this.isNothing()?MayBe.of(null):MayBe.of(fn(this.value));
};
// in action
//创建一个MayBe
MayBe.of("String")map((x)=x.toUpperCase())
//返回
MayBe {vale:"STRING"}
//更重要和有趣的是 x是否是null或者undefined并不关心。 它已经被MayBe函子抽象出来了
(x)=>x.toUpperCase()
//传入null
MayBe.of(null).map((x)=>toUpperCase())
//it returns
MayBe {value:null}
//map链式调用
MayBe.of("George")
.map((x)=>x.toUpperCase())
.map((x)=>"Mr. "+ x)
//MayBe {value: "Mr. GEORGE "}
用一个api获取Reddit网站子版块的Top10数据
let getTopTenSubRedditPosts = (type)=>{
let response
try{
response = JSON.parse(request('GET','https://www.reddit.com/r/subreddits/'+type+".json?limit=10").getBody('utf8'))
}catch(err){
response = {message:"Something went wrong",errorCode: err['statusCode']}
}
return response;
}
//request 来自 sync-request
//调用api
getTopTenSubRedditPosts("new")
使用maybe实现获取Reddit子版块的Top10帖子
//导入类库的ArrayList对象
import {arrayUtils} from '.../lib/es6-functional.js'
let getTopTenSubRedditData = (type)=>{
let response = getTopTenSubRedditPosts(type);
return MayBe.of(response)
//函数序列
.map((arr)=>arr['data'])
.map((arr)=>arr['children'])
//遍历children 并且只返回title和URL
.map((arr)=>arrayUtils.map(arr,(x)=>{
return {
titile:x['data'].title,
url:x['data'].url
}
}))
}
Either函子能够解决分支拓展问题(branching-out problem)
给出一个上下文,看一下上节的例子
MayBe.of("George")
.map(()=>undefined)
.map((x)=>"Mr."+x)
//返回如下结果
MayBe {value:null}
const Nothing = function(val){
this.value = val;
};
Nothing.of = function(val){
return new Nothing(val);
}
//返回对象本身
Nothing.prototype.map = function(f){
return this;
};
const Some = function(val){
this.value = val;
};
Some.of = function(val){
return new Some(val);
};
//一个container的副本
Some.prototype.map = function(fn){
return Some.of(fn(this.value));
}
//可以在some上运行函数,但是不能在nothing上面运行。
//eg:
Some.of("test").map((x)=>x.toUpperCase())
=>Some {value:"TEST"}
Nothing.of("test").map((x)=>x.toUpperCase())
=>Nothing {value:"test"}
const Either = {
Some:Some,
Nothing:Nothing
}
let getTopTenSubRedditData = (type)=>{
let response = getTopTenSubRedditPosts(type);
return MayBe.of(response).map((arr)=>arr['data'])
.map((arr)=>arr['children'])
.map((arr)=>arrayUtils.map(arr,(x)={return {
title:x['data'].title,
url:x['data'].url
}}))
}
//传入错误l类型
getTopTenSubRedditData('unknow')
=>MayBe(value:null)
let getTopTenSubRedditPostsEither = (type)=>{
let response
try{
//封装正确响应
response = Some.of(JSON.parse(request('get','https://www.reddit.com/r/subreddits'+type+".json?limit=10").getBody('utf8')))
}catch(err){
//封装错误响应
response = Nothing.of((message:"Something went wrong",errorCode:err['statusCode']))
}
return response;
}
let getTopTenSubRedditDataEither=(type)=>{
let response = getTopTenSubRedditPostsEither(type);
return response.map((arr)=>arr['data'])
.map((arr)=>arr['children'])
.map((arr)=>arrayUtils.map(arr,(x)={
return {
title:x['data'].title,
url:x['data'].url
}
}))
}
//使用错误的Reddit数据调用新的api
getTopTenSubRedditDataEither('new2')
//返回
Nothing(value:{message:'Something went wrong',errorCode:404})
使用either获得了分支失败的确切原因,在getTopTenSubRedditEither返回Nothing,因此函数永远不会执行