博文原地址 https://github.com/daihere1993/ThoughtRam/issues/7 , 期待你的 Star .
一, BaconJS 是什么
BaconJS 是一个函数响应式编程 (Functional Reactive Programming) 的 JS 库. 虽说是函数响应式编程, 但我用了会儿, 感觉响应式编程更令人印象深刻, 它与业界比较火的响应式编程库 RxJS 有点类似. 值得一提的是, Angular 内置的许多 API 就使用了 RxJS . 可见, 响应式编程可能会是未来一个很重要的发展方向.
二, 如何理解 FRP
函数响应式编程(Function Reactive Programmin), 简称 FRP . 这是 BaconJS 最显著的特点, 要想入门 BaconJS , 只需理解这种编程思维即可, 不必被官网提供的海量 API 所困扰.
FRP = FP(Function Programming) + RP(Reactive Programming)
顾名思义, 函数响应式编程 = 函数式编程(Functional Programming) + 响应式编程(Reactive Programming)
这两种编程模式是两个很大的话题, 都可以聊很久. 我在这里就大致的罗列下两者的一些特点, 并简述下我个人的理解.
2.1 函数式编程(FP)
函数式编程是业界很火的一个概念, 我曾不止一次试图去彻底的弄懂它, 但至今也是一知半解的状态~
不管怎么样, 以下几个 FP 的特点还是需要了解的:
纯函数(Pure function), 函数唯一的输入只会有唯一的输出.
不可修改状态(Immutable state).
我们常常把 FP 和命令式编程 (Imperative programming) 进行比较, 我个人认为 FP 更加强调代码的可预测性(Predictable), 尽可能的避免代码的副作用(Side effect), 更加关心数据的映射, 而命令式编程关心的是解决问题的步骤.
2.2 响应式编程(RP)
随着 Google 前端框架 Angular(Angular 1.x 称为 AngularJS,Angular v2+ 称为 Angular) 的发布, 由于 Angular 几乎所有的异步接口都使用了响应式编程库 RxJS , 响应式编程得到了越来越多的关注.
我算是半个 NG 粉, 有用 Angular 开发过一个项目, 用下来感觉 RxJS 的开发体验特别好. 总的来说, RP 有如下几个特点:
流 (Stream) 的概念, 衍生的概念有: 数据流(Data stream), 事件流(Event stream) .
链式调用(Chain invoke), 使代码看起来清晰, 简洁.
处理异步数据流的神器.
响应式编程主要是用来处理异步数据流 (Asynchronous data stream), 可以是 ajax 请求产生的数据流, 也可以是 DOM 事件(或者其他时间) 产生的事件流.
对于其核心的概念: 流 (Stream), 我的理解是: 流是按照时间顺序排列的一系列正在进行的事件. 在流这个概念的基础上, RP 框架提供了一系列(海量) 的接口来处理流, 使得我们开发出来的代码显得非常清晰.
- function isPhoneAvailable(n) {
- return /^[1][3,4,5,7,8][0-9]{9}$/.test(n);
- }
- function isVCAvailable(c) {
- return c.length === 4;
- }
- // 1. 生成手机号码输入框对应的'keyup' 事件流.
- // 2. 通过 map 方法校验手机号码的有效性.
- // 3. 将事件流对象转化成 Property 对象, 方便后面使用'and' 方法.
- let phoneNumber$ = $('#phone_number')
- .asEventStream('keyup')
- .map((event) => {
- let value = $(event.target).val();
- return isPhoneAvailable(value);
- })
- .toProperty('');
- // 同上
- let verificationCode$ = $('#verification_code')
- .asEventStream('keyup')
- .map((event) => {
- let value = $(event.target).val();
- return isVCAvailable(value);
- })
- .toProperty('');
- // 通过'Property' 对象的'and' 方法, 生成一个新的'Property' 对象
- // Property.prototype.and 的含义: 等同于 return A&B;
- let registerButton$ = phoneNumber$.and(verificationCode$);
- // 对 registerButton$ 取反, 然后通过'assign' 方法设置注册按钮的 disabled 属性
- registerButton$.not().assign($('#register_button'), 'attr', 'disabled');
- const Bacon = require('baconjs').Bacon;
- const events = require('events');
- const eventEmitter = new events.EventEmitter();
- Bacon
- .fromEvent(eventEmitter, 'someEvent')
- .onValue((data) => { console.log(data) });
- eventEmitter.emit('someEvent', 'some data');
- // 输出 =>
- // 'some data'
- // 例子一: 可以给回调函数传参
- Bacon
- .fromArray([1, 2, 3])
- .map(function (a, b) {
- console.log(`${a}, ${b}`)
- return a*b;
- }, 3)
- .log();
- // 输出 =>
- // 3, 1
- // 3
- // 3, 2
- // 6
- // 3, 3
- // 9
- // <end>
- // 例子二: 可以用 '.foo' 获取对象的某个属性值
- Bacon
- .fromPoll(1000)
- .map(() => { return { foo: `foo${parseInt(Math.random()*100)}` } })
- .take(4)
- .map('.foo')
- .log();
- // 输出(foo 后面的数字是随机的) =>
- // foo25
- // foo35
- // foo98
- // foo76
- // <end>
- let eventStream1 = Bacon.fromArray([1, 2, 5]);
- let eventSream2 = Bacon.fromArray([3, 4]);
- eventStream1.merge(eventSream2).log();
- // 输出 =>
- // 1
- // 2
- // 3
- // 4
- // 5
- // <end>
来源: https://juejin.im/post/5b70e3ab5188256139280546