描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787111596646
产品特色
编辑推荐
软件开发中有什么老问题?技术发展迅速,用户的需求增加更快,软件的代码库也会随需求增长快速膨胀。
在这种情况下,如何保证代码质量?如何控制代码的复杂度?如何维持代码的可维护性?这些就成了软件开发的大问题。业界的同仁们为了解决这些老问题做了各种努力,函数式编程和响应式编程就是在实践中被证明行之有效的两种方法。
RxJS兼具函数式和响应式两种编程方式的特点,RxJS擅长处理异步操作,因为它对数据采用“推”的处理方式,当一个数据产生的时候,被推送给对应的处理函数,这个处理函数不用关心数据是同步产生的还是异步产生的,这样就把开发者从命令式异步处理的枷锁中解放了出来。
本书由浅入深地讲解RxJS,不仅介绍所有操作符的功能,而且讲解实际应用与利弊,是提升开发内功的好教程。
在这种情况下,如何保证代码质量?如何控制代码的复杂度?如何维持代码的可维护性?这些就成了软件开发的大问题。业界的同仁们为了解决这些老问题做了各种努力,函数式编程和响应式编程就是在实践中被证明行之有效的两种方法。
RxJS兼具函数式和响应式两种编程方式的特点,RxJS擅长处理异步操作,因为它对数据采用“推”的处理方式,当一个数据产生的时候,被推送给对应的处理函数,这个处理函数不用关心数据是同步产生的还是异步产生的,这样就把开发者从命令式异步处理的枷锁中解放了出来。
本书由浅入深地讲解RxJS,不仅介绍所有操作符的功能,而且讲解实际应用与利弊,是提升开发内功的好教程。
内容简介
随着互联网行业的飞速发展,Web开发者面临的挑战也越来越大,伴随着功能的增多,Web应用的复杂度也快速膨胀。对于Web应用,*复杂的问题就是异步操作的处理, 无论用户操作、AJAX请求、动画、WebSocket推送都涉及到异步操作,传统的异步处理方法越来越不适应复杂应用的需要,RxJS的产生,就是为了解决日益复杂的前端异步处理问题。RxJS是一门进入门槛比较高的技术,本书的目的就是降低学习成本,由浅入深地介绍RxJS,逐步解析这门技术的各个方面,让读者能够以一个平缓的学习曲线来掌握这一潜力无限的技术。
主要内容包括:·函数响应式编程·数据流的工作原理·RxJS操作符的创建方法·RxJS所有操作符的详细分类介绍·多播的应用·Scheduler的应用·RxJS的调试和测试方法·React、Redux和RxJS的组合使用方法·RxJS开发游戏的实践
主要内容包括:·函数响应式编程·数据流的工作原理·RxJS操作符的创建方法·RxJS所有操作符的详细分类介绍·多播的应用·Scheduler的应用·RxJS的调试和测试方法·React、Redux和RxJS的组合使用方法·RxJS开发游戏的实践
本书系统讲解RxJS响应式编程的技术原理与应用。第1章剖析函数响应式编程的基本概念,通过简单RxJS代码引入函数响应式编程,并与传统编程方式对比,解释这种编程范式的优势,以及这种范式形成的历史。第2章介绍学习RxJS必须掌握的基本概念,包括数据流、操作符和观察者模式。第3~9章介绍RxJS的各种操作符,以及如何选择恰当的操作符来完成不同的任务。第10章介绍RxJS如何实现多播的方式。第11章介绍实现调度Scheduler的作用、原理与使用。第12章介绍如何调试和测试RxJS相关代码,写出高可测试性代码。第13章介绍如何在React应用中使用RxJS,提高代码质量。第14章介绍Redux与RxJS的组合应用,发挥两者的共同优势。第15章介绍一个综合案例,用RxJS实现网页游戏Breakout,并剖析RxJS如何实现动画和绘图。
目 录
目录 Contents
前言
第1章 函数响应式编程1
1.1 一个简单的RxJS例子1
1.2 函数式编程5
1.2.1 什么是函数式编程5
1.2.2 为什么函数式编程近才崛起11
1.2.3 函数式编程和面向对象编程的比较13
1.3 响应式编程14
1.4 Reactive Extension15
1.5 RxJS是否是函数响应式编程16
1.6 函数响应式编程的优势17
1.7 本章小结18
第2章 RxJS入门19
2.1 RxJS的版本和运行环境19
2.2 Observable和Observer24
2.2.1 观察者模式24
2.2.2 迭代器模式25
2.2.3 创造Observable26
2.2.4 跨越时间的Observable28
2.2.5 永无止境的Observable29
2.2.6 Observable的完结30
2.2.7 Observable的出错处理31
2.2.8 Observer的简单形式32
2.3 退订Observable33
2.4 Hot Observable和Cold Observable35
2.5 操作符简介37
2.6 弹珠图39
2.7 本章小结41
第3章 操作符基础42
3.1 为什么要有操作符42
3.2 操作符的分类44
3.2.1 功能分类45
3.2.2 静态和实例分类46
3.3 如何实现操作符49
3.3.1 操作符函数的实现49
3.3.2 关联Observable53
3.3.3 改进的操作符定义55
3.3.4 lettable/pipeable 操作符60
3.4 本章小结68
第4章 创建数据流69
4.1 创建类操作符70
4.2 创建同步数据流70
4.2.1 create:毫无神奇之处71
4.2.2 of:列举数据71
4.2.3 range:指定范围73
4.2.4 generate:循环创建74
4.2.5 repeat:重复数据的数据流75
4.2.6 三个极简的操作符:empty、never和throw78
4.3 创建异步数据的Observable对象80
4.3.1 interval和timer:定时产生数据80
4.3.2 from:可把一切转化为Observable82
4.3.3 fromPromise:异步处理的交接84
4.3.4 fromEvent85
4.3.5 fromEventPattern87
4.3.6 ajax88
4.3.7 repeatWhen89
4.3.8 defer91
4.4 本章小结92
第5章 合并数据流93
5.1 合并类操作符94
5.1.1 concat:首尾相连94
5.1.2 merge:先到先得快速通过96
5.1.3 zip:拉链式组合99
5.1.4 combineLatest:合并后一个数据102
5.1.5 withLatestFrom109
5.1.6 解决glitch112
5.1.7 race:胜者通吃115
5.1.8 startWith115
5.1.9 forkJoin117
5.2 高阶Observable118
5.2.1 高阶Observable的意义119
5.2.2 操作高阶Observable的合并类操作符120
5.2.3 进化的高阶Observable处理124
5.3 本章小结128
第6章 辅助类操作符129
6.1 数学类操作符129
6.1.1 count:统计数据个数130
6.1.2 max和min:小值130
6.1.3 reduce:规约统计131
6.2 条件布尔类操作符133
6.2.1 every134
6.2.2 find和findIndex135
6.2.3 isEmpty137
6.2.4 defaultIfEmpty138
6.3 本章小结138
第7章 过滤数据流139
7.1 过滤类操作符的模式140
7.1.1 filter141
7.1.2 first142
7.1.3 last144
7.1.4 take一族操作符145
7.1.5 计时的点击计数网页程序150
7.1.6 skip151
7.1.7 skipWhile和skipUntil151
7.2 回压控制152
7.2.1 throttle和debounce154
7.2.2 auditTime和audit164
7.2.3 sampleTime和sample166
7.2.4 根据数据序列做回压控制168
7.3 其他过滤方式171
7.3.1 ignoreElements172
7.3.2 elementAt172
7.3.3 single173
7.4 本章小结173
第8章 转化数据流174
8.1 转化类操作符174
8.2 映射数据175
8.2.1 map176
8.2.2 mapTo177
8.2.3 pluck178
8.3 缓存窗口:无损回压控制179
8.3.1 windowTime和bufferTime180
8.3.2 windowCount和bufferCount183
8.3.3 windowWhen和bufferWhen184
8.3.4 windowToggle和buffer-Toggle185
8.3.5 window和buffer186
8.4 高阶的map188
8.4.1 concatMap189
8.4.2 mergeMap192
8.4.3 switchMap193
8.4.4 exhaustMap195
8.4.5 高阶的MapTo195
8.4.6 expand196
8.5 数据分组196
8.6 累计数据200
8.6.1 scan200
8.6.2 mergeScan201
8.7 本章小结203
第9章 异常错误处理204
9.1 异常处理不可避免204
9.2 异常处理的难点206
9.2.1 try/catch只支持同步运算207
9.2.2 回调函数的局限207
9.2.3 Promise的异常处理209
9.3 RxJS的异常处理212
9.3.1 catch214
9.3.2 retry216
9.3.3 retryWhen217
9.3.4 finally220
9.4 重试的本质221
9.5 本章小结223
第10章 多播225
10.1 数据流的多播225
10.2 Hot和Cold数据流差异228
10.3 Subject230
10.3.1 两面神Subject230
10.3.2 用Subject实现多播233
10.3.3 makeHot 操作符234
10.3.4 Subject不能重复使用235
10.3.5 Subject可以有多个上游237
10.3.6 Subject的错误处理239
10.4 支持多播的操作符241
10.4.1 multicast241
10.4.2 publish253
10.4.3 share255
10.5 高级多播功能257
10.5.1 publishLast和Async-Subject258
10.5.2 pubishReplay和Replay-Subject259
10.5.3 publishBehavior和BehaviorSubject262
10.6 本章小结263
第11章 掌握时间的Scheduler265
11.1 Schedu
前言
第1章 函数响应式编程1
1.1 一个简单的RxJS例子1
1.2 函数式编程5
1.2.1 什么是函数式编程5
1.2.2 为什么函数式编程近才崛起11
1.2.3 函数式编程和面向对象编程的比较13
1.3 响应式编程14
1.4 Reactive Extension15
1.5 RxJS是否是函数响应式编程16
1.6 函数响应式编程的优势17
1.7 本章小结18
第2章 RxJS入门19
2.1 RxJS的版本和运行环境19
2.2 Observable和Observer24
2.2.1 观察者模式24
2.2.2 迭代器模式25
2.2.3 创造Observable26
2.2.4 跨越时间的Observable28
2.2.5 永无止境的Observable29
2.2.6 Observable的完结30
2.2.7 Observable的出错处理31
2.2.8 Observer的简单形式32
2.3 退订Observable33
2.4 Hot Observable和Cold Observable35
2.5 操作符简介37
2.6 弹珠图39
2.7 本章小结41
第3章 操作符基础42
3.1 为什么要有操作符42
3.2 操作符的分类44
3.2.1 功能分类45
3.2.2 静态和实例分类46
3.3 如何实现操作符49
3.3.1 操作符函数的实现49
3.3.2 关联Observable53
3.3.3 改进的操作符定义55
3.3.4 lettable/pipeable 操作符60
3.4 本章小结68
第4章 创建数据流69
4.1 创建类操作符70
4.2 创建同步数据流70
4.2.1 create:毫无神奇之处71
4.2.2 of:列举数据71
4.2.3 range:指定范围73
4.2.4 generate:循环创建74
4.2.5 repeat:重复数据的数据流75
4.2.6 三个极简的操作符:empty、never和throw78
4.3 创建异步数据的Observable对象80
4.3.1 interval和timer:定时产生数据80
4.3.2 from:可把一切转化为Observable82
4.3.3 fromPromise:异步处理的交接84
4.3.4 fromEvent85
4.3.5 fromEventPattern87
4.3.6 ajax88
4.3.7 repeatWhen89
4.3.8 defer91
4.4 本章小结92
第5章 合并数据流93
5.1 合并类操作符94
5.1.1 concat:首尾相连94
5.1.2 merge:先到先得快速通过96
5.1.3 zip:拉链式组合99
5.1.4 combineLatest:合并后一个数据102
5.1.5 withLatestFrom109
5.1.6 解决glitch112
5.1.7 race:胜者通吃115
5.1.8 startWith115
5.1.9 forkJoin117
5.2 高阶Observable118
5.2.1 高阶Observable的意义119
5.2.2 操作高阶Observable的合并类操作符120
5.2.3 进化的高阶Observable处理124
5.3 本章小结128
第6章 辅助类操作符129
6.1 数学类操作符129
6.1.1 count:统计数据个数130
6.1.2 max和min:小值130
6.1.3 reduce:规约统计131
6.2 条件布尔类操作符133
6.2.1 every134
6.2.2 find和findIndex135
6.2.3 isEmpty137
6.2.4 defaultIfEmpty138
6.3 本章小结138
第7章 过滤数据流139
7.1 过滤类操作符的模式140
7.1.1 filter141
7.1.2 first142
7.1.3 last144
7.1.4 take一族操作符145
7.1.5 计时的点击计数网页程序150
7.1.6 skip151
7.1.7 skipWhile和skipUntil151
7.2 回压控制152
7.2.1 throttle和debounce154
7.2.2 auditTime和audit164
7.2.3 sampleTime和sample166
7.2.4 根据数据序列做回压控制168
7.3 其他过滤方式171
7.3.1 ignoreElements172
7.3.2 elementAt172
7.3.3 single173
7.4 本章小结173
第8章 转化数据流174
8.1 转化类操作符174
8.2 映射数据175
8.2.1 map176
8.2.2 mapTo177
8.2.3 pluck178
8.3 缓存窗口:无损回压控制179
8.3.1 windowTime和bufferTime180
8.3.2 windowCount和bufferCount183
8.3.3 windowWhen和bufferWhen184
8.3.4 windowToggle和buffer-Toggle185
8.3.5 window和buffer186
8.4 高阶的map188
8.4.1 concatMap189
8.4.2 mergeMap192
8.4.3 switchMap193
8.4.4 exhaustMap195
8.4.5 高阶的MapTo195
8.4.6 expand196
8.5 数据分组196
8.6 累计数据200
8.6.1 scan200
8.6.2 mergeScan201
8.7 本章小结203
第9章 异常错误处理204
9.1 异常处理不可避免204
9.2 异常处理的难点206
9.2.1 try/catch只支持同步运算207
9.2.2 回调函数的局限207
9.2.3 Promise的异常处理209
9.3 RxJS的异常处理212
9.3.1 catch214
9.3.2 retry216
9.3.3 retryWhen217
9.3.4 finally220
9.4 重试的本质221
9.5 本章小结223
第10章 多播225
10.1 数据流的多播225
10.2 Hot和Cold数据流差异228
10.3 Subject230
10.3.1 两面神Subject230
10.3.2 用Subject实现多播233
10.3.3 makeHot 操作符234
10.3.4 Subject不能重复使用235
10.3.5 Subject可以有多个上游237
10.3.6 Subject的错误处理239
10.4 支持多播的操作符241
10.4.1 multicast241
10.4.2 publish253
10.4.3 share255
10.5 高级多播功能257
10.5.1 publishLast和Async-Subject258
10.5.2 pubishReplay和Replay-Subject259
10.5.3 publishBehavior和BehaviorSubject262
10.6 本章小结263
第11章 掌握时间的Scheduler265
11.1 Schedu
前 言
Preface 前 言这是一个信息技术爆炸的时代,计算机编程语言和框架层出不穷,同时,编程的风格也在发生变化。也许你还没有注意到,但是变化的确在发生。曾经面向对象式编程方法一统天下,如今越来越多开发者开始转向函数式编程方法;与此同时,一直具有统治地位的指令式编程方法,也发现自己要面对一个新的对手:响应式编程。在这本书里,我们介绍的就是兼具函数式和响应式两种先进编程风格的框架RxJS。
RxJS是Reactive Extension这种模式的JavaScript语言实现,通过学习了解RxJS,你将打开一扇通往全新编程风格的大门。
当然,我们学习RxJS,并不是因为RxJS是一项炫酷的技术,也不是因为RxJS是一个的技术。在技术的道路上,如果只是追逐“炫酷”和“”,肯定是要吃苦头的,因为这是舍本逐末。
我们学习和应用RxJS,是因为RxJS的的确确能够帮助我们解决问题,而且这些问题长期以来一直在困扰我们,没有好的解决办法,这些问题包括:
如何控制大量代码的复杂度;如何保持代码可读;如何处理异步操作。
RxJS的价值在于提供了一种不一样的编程方式,能够解决很多困扰我们开发者的问题。
打开了这本书的读者,你们想必也曾经面对过软件开发过程中的这些挑战,学习RxJS能够帮助大家在“军火库”中增加一种有力武器,也许你不用随时随地使用这种武器,但是,你肯定多了一种解决这些问题的更有效方法。
不过,可能你也早有耳闻,RxJS的学习曲线非常陡峭,可以说已经陡峭到了不能称为学习曲线的程度,应该称为“学习悬崖”。这并不夸张,我个人学习RxJS就尝试了三次。
次学习RxJS时,感觉这种思想很酷,但是很快就发现太多概念都是交叉出现的,文档中为了解释一个概念,就会引入一个新的概念,当我去了解这个新的概念的时候,发现为了解释这个新的概念又需要理解其他的概念,整个RxJS的知识图就像是一个迷宫,我次学习RxJS的经历就终结在这个迷宫之中。
几个月后,我第二次鼓起勇气来学习RxJS,因为有了次的一些基础,这一次还比较顺利,我把概念都掌握得差不多了,但是接下来面对的就是RxJS中大量的操作符,RxJS的应用几乎就是在选择用哪种操作符合适。虽然我把RxJS的迷宫整个都摸了一遍,但是很多操作符我也没有发现实际的应用场景,所以这一次学习后依然不了了之。
后,终于有个机会,我需要用RxJS来解决实际的问题。这一次,因为存在实际应用的驱动,我不得不深入去理解RxJS的内在机制,揣摩一个操作符为什么要设计成这样而不是另一个样子,把自己摆在RxJS的角度来思考问题。我还是很幸运,这一次,终于对RxJS有了一个全面的认识。
我终于体会到RxJS的卓越之处,我很兴奋,希望这个工具能够被更多人了解,于是我向朋友们介绍RxJS,有的朋友的确花了时间去学习,但是,他们大多数后依然放弃了。
怎么会这样?简单来说,是因为RxJS的学习曲线太陡峭。
上图就是对RxJS学习曲线的形象描述,一般知识的学习曲线像是一个小山坡,而RxJS的学习曲线就像是一个悬崖,而且这个悬崖有的部分的倾斜角度超过了90度!
怎么会这样?这个问题我也思考了很久,回顾自己三次学习RxJS的过程,我发现了问题的所在,那就是,目前几乎没有一个像样的用线性方法教授RxJS的教材。
RxJS是开源软件,这个软件经过了很长时间的演进,代码的确可圈可点,但是其文档实在算不上优秀。RxJS的官方文档内容虽然不少,但是内容太多,很多部分之间相互引用,并没有一步一步告诉初学者该如何入门,初学者很容易(就像我次学习RxJS一样)发现自己陷入一个巨大的、没有头绪的迷宫之中。
于是我想,既然RxJS是一个好东西,那为什么不用一种简单易懂的方式来介绍这种技术呢?这也正是写作本书的动因。
在这本书里,我会尽量用一种线性的方法来介绍RxJS的各个方面,读者按照正常的、从前到后的顺序来阅读,不需要在各个知识点之间跳来跳去,这样,当读者看到后一页的时候,应该就能够对RxJS有全面深刻的认识了。
读者可能会想,RxJS这样一个复杂难懂的东西,我有必要去学吗?如果你只是满足于现状,那真的不用去学,不过,我前面也说过,当今世界的变化和发展非常快,函数式编程在目前也许还不起眼,但是在未来可能会占统治地位,这个变化可能发生在明年,也可能发生在明晚。你肯定不希望当变化发生的时候自己手足无措,所以,花一些时间来接触这个面向未来的思想,对你没有坏处。
坦白地说,我并不相信每个读者学习RxJS的经历都会一帆风顺,如果你真的能够一次就学透RxJS,那你真的很可能是一个天才,记得一定要给我留言;如果你在学习中遇到一些挫折,请相信,你并不孤单,这本书的作者就经历了三次学习才真正学会RxJS,任何疑问都可以留言和我交流。
让我们开始这段旅程吧!
本书的内容本书以线性方式来介绍RxJS,所以建议读者以顺序的方式来阅读本书,如果读者觉得对某一个方面已经十分了解,也可以跳过相关章节,不过,还是希望读者在时间允许的情况下阅读全部内容,你肯定会有新的体会。本书
RxJS是Reactive Extension这种模式的JavaScript语言实现,通过学习了解RxJS,你将打开一扇通往全新编程风格的大门。
当然,我们学习RxJS,并不是因为RxJS是一项炫酷的技术,也不是因为RxJS是一个的技术。在技术的道路上,如果只是追逐“炫酷”和“”,肯定是要吃苦头的,因为这是舍本逐末。
我们学习和应用RxJS,是因为RxJS的的确确能够帮助我们解决问题,而且这些问题长期以来一直在困扰我们,没有好的解决办法,这些问题包括:
如何控制大量代码的复杂度;如何保持代码可读;如何处理异步操作。
RxJS的价值在于提供了一种不一样的编程方式,能够解决很多困扰我们开发者的问题。
打开了这本书的读者,你们想必也曾经面对过软件开发过程中的这些挑战,学习RxJS能够帮助大家在“军火库”中增加一种有力武器,也许你不用随时随地使用这种武器,但是,你肯定多了一种解决这些问题的更有效方法。
不过,可能你也早有耳闻,RxJS的学习曲线非常陡峭,可以说已经陡峭到了不能称为学习曲线的程度,应该称为“学习悬崖”。这并不夸张,我个人学习RxJS就尝试了三次。
次学习RxJS时,感觉这种思想很酷,但是很快就发现太多概念都是交叉出现的,文档中为了解释一个概念,就会引入一个新的概念,当我去了解这个新的概念的时候,发现为了解释这个新的概念又需要理解其他的概念,整个RxJS的知识图就像是一个迷宫,我次学习RxJS的经历就终结在这个迷宫之中。
几个月后,我第二次鼓起勇气来学习RxJS,因为有了次的一些基础,这一次还比较顺利,我把概念都掌握得差不多了,但是接下来面对的就是RxJS中大量的操作符,RxJS的应用几乎就是在选择用哪种操作符合适。虽然我把RxJS的迷宫整个都摸了一遍,但是很多操作符我也没有发现实际的应用场景,所以这一次学习后依然不了了之。
后,终于有个机会,我需要用RxJS来解决实际的问题。这一次,因为存在实际应用的驱动,我不得不深入去理解RxJS的内在机制,揣摩一个操作符为什么要设计成这样而不是另一个样子,把自己摆在RxJS的角度来思考问题。我还是很幸运,这一次,终于对RxJS有了一个全面的认识。
我终于体会到RxJS的卓越之处,我很兴奋,希望这个工具能够被更多人了解,于是我向朋友们介绍RxJS,有的朋友的确花了时间去学习,但是,他们大多数后依然放弃了。
怎么会这样?简单来说,是因为RxJS的学习曲线太陡峭。
上图就是对RxJS学习曲线的形象描述,一般知识的学习曲线像是一个小山坡,而RxJS的学习曲线就像是一个悬崖,而且这个悬崖有的部分的倾斜角度超过了90度!
怎么会这样?这个问题我也思考了很久,回顾自己三次学习RxJS的过程,我发现了问题的所在,那就是,目前几乎没有一个像样的用线性方法教授RxJS的教材。
RxJS是开源软件,这个软件经过了很长时间的演进,代码的确可圈可点,但是其文档实在算不上优秀。RxJS的官方文档内容虽然不少,但是内容太多,很多部分之间相互引用,并没有一步一步告诉初学者该如何入门,初学者很容易(就像我次学习RxJS一样)发现自己陷入一个巨大的、没有头绪的迷宫之中。
于是我想,既然RxJS是一个好东西,那为什么不用一种简单易懂的方式来介绍这种技术呢?这也正是写作本书的动因。
在这本书里,我会尽量用一种线性的方法来介绍RxJS的各个方面,读者按照正常的、从前到后的顺序来阅读,不需要在各个知识点之间跳来跳去,这样,当读者看到后一页的时候,应该就能够对RxJS有全面深刻的认识了。
读者可能会想,RxJS这样一个复杂难懂的东西,我有必要去学吗?如果你只是满足于现状,那真的不用去学,不过,我前面也说过,当今世界的变化和发展非常快,函数式编程在目前也许还不起眼,但是在未来可能会占统治地位,这个变化可能发生在明年,也可能发生在明晚。你肯定不希望当变化发生的时候自己手足无措,所以,花一些时间来接触这个面向未来的思想,对你没有坏处。
坦白地说,我并不相信每个读者学习RxJS的经历都会一帆风顺,如果你真的能够一次就学透RxJS,那你真的很可能是一个天才,记得一定要给我留言;如果你在学习中遇到一些挫折,请相信,你并不孤单,这本书的作者就经历了三次学习才真正学会RxJS,任何疑问都可以留言和我交流。
让我们开始这段旅程吧!
本书的内容本书以线性方式来介绍RxJS,所以建议读者以顺序的方式来阅读本书,如果读者觉得对某一个方面已经十分了解,也可以跳过相关章节,不过,还是希望读者在时间允许的情况下阅读全部内容,你肯定会有新的体会。本书
评论
还没有评论。