ReactiveX combines the
Observer pattern
with theIterator pattern
andfunctional programming
with collections to fill the need for an ideal way of managing sequences of events. ReactiveX将观察者模式、迭代器模式和函数编程与集合结合起来,以知足管理事件序列的理想方式的须要。html
根据官方定义,RxJS 是基于观察者模式和迭代器模式以函数式编程思惟来实现的,那么咱们先了解一下这几个概念。express
Functional Programming
是一种编程范式(programming paradigm
),就像Object-oriented Programming(OOP)
同样,就是一种写程式的方法论,这些方法论告诉咱们如何思考及解决问题。编程
函数式编程关心数据的映射,命令式编程关心解决问题的步骤.bash
这里的映射就是数学上函数的概念——一种东西和另外一种东西之间的对应关系, 简单说Functional Programming
核心思想就是作运算处理,并用function 来思考问题.markdown
所谓一等公民是指跟其它对象具备同等的地位,也就是说函数可以被赋值给变量,也可以被看成参数传入另外一个函数,也可看成一个函数的返回值。数据结构
// 函数可以被赋值给变量 var hello = function() {} // 函数看成参数传入另外一个函数 fetch('www.google.com') .then(function(response) {}) // 匿名 function 被傳入 then() // 看成一个函数的返回值 var a = function(a) { return function(b) { return a + b; }; } 复制代码
Functional Programming
都是表达式(Expression
)不会是语句(Statement)。 基本区分表达式与语句:并发
function
, 声明一个变量。有时候表达式也可能同时是合法的语句,这里只讲基本的判断方法。若是想更深刻了解其中的差别,能够看这篇文章Expressions versus statements in JavaScript dom
纯函数是这样一种函数,即相同的输入,永远会获得相同的输出,并且没有任何可观察的反作用(side effect
)ide
举个简单的例子,slice
和splice
:函数式编程
var arr = [1, 2, 3, 4, 5];
arr.slice(0, 3); // [1, 2, 3]
arr.slice(0, 3); // [1, 2, 3]
arr.slice(0, 3); // [1, 2, 3]
复制代码
这里能够看到slice
无论执行几回,返回值都是相同的,而且除了返回一个值以外并无作任何事,因此slice
就是一个pure function
。
var arr = [1, 2, 3, 4, 5];
arr.splice(0, 3); // [1, 2, 3]
arr.splice(0, 3); // [4, 5]
arr.slice(0, 3); // []
复制代码
这里咱们换成用splice
,由于splice
每执行一次就会影响arr
的值,致使每次结果都不一样,这就很明显不是一个pure function
。
什么是反作用(side effect
)
反作用指一个function
作了跟自己运算返回值没有关系的事,好比说修改某个全域变数,或是修改传入参数的值,甚至是执行console.log
都算是反作用。
Functional Programming
强调没有反作用,也就是function
要保持纯粹,只作运算并返回一个值,没有其余额外的行为。
这里列举几个常见的反作用:
归纳来说,只要是跟函数外部环境发生的交互就都是反作用——这一点可能会让你怀疑无反作用编程的可行性。函数式编程的哲学就是假定反作用是形成不正当行为的主要缘由, 这并非说,要禁止使用一切反作用,而是说,要让它们在可控的范围内发生。
Pure function
等特性,执行结果不依赖外部状态,且不会对外部环境有任何操做,这使得单元测试和调试都更容易观察者模式,即发布-订阅模式,它定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象。这样一来,当被观察者状态发生改变时,须要通知相应的观察者,使这些观察者对象可以自动更新。
主题是观察者观察的对象,一个主题必须具有下面三个特征。
当主题发生变化,收到通知后进行具体的处理
这里举一个例子来讲明,牛奶送奶站就是主题,订奶客户为监听者,客户从送奶站订阅牛奶后,会天天收到牛奶。若是客户不想订阅了,能够取消,之后就不会收到牛奶。
根据上面的说明,咱们能够简单实现一个被观察者:
class Subject { constructor() { this.observerCollection = []; } registerObserver(observer){ this.observerCollection.push(observer) } unRegisterObserver(observer){ this.observerCollection.splice(this.observer.findIndex(observer), 1) } notifyObservers(message){ this.observerCollection.forEach(observer => { observer.notify(message); }) } } 复制代码
迭代器模式(Iterator)提供了一种方法顺序访问一个集合对象中各个元素,而又不暴露该对象的内部表示,迭代器模式能够把迭代的过程从业务逻辑中分离出来,在使用迭代器模式以后,即便不关心对象的内部构造,也能够按顺序访问其中的每一个元素。
Iterator
的遍历过程是这样的:
建立一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
第一次调用指针对象的next
方法,能够将指针指向数据结构的第一个成员。
第二次调用指针对象的next
方法,指针就指向数据结构的第二个成员。
不断调用指针对象的next
方法,直到它指向数据结构的结束位置。
可参考ES6系列--7. 可迭代协议和迭代器协议中关于迭代器的介绍。
JavaScript
中像 Array、Set、Map
等都属于内置的可迭代类型, 能够经过 iterator
方法来获取一个迭代对象,调用迭代对象的 next
方法将获取一个元素对象,以下示例:
var arr = [1, 2, 3]; var iterator = arr[Symbol.iterator](); iterator.next(); // { value: 1, done: false } iterator.next(); // { value: 2, done: false } iterator.next(); // { value: 3, done: false } iterator.next(); // { value: undefined, done: true } 复制代码
遍历迭代器可使用下面的方法。
while(true) { let result; try { result = iterator.next(); } catch (error) { handleError(error); // 错误处理 } if (result.done) { handleCompleted(); // 已完成以后的处理 } doSomething(result.value); } 复制代码
上面的代码主要对应三种状况:
next
方法能够将元素一个个的返回,这样就支持了返回屡次值。done
为true
。next
方法执行时报错,则会抛出 error
事件,因此能够用 try catch
包裹 next
方法处理可能出现的错误。下一篇开始介绍Observable 和 observer。