30 天精通 RxJS (03):Functional Programming 通用函式

RxJS Logo

了解Functional Programming的通用函式,能让我们写出更简洁的代码,也能帮助我们学习RxJS

了解 Functional Programming 的通用函式,能让我们写出更简洁的代码,也能帮助我们学习 RxJS。

读者可能会很好奇,我们的主题是 RxJS 为什么要特别讲 Functional Programming 的通用函式呢? 实际上,RxJS 核心的 Observable 操作观念跟 FP 的阵列操作是极为相近的,只学会以下几个基本的方法跟观念后,会让我们之后上手 Observable 简单很多!

今天的代码比较多,大家可以直接看影片!

ForEach

forEach 是 JavaScript 在 ES5 后,原生就有支持的方法。

原本我们可能要透过 for loop 取出阵列中的每一个元素

	var arr = ['Jerry', 'Anna'];
	for(var i = 0; i < arr.length; i++) {
		console.log(arr[i]);
	}

现在可以直接透过阵列的 forEach 取出每一个元素。

	var arr = ['Jerry', 'Anna'];
	arr.forEach(item => console.log(item));

forEach 是 FP 操作阵列的基本方法,我们可以用这个方法来实作下面三个我们今天要讲的重点分别为 map, filter, concatAll。

Map

试着把 newCourseList 每个元素的 { id, title } 塞到新的阵列 idAndTitlePairs

var newCourseList = [
		{
			id: 511021,
			title: 'React for Beginners',
			coverPng:
				'https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png',
			rating: 5,
		},
		{
			id: 511022,
			title: 'Vue2 for Beginners',
			coverPng:
				'https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png',
			rating: 5,
		},
		{
			id: 511023,
			title: 'Angular2 for Beginners',
			coverPng:
				'https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png',
			rating: 5,
		},
		{
			id: 511024,
			title: 'Webpack for Beginners',
			coverPng:
				'https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png',
			rating: 4,
		},
	],
	idAndTitle = []
newCourseList.forEach((course) => {
	idAndTitle.push({ id: course.id, title: course.title })
})

虽然我们成功的把 newCourseList 转成 idAndTitlePairs,但这样的写法还是显得有点太复杂了,我们可以用更抽象化的方式来完成。

上面我们练习到 newCourseList 转换成一个新的阵列 idAndTitlePairs,这个转换的过程其实就是两件事

  • 遍历 newCourseList 所有的元素
  • 把每个元素的预期值给到新的阵列

把这个过程抽象化成一个方法 map,以下是简化的基本思路:

  1. 我们会让每个阵列都有一个 map 方法

  2. 这个方法会让用户自定义传入一个 callback function

  3. 这个 callback function 会回传用户预期的元素

虽然 ES5 之后原生的 JavaScript 阵列有 map 方法了,但希望读者自我实做一次,能帮助理解。

	// 我们希望每一个阵列都有一个 map 这个方法,所以我们在 Array.prototype 扩充 map function
	Array.prototype.map = function(callback) {
	  var result = []; // map 最后一定会返回一个新阵列,所以我们先宣告一个新阵列

	  this.forEach(function(element, index) {
		  // this 就是呼叫 map 的阵列
		  result.push(callback(element, index));
		  // 执行使用者定义的 callback, callback 会回复使用者预期的元素,所以我们把它 push 这新阵列
	  })

	  return result;
	}

这里用到了 JavaScript 的 prototype chain 以及 this 等观念,可以看此影片了解 TODO!

到这里我们就实作完成 map 的方法了,让我们来试试这个方法吧!

	var idAndTitle = newCourseList
		.map((course) => {
			return { id: course.id, title: course.title };
		});

Filter

如果我们希望过滤一个阵列,留下阵列中我们想要的元素,并产生一个新的阵列,需要怎么做呢?让我们先用 foreach 来完成!
让我们过滤出 rating 值时 5 的元素

var ratingIsFive = [];
newCourseList.forEach((course) => {
	if(course.rating === 5){
		ratingIsFive.push(course);
	}
})

同样的我们试着简化这个过程,首先在这个转化的过程中,我们做了两件式:

  1. 遍历 newCourseList 中的所有元素
  2. 判断元素是否符合条件,符合则添加到新的阵列中
Array.prototype.filter = function(callback){
	var result = [];
	this.forEach((item,index) => {
		if(callback(item,index)) result.push(item);
	})
}

试试这个方法

var ratingIsFive = newCourseList
		.filter(course => course.rating === 5)
		.map(course => course.title)

ConcatAll

有时候我们会遇到组出一个二维阵列,但我们希望阵列是一维的,问题如下:
假如我们要取出 courseLists 中所有 rating 为 5 的课程,这时可能就会用到两个 forEach

var user = {
	id: 888,
	name: 'JerryHong',
	courseLists: [
		{
			name: 'My Courses',
			courses: [
				{
					id: 511019,
					title: 'React for Beginners',
					coverPng:
						'https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png',
					tags: [{ id: 1, name: 'JavaScript' }],
					rating: 5,
				},
				{
					id: 511020,
					title: 'Front-End automat workflow',
					coverPng:
						'https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png',
					tags: [
						{ id: 2, name: 'gulp' },
						{ id: 3, name: 'webpack' },
					],
					rating: 4,
				},
			],
		},
		{
			name: 'New Release',
			courses: [
				{
					id: 511022,
					title: 'Vue2 for Beginners',
					coverPng:
						'https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png',
					tags: [{ id: 1, name: 'JavaScript' }],
					rating: 5,
				},
				{
					id: 511023,
					title: 'Angular2 for Beginners',
					coverPng:
						'https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png',
					tags: [{ id: 1, name: 'JavaScript' }],
					rating: 4,
				},
			],
		},
	],
}
var allCourseIds = []
user.courseLists.forEach((list) => {
	list.courses
		.filter((item) => item.rating === 5)
		.forEach((item) => {
			allCourseIds.push(item)
		})
})

可以看到上面的代码,我们用了较为低阶的操作来解决这个问题,我们刚刚已经试着用抽象化的方式实作了 map 跟 filter,那我们同样也能够定义一个方法用来摊平二维阵列。
让我们来加入一个 concatAll 方法来简化这段代码吧! concatAll 要做的事情很简单,就是把一个二维阵列转成一维。

Array.prototype.concatAll = function () {
	var result = []
	// 用 apply 完成
	this.forEach((array) => {
		result.push.apply(result, array)
	})
	// 用两个 forEach 完成
	// this.forEach((array) => {
	//   array.forEach(item => {
	//     result.push(item)
	//   })
	// });

	// 用 ES6 spread 完成
	// this.forEach((array) => {
	//   result.push(...array);
	// })

	return result
}

同样的我们用前面定要好的 courseLists 来试试 concatAll 吧!

var allCourseIds = user.courseLists
	.map((list) => {
		return list.courses.filter((course) => course.rating === 5)
	})
	.concatAll()

这边出一个比较难的题目,大家可以想想看要怎么解

var courseLists = [
	{
		name: 'My Courses',
		courses: [
			{
				id: 511019,
				title: 'React for Beginners',
				covers: [
					{
						width: 150,
						height: 200,
						url: 'http://placeimg.com/150/200/tech',
					},
					{
						width: 200,
						height: 200,
						url: 'http://placeimg.com/200/200/tech',
					},
					{
						width: 300,
						height: 200,
						url: 'http://placeimg.com/300/200/tech',
					},
				],
				tags: [
					{
						id: 1,
						name: 'JavaScript',
					},
				],
				rating: 5,
			},
			{
				id: 511020,
				title: 'Front-End automat workflow',
				covers: [
					{
						width: 150,
						height: 200,
						url: 'http://placeimg.com/150/200/arch',
					},
					{
						width: 200,
						height: 200,
						url: 'http://placeimg.com/200/200/arch',
					},
					{
						width: 300,
						height: 200,
						url: 'http://placeimg.com/300/200/arch',
					},
				],
				tags: [
					{
						id: 2,
						name: 'gulp',
					},
					{
						id: 3,
						name: 'webpack',
					},
				],
				rating: 5,
			},
		],
	},
	{
		name: 'New Release',
		courses: [
			{
				id: 511022,
				title: 'Vue2 for Beginners',
				covers: [
					{
						width: 150,
						height: 200,
						url: 'http://placeimg.com/150/200/nature',
					},
					{
						width: 200,
						height: 200,
						url: 'http://placeimg.com/200/200/nature',
					},
					{
						width: 300,
						height: 200,
						url: 'http://placeimg.com/300/200/nature',
					},
				],
				tags: [
					{
						id: 1,
						name: 'JavaScript',
					},
				],
				rating: 5,
			},
			{
				id: 511023,
				title: 'Angular2 for Beginners',
				covers: [
					{
						width: 150,
						height: 200,
						url: 'http://placeimg.com/150/200/people',
					},
					{
						width: 200,
						height: 200,
						url: 'http://placeimg.com/200/200/people',
					},
					{
						width: 300,
						height: 200,
						url: 'http://placeimg.com/300/200/people',
					},
				],
				tags: [
					{
						id: 1,
						name: 'JavaScript',
					},
				],
				rating: 5,
			},
		],
	},
]
/*
  var result = courseList
  不得直接使用索引 covers[0],请用 concatAll, map, filter, forEach 完成
  result 结果为 [
      {
        id: 511019,
        title: "React for Beginners",
        cover: "http://placeimg.com/150/200/tech"
      }, {
        id: 511020,
        title: "Front-End automat workflow",
        cover: "http://placeimg.com/150/200/arch"
      }, {
        id: 511022,
        title: "Vue2 for Beginners",
        cover: "http://placeimg.com/150/200/nature"
      }, {
        id: 511023,
        title: "Angular2 for Beginners",
        cover: "http://placeimg.com/150/200/people"
      },
   ]
*/

本系列仅作为学习记录所用,摘录自30天精通Rxjs!强烈推荐!膜拜大佬!

posted @   楚小九  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示