游戏饰品共享饰品 gprent

&img src=&/50/v2-abdcdef3a11d85b0669496_b.png& data-rawwidth=&1284& data-rawheight=&432& class=&origin_image zh-lightbox-thumb& width=&1284& data-original=&/50/v2-abdcdef3a11d85b0669496_r.png&&&p&汇集整理泛 IT 技术领域(云计算,大数据,运维,安全,开发语言,智能硬件,前端,后端等等)学习技能图谱,帮助程序员梳理知识框架结构,并尝试提供路径指导和精华资源,方便技术人学习成长。 后续技能图谱内容将会持续更新&/p&&p&&br&&/p&&p&&br&&/p&&img src=&/v2-5ec3c04591_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-5ec3c04591_r.png&&&p&&br&&/p&&p&H5技能图谱1.0.png&/p&&p&&br&&/p&&p&&br&&/p&&img src=&/v2-b06b3b404eb_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-b06b3b404eb_r.jpg&&&p&&br&&/p&&p&Hadoop技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-9fe72c2ef6a3a05fd190ea_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1828& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-9fe72c2ef6a3a05fd190ea_r.jpg&&&p&&br&&/p&&p&iOS技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-1b3de331cf54badc7c371ab1f2743919_b.jpg& data-caption=&& data-rawwidth=&1138& data-rawheight=&774& class=&origin_image zh-lightbox-thumb& width=&1138& data-original=&/v2-1b3de331cf54badc7c371ab1f2743919_r.jpg&&&p&&br&&/p&&p&Java List类图.jpg&/p&&p&&br&&/p&&img src=&/v2-0aa900ad9bbc26_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&579& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-0aa900ad9bbc26_r.jpg&&&p&&br&&/p&&p&Java Map类图.jpg&/p&&p&&br&&/p&&img src=&/v2-262fc93f182cb29b47625_b.jpg& data-caption=&& data-rawwidth=&1237& data-rawheight=&821& class=&origin_image zh-lightbox-thumb& width=&1237& data-original=&/v2-262fc93f182cb29b47625_r.jpg&&&p&&br&&/p&&p&Java Set类图.jpg&/p&&p&&br&&/p&&img src=&/v2-c023e46d2dff90cc017f7be_b.png& data-caption=&& data-rawwidth=&1219& data-rawheight=&3765& class=&origin_image zh-lightbox-thumb& width=&1219& data-original=&/v2-c023e46d2dff90cc017f7be_r.png&&&p&&br&&/p&&p&Java TCP IP.png&/p&&p&&br&&/p&&img src=&/v2-2b631a378b311b2cd3b4_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&612& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-2b631a378b311b2cd3b4_r.jpg&&&p&&br&&/p&&p&Java并发图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-7abfb60a4498dfd_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&461& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-7abfb60a4498dfd_r.jpg&&&p&&br&&/p&&p&Java集合类图.jpg&/p&&p&&br&&/p&&img src=&/v2-e1b6a50edddd6d25ae9a7b_b.jpg& data-caption=&& data-rawwidth=&747& data-rawheight=&1012& class=&origin_image zh-lightbox-thumb& width=&747& data-original=&/v2-e1b6a50edddd6d25ae9a7b_r.jpg&&&p&&br&&/p&&p&Java集合图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-c81860e6_b.jpg& data-caption=&& data-rawwidth=&1072& data-rawheight=&2219& class=&origin_image zh-lightbox-thumb& width=&1072& data-original=&/v2-c81860e6_r.jpg&&&p&&br&&/p&&p&Java架构师图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-ac5fc24be754da34d84b1b_b.jpg& data-caption=&& data-rawwidth=&1532& data-rawheight=&1410& class=&origin_image zh-lightbox-thumb& width=&1532& data-original=&/v2-ac5fc24be754da34d84b1b_r.jpg&&&p&&br&&/p&&p&java知识结构图.jpg&/p&&p&&br&&/p&&img src=&/v2-18dbf42be2effd2c9ddfc4_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1212& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-18dbf42be2effd2c9ddfc4_r.png&&&p&&br&&/p&&p&JVM垃圾回图谱.png&/p&&p&&br&&/p&&img src=&/v2-58dd92f987afffbb8f18cb7e_b.jpg& data-caption=&& data-rawwidth=&980& data-rawheight=&1736& class=&origin_image zh-lightbox-thumb& width=&980& data-original=&/v2-58dd92f987afffbb8f18cb7e_r.jpg&&&p&&br&&/p&&p&liveplay知识结构图.jpg&/p&&p&&br&&/p&&img src=&/v2-bea693a6ceabb4e2b65eac_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-bea693a6ceabb4e2b65eac_r.jpg&&&p&&br&&/p&&p&OpenResty技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-41f44c1d008f8e146f5c2_b.jpg& data-caption=&& data-rawwidth=&980& data-rawheight=&978& class=&origin_image zh-lightbox-thumb& width=&980& data-original=&/v2-41f44c1d008f8e146f5c2_r.jpg&&&p&&br&&/p&&p&php知识结构图.jpg&/p&&p&&br&&/p&&img src=&/v2-fd29effac0ff8b08fa3a19_b.jpg& data-caption=&& data-rawwidth=&704& data-rawheight=&296& class=&origin_image zh-lightbox-thumb& width=&704& data-original=&/v2-fd29effac0ff8b08fa3a19_r.jpg&&&p&&br&&/p&&p&阿里巴巴常用小框架.jpg&/p&&p&&br&&/p&&img src=&/v2-8b02b7f7c7c5e6270105eb_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-8b02b7f7c7c5e6270105eb_r.jpg&&&p&&br&&/p&&p&安全秘籍.jpg&/p&&p&&br&&/p&&img src=&/v2-65ba6c9e4dceb0df0c4385_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-65ba6c9e4dceb0df0c4385_r.jpg&&&p&&br&&/p&&p&大数据技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-d826cbe49faf678f1d9bf_b.jpg& data-caption=&& data-rawwidth=&600& data-rawheight=&935& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-d826cbe49faf678f1d9bf_r.jpg&&&p&&br&&/p&&p&后端开发.jpg&/p&&p&&br&&/p&&img src=&/v2-078d1d7b5cbd69b7863a42_b.jpg& data-caption=&& data-rawwidth=&1205& data-rawheight=&268& class=&origin_image zh-lightbox-thumb& width=&1205& data-original=&/v2-078d1d7b5cbd69b7863a42_r.jpg&&&p&&br&&/p&&p&互联网大流量的方法.jpg&/p&&p&&br&&/p&&img src=&/v2-7e0ea7b35_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-7e0ea7b35_r.png&&&p&&br&&/p&&p&技术管理CTO技能图.png&/p&&p&&br&&/p&&img src=&/v2-abc1cdc5e30f4d5bdbce410c1fc2994b_b.jpg& data-caption=&& data-rawwidth=&942& data-rawheight=&641& class=&origin_image zh-lightbox-thumb& width=&942& data-original=&/v2-abc1cdc5e30f4d5bdbce410c1fc2994b_r.jpg&&&p&&br&&/p&&p&架构方法论图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-1ea6be14c40b93bc869b516_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-1ea6be14c40b93bc869b516_r.png&&&p&&br&&/p&&p&架构师技能图谱1.0.png&/p&&p&&br&&/p&&img src=&/v2-faa1ce0e9b837_b.jpg& data-caption=&& data-rawwidth=&1224& data-rawheight=&2312& class=&origin_image zh-lightbox-thumb& width=&1224& data-original=&/v2-faa1ce0e9b837_r.jpg&&&p&&br&&/p&&p&架构师图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-03c22c0f98ceb4c26314bd_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-03c22c0f98ceb4c26314bd_r.jpg&&&p&&br&&/p&&p&开发语言宝典.jpg&/p&&p&&br&&/p&&img src=&/v2-29dca200dae7_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1827& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-29dca200dae7_r.jpg&&&p&&br&&/p&&p&前端技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-f30ec7e6_b.jpg& data-caption=&& data-rawwidth=&600& data-rawheight=&783& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-f30ec7e6_r.jpg&&&p&&br&&/p&&p&前端开发.jpg&/p&&p&&br&&/p&&img src=&/v2-b95ee7d8c9c4bb9cc422e9_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1826& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-b95ee7d8c9c4bb9cc422e9_r.jpg&&&p&&br&&/p&&p&嵌入式开发技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-bba18cd845ed9c2_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-bba18cd845ed9c2_r.jpg&&&p&&br&&/p&&p&容器技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-bbb7df50adc7c4eecb8779_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&715& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-bbb7df50adc7c4eecb8779_r.jpg&&&p&&br&&/p&&p&软件发布流程.jpeg&/p&&p&&br&&/p&&img src=&/v2-6e4bb5c9582_b.jpg& data-caption=&& data-rawwidth=&640& data-rawheight=&470& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/v2-6e4bb5c9582_r.jpg&&&p&&br&&/p&&p&软件工程.jpeg&/p&&p&&br&&/p&&img src=&/v2-3eb2f1dac75_b.jpg& data-caption=&& data-rawwidth=&978& data-rawheight=&590& class=&origin_image zh-lightbox-thumb& width=&978& data-original=&/v2-3eb2f1dac75_r.jpg&&&p&&br&&/p&&p&设计模式秘籍图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-74c9fe2bc8b4_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-74c9fe2bc8b4_r.png&&&p&&br&&/p&&p&微服务架构技能图谱.png&/p&&p&&br&&/p&&img src=&/v2-56bcb603f198_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-56bcb603f198_r.jpg&&&p&&br&&/p&&p&微服务架构秘籍.jpg&/p&&p&&br&&/p&&img src=&/v2-7cb447fde6b5be27eac30_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&2323& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-7cb447fde6b5be27eac30_r.jpg&&&p&&br&&/p&&p&现代前端技术解析_V2@高清.jpg&/p&&p&&br&&/p&&img src=&/v2-488fee1ecd8e_b.jpg& data-caption=&& data-rawwidth=&692& data-rawheight=&461& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&/v2-488fee1ecd8e_r.jpg&&&p&&br&&/p&&p&一致性图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-7bcbffeaee2a779d51fc211a_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-7bcbffeaee2a779d51fc211a_r.jpg&&&p&&br&&/p&&p&移动端测试图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-0f4f1f5bccaa532fd36346_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-0f4f1f5bccaa532fd36346_r.png&&&p&&br&&/p&&p&移动性能优化1.0.png&/p&&p&&br&&/p&&img src=&/v2-e0a4a04e93f283f4bd3088_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1827& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-e0a4a04e93f283f4bd3088_r.jpg&&&p&&br&&/p&&p&云计算技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-bf600fd226d93eefd71ccdf7_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1098& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-bf600fd226d93eefd71ccdf7_r.jpg&&&p&&br&&/p&&p&云计算图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-6e139fc17d602d57f2bfe26e5865451d_b.jpg& data-caption=&& data-rawwidth=&1240& data-rawheight=&1827& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-6e139fc17d602d57f2bfe26e5865451d_r.jpg&&&p&&br&&/p&&p&运维技能图谱.jpg&/p&&p&&br&&/p&&img src=&/v2-906beb63b998c4a569b482_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1804& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-906beb63b998c4a569b482_r.png&&&p&&br&&/p&&p&Android 架构师.png&/p&&p&&br&&/p&&img src=&/v2-909b3b0e677e6aebf8253_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-909b3b0e677e6aebf8253_r.png&&&p&&br&&/p&&p&Angular 2技能图谱.png&/p&&p&&br&&/p&&img src=&/v2-b42ac7f5b2dcc250d34d68_b.jpg& data-caption=&& data-rawwidth=&980& data-rawheight=&742& class=&origin_image zh-lightbox-thumb& width=&980& data-original=&/v2-b42ac7f5b2dcc250d34d68_r.jpg&&&p&&br&&/p&&p&architecture知识结构图.jpg&/p&&p&&br&&/p&&img src=&/v2-aa49acedce9d021a5f00b551ae655fbe_b.png& data-caption=&& data-rawwidth=&1240& data-rawheight=&1821& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&/v2-aa49acedce9d021a5f00b551ae655fbe_r.png&&&p&&br&&/p&&p&DBA 技能图谱1.0.png&/p&&p&&br&&/p&&img src=&/v2-423b4fc73a4f_b.jpg& data-caption=&& data-rawwidth=&600& data-rawheight=&780& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-423b4fc73a4f_r.jpg&&&p&&br&&/p&&p&DevOps.jpg&/p&&p&&br&&/p&&p&&br&&/p&&img src=&/v2-ae5da7ad50e9d_b.jpg& data-caption=&& data-rawwidth=&980& data-rawheight=&3052& class=&origin_image zh-lightbox-thumb& width=&980& data-original=&/v2-ae5da7ad50e9d_r.jpg&&&p&dotnet知识结构图.jpg&/p&&p&全套48张高清下载:关注公众号[代码技巧](ID:&b&daimajiqiao&/b&)回复 &b&100&/b& 即可获取,付出总会有收获&/p&&p&&/p&&p&&/p&
汇集整理泛 IT 技术领域(云计算,大数据,运维,安全,开发语言,智能硬件,前端,后端等等)学习技能图谱,帮助程序员梳理知识框架结构,并尝试提供路径指导和精华资源,方便技术人学习成长。 后续技能图谱内容将会持续更新 H5技能图谱1.0.png Hadoop技能…
&img src=&/50/v2-e9b98a954c3d7b785ff724c_b.jpg& data-rawwidth=&500& data-rawheight=&258& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/50/v2-e9b98a954c3d7b785ff724c_r.jpg&&&p&前阵子,电气和电子工程师协会( IEEE),发布了2017年顶级编程语言交互排行榜,该榜单根据不同的变量,对48种比较流行的语言进行了排行,如招聘最受欢迎程度,语言的新兴程度,开源程度等12个标准,使得不同的受众根据自己的需求,进行自定义排名权重。&/p&&img src=&/50/v2-b0b681ccebd792fa7b52_b.jpg& data-rawwidth=&632& data-rawheight=&781& class=&origin_image zh-lightbox-thumb& width=&632& data-original=&/50/v2-b0b681ccebd792fa7b52_r.jpg&&&p&从榜单可以看出,世界上最好的编程语言PHP居第八位,而Python以微弱的优势,领先C语言排名第一。 &/p&&img src=&/50/v2-ae9d2b9a145a4209f9dbd11f_b.jpg& data-rawwidth=&640& data-rawheight=&431& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/50/v2-ae9d2b9a145a4209f9dbd11f_r.jpg&&&p&如果用大热门美剧《权力的游戏》中的角色,来解读这个排行榜,会有一些有趣的发现,死了又被复活的囧诺代表的是Python,而复活他的梅姨却是Ruby?而最好的语言PHP又是谁,一起来看看吧~&/p&&img src=&/v2-bf1ff5b6ca63337defac180e_b.jpg& data-rawwidth=&500& data-rawheight=&258& data-thumbnail=&/50/v2-bf1ff5b6ca63337defac180e_b.jpg& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/50/v2-bf1ff5b6ca63337defac180e_r.gif&&&h2&&b&Java——龙妈&/b&&/h2&&p&理由:Java是使用程度最为广泛、同时也是目前使用人数最多的编程语言,这一点,跟龙妈目前的处境还不太吻合(毕竟龙妈属于从边境慢慢渗入权力中心君临的)。不过,鉴于龙妈的真命天女身份,同时身边聚集了越来越多的力量,更为重要的一点——用Java写的项目,在你了解到核心部分之前,可能先要阅读很长很长的非核心代码......而任何人在跟龙妈交手之前,先要了解她的N种头衔(见下),真是一次打赢,终身炫功啊! &/p&&img src=&/v2-34b40f4d3bd61752cddd11e2bf49bc0e_b.jpg& data-rawwidth=&461& data-rawheight=&263& data-thumbnail=&/50/v2-34b40f4d3bd61752cddd11e2bf49bc0e_b.jpg& class=&origin_image zh-lightbox-thumb& width=&461& data-original=&/50/v2-34b40f4d3bd61752cddd11e2bf49bc0e_r.gif&&&p&龙妈(Daenerys Targaryen,丹尼莉丝·坦格利安)&/p&&p&风暴降生,龙石岛公主,不焚者,龙之母,弥林女王,阿斯塔波的解放者,安达尔人、罗伊拿人和先民的女王,七国统治者暨全境守护者,大草原上多斯拉克人的卡丽熙,打碎镣铐之人。(这一串儿当然是抄的了,根本记不住...)&/p&&p&龙妈女主光环加身,前六季下来,一路开外挂,不光有三条神龙护体(。。。传送门事件节哀),还有大军跟随;当然更重要的,还有好几位智商超群的仰慕者辅佐,就连掐架失手杀爹的小恶魔也投奔龙妈了......&/p&&p&技能修炼手册: &/p&&ul&&li&Java 8实战&/li&&li&Tomcat架构解析&/li&&/ul&&h2&&b&C语言——泰温老爹&/b&&/h2&&p&理由:C语言容易编译,贴近底层,多年占据编程语言排行榜前列,虽然近期份额有一定数量下降...凯岩城领主泰温虽然在如厕的时候挂了,但是他是少数几位对君临整体情况、不同家族的人物背景及未来趋势有深入洞察力的老前辈。&/p&&img src=&/50/v2-8e04d0fa792c63eac88462_b.jpg& data-rawwidth=&640& data-rawheight=&360& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/50/v2-8e04d0fa792c63eac88462_r.jpg&&&p&Tywin Lannister(泰温·兰尼斯特)&/p&&p&凯岩城公爵、西境守护,兰尼斯特狮族掌门人,詹姆、瑟曦和提利昂之父。手握重权、心思缜密、行事老辣,具有极高的军事和政治才能。被自己嫌弃的侏儒儿子提利昂在父子冲突中失手弄死......如若没有,在权力的游戏中,老前辈的筹码很高啊。&/p&&p&技能修炼手册:&/p&&ul&&li&明解C语言(入门篇,中级篇)&/li&&li&C语言程序设计:现代方法(第2版)&/li&&/ul&&h2&&b&C++——色后&/b&&/h2&&p&理由:C++功能强大,有直接提供面向对象编程和泛型编程的特性。瑟曦虽然不如老爹泰温那样老谋深算,但是傲娇任性、野心勃勃,处处效仿老爹......总之,是一个让人移不开眼的狠角色......&/p&&img src=&/v2-78b6f84f7dd531befd2dc0_b.jpg& data-rawwidth=&500& data-rawheight=&281& data-thumbnail=&/50/v2-78b6f84f7dd531befd2dc0_b.jpg& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/50/v2-78b6f84f7dd531befd2dc0_r.gif&&&p&色后(Cersei Lannister,瑟曦·兰尼斯特)&/p&&p&泰温之女,君临城鹿族劳勃国王之妻,乔弗里、弥塞菈、托曼的母亲,与孪生弟弟詹姆关系亲密,痛恨侏儒弟弟小恶魔。从小被巫魔女预言了一生,部分已应验(比如嫁给国王,母仪天下;三个孩子都会死去......)。&/p&&p&技能修炼手册:&/p&&ul&&li&C++权威教程(第6版)&/li&&li&C++程序设计实践与技巧&/li&&/ul&&h2&&b&Python——囧诺&/b&&/h2&&p&理由:如果说Python 3是从Python 2中重生了...另外,在人工智能和机器学习领域,Python炙手可热,作为未来之星的囧诺似乎可以拿Python一比。&br&&/p&&img src=&/v2-fa67f53ef537d8cc7b88c2d7_b.jpg& data-rawwidth=&300& data-rawheight=&184& data-thumbnail=&/50/v2-fa67f53ef537d8cc7b88c2d7_b.jpg& class=&content_image& width=&300&&&p&&br&&/p&&p&囧诺 (Jon Snow,琼恩·雪诺)&/p&&p&啥都不懂的临冬城公爵、北境守护、史塔克狼族掌门人艾德·史塔克的私生子(额,应该说是表面上的私生子,实际身份成迷,广为流传的推测是:龙族疯王长子雷加(龙妈的大哥)与艾德的妹妹绝世美女莱安娜之子)。囧雪第六季挂了,后面又被HBO和马丁爸爸复活了。&/p&&p&技能修炼手册:&/p&&ul&&li&Python编程:从入门到实践&/li&&li&流畅的Python&/li&&/ul&&h2&&b&C#——小玫瑰&/b&&/h2&&p&理由:借鉴了Java的设计思想,好学,易用,同时兼顾运行效率。小玫瑰聪明机智、处事圆滑,能屈能伸。&/p&&img src=&/v2-9b3a69dbcad_b.jpg& data-rawwidth=&500& data-rawheight=&280& data-thumbnail=&/50/v2-9b3a69dbcad_b.jpg& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/50/v2-9b3a69dbcad_r.gif&&&p&小玫瑰(Margaery Tyrell ,玛格丽·提利尔)&/p&&p&隶属高庭金玫瑰提利尔家族,师从祖母荆棘女王(老谋深算的奥莲娜·雷德温夫人),年轻貌美,处事圆滑周到。先后嫁给鹿族的蓝礼(劳勃的弟弟)、乔弗里和托曼(劳勃和色后的两个儿子)。她对权力同样具有勃勃野心,但她的手段比较高明,进入君临不久就受到了城内百姓的爱戴。当观众爱上这个心机貌美的皇后,并期待她跟色后和大麻雀有更多较量时,她就因为色后的一锅端复仇法领便当了...... &/p&&p&技能修炼手册:&/p&&ul&&li&C#图解教程(第4版)&/li&&li&深入理解C#(第3版)&/li&&/ul&&h2&&b&JavaScript——小恶魔&/b&&/h2&&p&理由:黑白两道通吃(前后端都能搞定),三观正,洞察世事,偶尔做点小坏事儿,但总体非常受欢迎。&/p&&img src=&/v2-dbe8cdd299e3be99a5f877e_b.jpg& data-rawwidth=&500& data-rawheight=&270& data-thumbnail=&/50/v2-dbe8cdd299e3be99a5f877e_b.jpg& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/50/v2-dbe8cdd299e3be99a5f877e_r.gif&&&p&小恶魔(Tyrion Lannister,提利昂·兰尼斯特)&/p&&p&泰温次子,因出生时母亲难产死亡,深受父亲厌恶。他喜欢美女、今朝有酒今朝醉,随性洒脱,虽身为侏儒,但智商超群,被迫弑父后投奔龙妈,可能会是龙妈与色后大战中夺取君临的关键智囊。&/p&&p&技能修炼手册:&/p&&ul&&li&JavaScript DOM编程艺术(第2版)&/li&&li&JavaScript高级程序设计(第3版)&/li&&/ul&&h2&&b&PHP——三傻&/b&&/h2&&p&理由:PHP是最好的语言...三傻是最乖的宝宝....第七季刚开播,就能开看三傻(PHP)和囧雪(python)之间的暗战,哎,为了争夺临冬城第一(世界上最好的语言),这两位简直是塑料花般的姐妹情谊。&/p&&img src=&/v2-16f5e2f4e4b2b76d6885a_b.jpg& data-rawwidth=&450& data-rawheight=&200& data-thumbnail=&/50/v2-16f5e2f4e4b2b76d6885a_b.jpg& class=&origin_image zh-lightbox-thumb& width=&450& data-original=&/50/v2-16f5e2f4e4b2b76d6885a_r.gif&&&p&三傻(Sansa Stark,珊莎·史塔克)&/p&&p&狼族艾德·史塔克的大女儿,先后因为政治联姻和宫斗牺牲品嫁给君临国王“乔大帝”(劳勃死后继位)、小恶魔、小剥皮,又被小指头觊觎。在前几季里是让观众恨铁不成钢的傻白甜(外号三傻为英文谐音,同时表达了中国观众为她智商捉急的情绪)。但是,经历一系列命运的捉弄之后,三傻在第6季开始长心了。。。&/p&&p&技能修炼手册:&/p&&ul&&li&PHP与MySQL程序设计(第4版)&/li&&li&深入PHP:面向对象、模式与实践(第3版)&/li&&/ul&&h2&&b&Go语言——布兰&/b&&/h2&&p&理由:出身名门,年轻,潜力无限,兼备特殊技能(翻白眼)...&/p&&img src=&/50/v2-bd956c77fed96e2e2c88f33ab70ae831_b.jpg& data-rawwidth=&600& data-rawheight=&310& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/50/v2-bd956c77fed96e2e2c88f33ab70ae831_r.jpg&&&p&布兰(Brandon Stark,布兰登·史塔克)&/p&&p&狼族艾德·史塔克次子,理性聪明,因喜攀爬城墙撞见色后和詹姆的关系,被詹姆推下城墙失去双腿。狼灵,具备特殊技能,可短时间控制其他动物的意识。临冬城被攻占后,远离权力纷争,带几位随从寻找三眼乌鸦,对抗异鬼。&/p&&p&技能修炼手册:&/p&&ul&&li&Go语言编程&/li&&li&Go并发编程实战(第2版)&/li&&/ul&&h2&&b&Swift——二丫&/b&&/h2&&p&理由:机敏好学,稳扎稳打逐步完善自身。Swift代码少性能优,二丫话少行动快......&/p&&img src=&/50/v2-d1e1b24807bbd7e8e0ceef_b.jpg& data-rawwidth=&640& data-rawheight=&426& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/50/v2-d1e1b24807bbd7e8e0ceef_r.jpg&&&p&二丫
(Arya Stark,艾丽娅·史塔克) &/p&&p&狼族艾德·史塔克次女,从小是个假小子,随身佩戴一枚绣花针型利剑“缝衣针”,性格倔强,出手利落。二丫(艾丽娅谐音,同时是家里的二女儿)亲眼目睹父亲艾德被“乔大帝”处死,之后独身一人流亡在外,复仇的信念支撑她顽强地生长,并习得绝技。带着绣花针走天下的二丫,刚刚干掉了谷地世仇弗雷一家......接下去的目标是色后。&/p&&p&技能修炼手册:&/p&&ul&&li&从零开始学Swift(第2版)&/li&&li&Swift编程权威指南(第2版)&/li&&/ul&&h2&&b&SQL——小指头&/b&&/h2&&p&理由:SQL是其他关系型数据库的基础,是后台必备技能。而小指头隐藏在幕后,精于计算,事事都跟他有牵扯。&/p&&img src=&/v2-f_b.jpg& data-rawwidth=&500& data-rawheight=&250& data-thumbnail=&/50/v2-f_b.jpg& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/50/v2-f_r.gif&&&p&小指头(Petyr Baelish,培提尔·贝里席)&/p&&p&君临前御前财政大臣,《权力的游戏》中各种战争的直接和间接引发者,藏于幕后,精于计算,阴险狡诈,纵横捭阖。便当领的也有点莫名其妙。。。&/p&&p&技能修炼手册:&/p&&ul&&li&SQL必知必会(第4版)&/li&&li&SQL基础教程(第2版)&/li&&/ul&&h2&&b&Objective-C——弑君者&/b&&/h2&&p&理由:OC是一种通用、高级、面向对象的编程语言。詹姆作为名门望族之后,气质出众,但他拥有其他角色不具备的凡人之痛,错爱、忠孝难两全...&/p&&img src=&/v2-2afcc807a3a_b.jpg& data-rawwidth=&500& data-rawheight=&281& data-thumbnail=&/50/v2-2afcc807a3a_b.jpg& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/50/v2-2afcc807a3a_r.gif&&&p&弑君者(Jaime Lannister,詹姆·兰尼斯特)&/p&&p&狮族泰温长子,色后孪生弟弟,痴爱色后。高大英俊美男、武艺超群,表面上冷酷无情,实际是一位具有骑士精神的男士。在断臂之前更英勇无双,断臂之后虽然失去了武力优势,但是开始从多维度思考问题。最让人唏嘘的是,一路走来,我们越来越能体会詹姆是一位悲情的角色,不出意外,巫魔女关于色后的预言会在詹姆的参与下画上句号。&/p&&p&技能修炼手册:&/p&&ul&&li&Objective-C基础教程(第2版)&/li&&li&Objective-C高级编程:iOS与OS X多线程和内存管理&/li&&/ul&&h2&&b&Ruby——红袍女&/b&&/h2&&p&理由:梅姨有红宝石,还有巫术和预言能力.....Ruby虽然入门难度略高,但是一旦搞定,用Ruby on Rails框架很快就可以搭建一个复杂的网站。&/p&&img src=&/50/v2-f61e122d0331dbaea0ce8ad2_b.jpg& data-rawwidth=&640& data-rawheight=&360& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/50/v2-f61e122d0331dbaea0ce8ad2_r.jpg&&&p&红袍女(Melisandre,梅丽珊卓)&/p&&p&梅丽珊卓,光之王拉赫洛的女祭司,据说已经几百岁,但是外表貌美。她的有一条红宝石项链,可以协助施展巫术。她能通过火焰看到未来的部分景象或者暗示(但需要解读,所以有可能出现错误),刚开始为鹿族劳勃国王的另一个弟弟史坦尼斯服务。到第六季为止,梅丽珊卓最大的功劳是复活囧诺。&/p&&p&技能修炼手册:&/p&&ul&&li&Ruby基础教程(第4版)&/li&&li&Ruby on Rails 教程(原书第4版)&/li&&/ul&&h2&&b&R语言——荆棘女王&/b&&/h2&&p&理由:R是一套完整的数据处理、计算和制图软件系统。荆棘女王在计算处理家族与国家之间利益的权衡利弊可谓是老谋深算,和R一样是一个不可小觑的存在。R作为一种统计分析软件,是集统计分析与图形显示于一体的。它可以运行于UNIX、Windows和Macintosh的操作系统上,而且嵌入了一个非常方便实用的帮助系统。荆棘女王为了谋取更大利益辗转于君临、多恩、高庭,可谓是不让须眉的女中人杰!&/p&&img src=&/50/v2-bb9ab3cb5ce6704_b.jpg& data-rawwidth=&640& data-rawheight=&360& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/50/v2-bb9ab3cb5ce6704_r.jpg&&&p&荆棘女王(Olenna Redwyne,奥莲娜·雷德温) &/p&&p&荆棘女王,小玫瑰玛格丽·提利尔、维拉斯·提利尔的祖母,梅斯·提利尔公爵的母亲,已故高庭公爵罗斯·提利尔的遗孀。曾设计让三傻嫁给维拉斯·提利尔,并阻止过色后与维拉斯的婚约。而据“小指头”与她在妓院的对话,不难猜出她才是谋害色后儿子乔佛里·拜拉席恩的真凶。在第六季小玫瑰的劝说下离开君临好运地躲过了色后的一锅端复仇,第七季肯定要和色后互相撕逼了,期待ing…… (来自图灵读者冰糖葫芦娃)&/p&&p&技能修炼手册:&/p&&ul&&li&R语言实战(第2版) &/li&&li&R语言与数据分析实战&/li&&/ul&&h2&&b&Scala——莫尔蒙&/b&&/h2&&p&理由:Scala就是龙母(Java )的乔拉·莫尔蒙骑士。 Scala 拥有面向对象和函数式的特性,运行在JVM上,兼容 Java 程序。代码最终还是编译成 Java 字节码文件,乔拉对丹妮莉丝的爱可谓爱到极致,对女王不离不弃。只要女王开心,他做什么都愿意。最后一次离开,女王说:“等将来一统天下登上铁王座时,我需要你站在我身边。” 乔拉为爱而已战,遵从内心的选择。是不是Scala跟Java 也是这样暧昧?(图灵读者叶糖糖)&/p&&img src=&/50/v2-12cabdf1c15e_b.jpg& data-rawwidth=&640& data-rawheight=&360& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/50/v2-12cabdf1c15e_r.jpg&&&h2&&b&汇编语言——伊蒙学士&/b&&/h2&&p&理由:他是整部剧中最年长的人,也是所有人物中最神秘,心智最高的。他说话轻柔,但富有价值,而且受人尊敬。他是真正的真龙血脉,用一生守护着维斯特洛大陆,他的守望至死不休~ 现在的很多内核和驱动的关键代码还是用汇编写的,汇编就像这些守夜人一样,守卫着软硬件的界线。(图灵读者Rekii)&/p&&img src=&/50/v2-f81e545c131be4a0baa1b_b.jpg& data-rawwidth=&640& data-rawheight=&360& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/50/v2-f81e545c131be4a0baa1b_r.jpg&&&p&&b&X——A·B&/b&&/p&&p&理由:更多编程语言与《权力的游戏》角色对应等你续写!......或者如果你不同意以上说法,让你当导演,你会将哪种编程语言安排给哪个角色呢?&/p&&p&————————————————————————————————————————&/p&&p&本文非原创,来自于网络采编,侵删。看文章似乎来自于图灵,不过我没找到原文。如有人知道原文地址拜托告知我一下,我将标注原文地址。&/p&
前阵子,电气和电子工程师协会( IEEE),发布了2017年顶级编程语言交互排行榜,该榜单根据不同的变量,对48种比较流行的语言进行了排行,如招聘最受欢迎程度,语言的新兴程度,开源程度等12个标准,使得不同的受众根据自己的需求,进行自定义排名权重。从榜…
&img src=&/50/v2-f05e6e6cce8aae2d0f81_b.png& data-rawwidth=&1179& data-rawheight=&327& class=&origin_image zh-lightbox-thumb& width=&1179& data-original=&/50/v2-f05e6e6cce8aae2d0f81_r.png&&&p&初稿日期:&br&发布于:&a href=&/?target=https%3A///library/article/simple-rxjs& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Angular 中文社区&i class=&icon-external&&&/i&&/a&&/p&&h2&量命名Style&/h2&&p&(个人约定,非任何best practice)&/p&&ul&&li&Observable变量以$结尾,如state$;&br&&/li&&li&Subject变量以$$结尾, 如state$$;&br&&/li&&li&Subscription变量以_结尾,如ultimate_。&/li&&/ul&&h2&写作目的&/h2&&p&RxJS包含许多概念,&a href=&/?target=http%3A//reactivex.io/rxjs/manual/index.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&RxJS Manual&i class=&icon-external&&&/i&&/a&在介绍RxJS的时候,引入了更多概念,对于初学者而言,不够直白。&br&比如:&/p&&blockquote&&p&ReactiveX combines the Observer pattern with the Iterator pattern and functional programming with collections to fill the need for an ideal way of managing sequences of events.&/p&&/blockquote&&p&再如:&/p&&blockquote&&p&Observable: represents the idea of an invokable collection of future values or events.&/p&&/blockquote&&p&本文将尝试用白话来解读RxJS。&br&比如:&/p&&blockquote&&p&Observable:好似一个Function,有很多return,定义了在哪个时点return什么数据。&/p&&/blockquote&&p&部分章节使用了伪代码,用来说明某个类的特性;其余部分,受限于水平,实在写不出伪代码。&br&难免错漏,敬请指正。&/p&&h2&Observer:一个Object,有3种callback&/h2&&p&Observer最平易近人,就是一个Object,里面有3种callback,形如:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const observer = {
next: (value) =& console.log(value),
error: (error) =& console.log(error),
complete: () =& console.log('completed')
&/code&&/pre&&/div&&h2&Observable:好似一个Function,有很多return,定义了在哪个时点return什么数据&/h2&&p&可以对照Function来看Observable。&br&Function是这样:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const funcX = () =& {
在处理处理处理以后:
return 'gogogo, but only once';
&/code&&/pre&&/div&&p&Observable就是这样:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const x$ = () =& {
(无条件的):
return 'I\'m the 1st go';
在'500 ms'以后:
return 'I\'m the 2nd go';
在http请求出错时:
return 'something went wrong';
// 然后就没有然后了
&/code&&/pre&&/div&&p&Observable可以return任意多次,每次return可能需要满足一定条件,比如“鼠标点击的时候”。&br&可是,怎么可能return多于1次呢?&br&这是伪代码,其实Observable应该是这样:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const x$ = (observer) =& {
(无条件的):
observer.next('I\'m the 1st go');
在'500 ms'以后:
observer.next('I\'m the 2nd go');
在http请求出错时:
observer.error('something went wrong');
// 然后就没有然后了
&/code&&/pre&&/div&&p&我们需要一个Observer,并用observer.callback来代替return。&/p&&h2&启动Observable:用Observable.subscribe(observer)&/h2&&p&Function在定义好以后,不会自动运行,Observable也是一样。&br&我们可以通过funX()(即在函数名后加上括号)来调用funX。&br&怎样调用Observable呢?看这里:&/p&&br&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kr&&const&/span& &span class=&nx&&FakeObservableClass&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&execution&/span& &span class=&o&&=&/span& &span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span&
&span class=&nx&&observer&/span&&span class=&p&&.&/span&&span class=&nx&&next&/span&&span class=&p&&(&/span&&span class=&s1&&'I\'m the 1st go'&/span&&span class=&p&&);&/span&
&span class=&nx&&observer&/span&&span class=&p&&.&/span&&span class=&nx&&next&/span&&span class=&p&&(&/span&&span class=&s1&&'I\'m the 2nd go'&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&nx&&FakeObservableClass&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&subscribe&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kr&&const&/span& &span class=&nx&&that&/span& &span class=&o&&=&/span& &span class=&k&&this&/span&&span class=&p&&;&/span&
&span class=&nx&&that&/span&&span class=&p&&.&/span&&span class=&nx&&execution&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&kr&&const&/span& &span class=&nx&&x$&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&nx&&FakeObservableClass&/span&&span class=&p&&();&/span&
&span class=&kr&&const&/span& &span class=&nx&&observer&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&&span class=&nx&&next&/span&&span class=&o&&:&/span& &span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)};&/span&
&span class=&nx&&x$&/span&&span class=&p&&.&/span&&span class=&nx&&subscribe&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&);&/span& &span class=&c1&&// 启动x$的运行&/span&
&/code&&/pre&&/div&&p&我们是通过subscribe方法,即x$.subscribe(observer)来启动x$的运行的。&/p&&h2&Subscription:一个Object,有一个方法unsubscribe,可以停掉运行中的Observable&/h2&&p&Function在启动以后是停不下来的,直到return。&br&Observable在subscribe以后,如果execution的内容是async的(比如setInterval、dom events、http response),它是可以停下来的。&br&以IntervalObservable为例:&/p&&br&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kr&&const&/span& &span class=&nx&&FakeIntervalObservableClass&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&interval&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&intervalId&/span& &span class=&o&&=&/span& &span class=&kc&&null&/span&&span class=&p&&;&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&execution&/span& &span class=&o&&=&/span& &span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span&
&span class=&kd&&let&/span& &span class=&nx&&state&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span&
&span class=&kd&&let&/span& &span class=&nx&&execute&/span& &span class=&o&&=&/span& &span class=&p&&(&/span&&span class=&nx&&state&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&intervalId&/span& &span class=&o&&=&/span& &span class=&nx&&setInterval&/span&&span class=&p&&(()&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span&
&span class=&nx&&observer&/span&&span class=&p&&.&/span&&span class=&nx&&next&/span&&span class=&p&&(&/span&&span class=&o&&++&/span&&span class=&nx&&state&/span&&span class=&p&&);&/span&
&span class=&p&&},&/span& &span class=&nx&&interval&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&nx&&execute&/span&&span class=&p&&(&/span&&span class=&nx&&state&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&nx&&FakeIntervalObservableClass&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&subscribe&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kr&&const&/span& &span class=&nx&&that&/span& &span class=&o&&=&/span& &span class=&k&&this&/span&&span class=&p&&;&/span&
&span class=&nx&&that&/span&&span class=&p&&.&/span&&span class=&nx&&execution&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&);&/span& &span class=&c1&&// subscribe的时候会执行setInterval&/span&
&span class=&c1&&// 接下来return一个Obeject,这个Object就是Subscription&/span&
&span class=&k&&return&/span& &span class=&p&&{&/span&
&span class=&nx&&unsubscribe&/span&&span class=&o&&:&/span& &span class=&p&&()&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span&
&span class=&nx&&clearInterval&/span&&span class=&p&&(&/span&&span class=&nx&&that&/span&&span class=&p&&.&/span&&span class=&nx&&intervalId&/span&&span class=&p&&);&/span& &span class=&c1&&// unsubscribe时执行clearInterval&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&kr&&const&/span& &span class=&nx&&x$&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&nx&&FakeIntervalObservableClass&/span&&span class=&p&&(&/span&&span class=&mi&&500&/span&&span class=&p&&);&/span&
&span class=&kr&&const&/span& &span class=&nx&&observer&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&&span class=&nx&&next&/span&&span class=&o&&:&/span& &span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)};&/span&
&span class=&kr&&const&/span& &span class=&nx&&x_&/span& &span class=&o&&=&/span& &span class=&nx&&x$&/span&&span class=&p&&.&/span&&span class=&nx&&subscribe&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&);&/span&
&span class=&nx&&x_&/span&&span class=&p&&.&/span&&span class=&nx&&unsubscribe&/span&&span class=&p&&();&/span&
&/code&&/pre&&/div&&p&subscribe方法返回一个Object,这个Object就是Subscription(即x_),它唯一的用途就是unsubsribe。&br&unsubscribe会停下Observable的运行,并释放其占用的资源。&br&上面的例子中,当我们调用x_.unsubscribe()的时候,触发了clearInterval,后面的execution就不再进行下去了。&br&如果是dom事件触发的Observable,subscribe时会addEventListener,unsubscribe时会removeEventListener。&br&如果是XMLHttpRequest触发的Observable,就是send和abort,等等。&/p&&h2&Subject:有next和subscribe方法,有一个observers列表&/h2&&p&Subject是一个Observable,因为它有subscribe方法;Subject又是一个Observer,因为它有next方法。&br&它维护一个observers列表,当运行subject.subscribe(observerX)的时候,这个observerX就被加到列表里,unsubscribe时从列表中删掉。&br&Subject像是一个proxy,外部调用subject.next(value)时,这个value会被forEach给Subject的observers。&/p&&br&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kr&&const&/span& &span class=&nx&&FakeSubjectClass&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&observers&/span& &span class=&o&&=&/span& &span class=&p&&[];&/span&
&span class=&p&&}&/span&
&span class=&nx&&FakeSubjectClass&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&subscribe&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kd&&let&/span& &span class=&nx&&that&/span& &span class=&o&&=&/span& &span class=&k&&this&/span&&span class=&p&&;&/span&
&span class=&kd&&let&/span& &span class=&nx&&subscription&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&unsubscribe&/span&&span class=&o&&:&/span& &span class=&p&&()&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span& &span class=&c1&&// unsubsribe要做的就是从observers列表里将这个observer删除&/span&
&span class=&nx&&that&/span&&span class=&p&&.&/span&&span class=&nx&&observers&/span& &span class=&o&&=&/span& &span class=&nx&&that&/span&&span class=&p&&.&/span&&span class=&nx&&observers&/span&&span class=&p&&.&/span&&span class=&nx&&filter&/span&&span class=&p&&((&/span&&span class=&nx&&observerX&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&nx&&observerX&/span& &span class=&o&&!==&/span& &span class=&nx&&observer&/span&&span class=&p&&)&/span&
&span class=&p&&}&/span&
&span class=&p&&};&/span&
&span class=&c1&&// 添加新observer,添加之前会检查是否observers已经有了这个observer,如果有,就直接返回subscription&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&nx&&that&/span&&span class=&p&&.&/span&&span class=&nx&&observers&/span&&span class=&p&&.&/span&&span class=&nx&&indexOf&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&)&/span& &span class=&o&&&=&/span& &span class=&mi&&0&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&return&/span& &span class=&nx&&subscription&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span& &span class=&k&&else&/span& &span class=&p&&{&/span&
&span class=&nx&&that&/span&&span class=&p&&.&/span&&span class=&nx&&observers&/span&&span class=&p&&.&/span&&span class=&nx&&push&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span&&span class=&p&&);&/span&
&span class=&k&&return&/span& &span class=&nx&&subscription&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&nx&&FakeSubjectClass&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&next&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kd&&let&/span& &span class=&nx&&that&/span& &span class=&o&&=&/span& &span class=&k&&this&/span&&span class=&p&&;&/span&
&span class=&nx&&that&/span&&span class=&p&&.&/span&&span class=&nx&&observers&/span&&span class=&p&&.&/span&&span class=&nx&&forEach&/span&&span class=&p&&(&/span&&span class=&nx&&observer&/span& &span class=&o&&=&&/span& &span class=&nx&&observer&/span&&span class=&p&&.&/span&&span class=&nx&&next&/span&&span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&));&/span& &span class=&c1&&// 外部调用subject.next,subject.next马上forEach转出去;这个就是multicast&/span&
&span class=&p&&}&/span&
&span class=&kd&&let&/span& &span class=&nx&&x$$&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&nx&&FakeSubjectClass&/span&&span class=&p&&();&/span& &span class=&c1&&// Subject以$$结尾,Observable以$结尾&/span&
&span class=&kd&&let&/span& &span class=&nx&&observerB&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&&span class=&nx&&next&/span&&span class=&o&&:&/span& &span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span&&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&sb&&`另一个logger说:&/span&&span class=&si&&${&/span&&span class=&nx&&value&/span&&span class=&si&&}&/span&&span class=&sb&&`&/span&&span class=&p&&)}}&/span&
&span class=&nx&&x$$&/span&&span class=&p&&.&/span&&span class=&nx&&subscribe&/span&&span class=&p&&(&/span&&span class=&nx&&observerB&/span&&span class=&p&&);&/span& &span class=&c1&&// 这个observerB被加到了x$$的observers列表里。&/span&
&span class=&nx&&x$&/span&&span class=&p&&.&/span&&span class=&nx&&subscribe&/span&&span class=&p&&(&/span&&span class=&nx&&x$$&/span&&span class=&p&&);&/span& &span class=&c1&&// 当x$向外推送时,调用的是x$$.next;x$$.next转身马上就forEach转给它的observers。&/span&
&/code&&/pre&&/div&&p&顺带提一下unicast和multicast的资源消耗。&br&说Observable是unicast,而Subject是multicast,就是因为这个observers列表 -- 在Observable里没有这个属性。&br&每次运行Observable.subscribe()都相当于一个Function.call(),是一个独立的运行,需要单独消耗资源。&br&而Subject.subscribe()消耗资源很少。&br&比如:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&let y$ = Observable.whatever() // 创建新的Observable
let y1_ = y$.subscribe(observerA); // 运行一次y$里的execution,消耗资源
let y2_ = y$.subscribe(observerB); // 又运行一次...
let y3_ ...
// ===========
let z$ = Observable.whatever();
let z$$ = new Subject();
let z1_ = z$$.subscribe(observerX) // observers列表里加一项,基本不消耗什么资源
let z2_ = z$$.subscribe(observerY) // observers列表里加一项
let z0_ = z$.subscribe(z$$) // 运行一次z$里的execution,仅此一次,然后z$推给z$$,z$$用forEach推给后面
&/code&&/pre&&/div&&h2&Operator:一半AllSpark(创建Observable),一半滤镜(变更推送时点和推送内容)&/h2&&p&在Observable类上调用的Operator是Static Operator, 比如:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Observable.interval(500);
// 每隔500ms,推送一个递增整数,从0开始
Observable.from([1, 2]);
// 连续推送1, 2
Observable.fromEvent(document, 'click'); // 每次点击,推送一个event Object
Observable.fromPromise(fetch('/users')); // resolve或reject时,推送给Observer
Observable.ajax('/users')
// http 'GET' /users,response或error时,推送给Observer
const ws$$ = Observable.webSocket('ws://echo.websocket.org/') // 这个ws$$是个Subject
// 通过ws$$.next(JSON.stringify(value))向websocket发送信息
// 通过ws$$.subscribe(observer)来接收信息
Observable.merge(x$, y$);
// 将x$与y$的推送混合在一起
Observable.concat(x$, y$); // 先运行x$,等x$推送plete(),再运行y$
&/code&&/pre&&/div&&p&Static Operator可以从无到有创建一个Observable(像是变形金刚里的AllSpark),也可以把互不干预的Observable组合起来。&br&前面的伪代码中用到const x$ = new FakeObservableClass();。&br&实际生活中new Observable()都是由Static Operator来处理的,所以在代码中不会看到new Observable()。&/p&&p&===== 分割线 =====&/p&&p&在Observable的实例上调用的Operator是Instance Operator, 比如:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const x$ = Observable.interval(500); // 创建一个Observable实例
const y$ = x$
.map(v =& v*3)
// 每个推送的数值乘以3
.filter(w =& w%5 === 1)
// 只推送除以5余数为1的数值
.delay(1000)
// 等待1秒再推送
// 推送10个数值以后,调用plete()结束
// x$还是那个x$
&/code&&/pre&&/div&&p&如果Observable是一幅画,Instance Operator就是滤镜。&br&经过滤镜处理,我们拿到了一幅新的画,原来的画还在。&/p&&h2&Scheduler:控制并发事件&/h2&&p&Scheduler的职能是控制并发事件。本人开发经验接近0,实在想不出实际生活中何时会用到Scheduler,也确实在实践中没用过。&br&如果要观察每种Scheduler对数据推送的影响,可以打开&a href=&/?target=http%3A//reactivex.io/rxjs/manual/index.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&RxJS Manual&i class=&icon-external&&&/i&&/a&,开启console,贴入下面的代码,回车。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const x$ = Rx.Observable.create((observer) =& {
observer.next(0);
observer.next(1);
}) // 这个用的是null Scheduler
const xOnQueue$ = x$.map(v=&'onQueue'+v).observeOn(Rx.Scheduler.queue);
const xOnAsync$ = x$.map(v=&'onAsync'+v).observeOn(Rx.Scheduler.async);
const xOnAsap$ = x$.map(v=&'onAsap'+v).observeOn(Rx.Scheduler.asap);
const xOnAnimationFrame$ = x$.map(v=&'onAnimationFrame'+v).observeOn(Rx.Scheduler.animationFrame);
const merged$ = Rx.Observable.merge(xOnAnimationFrame$, xOnAsync$, xOnAsap$, xOnQueue$, x$);
// 注意Observable.merge时的顺序,再对照console.log中的输出顺序
const merged_ = merged$.subscribe(console.log);
&/code&&/pre&&/div&&h2&总结&/h2&&p&提到Observable的时候,就想想Function,一个脱离了低级趣味的(有随意数量return的)Function,一个有益于人民的(定义了在某个时点return什么数据的)Function。&/p&&p&Happy coding!&/p&&h2&参考&/h2&&p&&a href=&/?target=https%3A///ReactiveX/rxjs& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&RxJS Source Code&i class=&icon-external&&&/i&&/a&&br&&a href=&/?target=http%3A//reactivex.io/rxjs/manual/index.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&RxJS Manual&i class=&icon-external&&&/i&&/a&&/p&
初稿日期: 发布于:量命名Style(个人约定,非任何best practice)Observable变量以$结尾,如state$; Subject变量以$$结尾, 如state$$; Subscription变量以_结尾,如ultimate_。写作目的RxJS包含许多概念,在介绍…
&img src=&/50/v2-f1bcffbb232eab77a08dd862ed2c8209_b.jpg& data-rawwidth=&1000& data-rawheight=&1000& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&/50/v2-f1bcffbb232eab77a08dd862ed2c8209_r.jpg&&&p&&b&2.1 相同网段的通信&/b&&br&&/p&大多数同学都知道电脑上网需要一个&b&IP地址&/b&,一个&b&掩码&/b&,还需要一个&b&网关&/b&,最后还需要一个&b&域名服务器DNS&/b&地址。&br&&p&橙子公司有三位员工,小明、小美、小丽,他们电脑IP参数分别如下:&br&&/p&&p&小明&br&IP = 10.1.1.2/24&br&Default Gateway = 10.1.1.1&br&DNS Server = 10.10.10.10&br&&br&小美&br&IP = 10.1.1.3/24&br&Default Gateway = 10.1.1.1&br&DNS Server = 10.10.10.10&br&&br&小丽&br&IP = 10.1.2.2/24&br&Default Gateway = 10.1.2.1&br&DNS Server = 10.10.10.10&br&&br&&b&小明的网络掩码为24&/b&,即二进制的24位,由于一个字节是8位,所以掩码一共是3个字节。&br&小明Ping小美的电脑,那什么是&b&Ping&/b&?&/p&&p&英国海军发明了&b&声纳(Sonar)&/b&,发射出特定波长的声波,遇到潜水艇会反射回来,以此可以定位潜水艇的位置与距离,以此作出准确的攻击。&/p&&img data-rawwidth=&300& data-rawheight=&244& src=&/v2-0e933d694e_b.jpg& class=&content_image& width=&300&&&br&&p&Ping也是类似原理,Ping包发送出去,到达目的电脑(IP)会弹回来,用于判断目的电脑是否联网状态。&/p&&p&小明&b&Ping 10.1.1.3&/b&,小明的电脑首先判断10.1.1.3是不是和自己在一个网段,那怎么判断呢?很简单,用自己的掩码(3个字节长),去遮掩(从左到右)10.1.1.3,这也是掩码的由来,很显然10.1.1.3一共4个字节,那被遮盖住的是哪三个字节呢?很显然是10.1.1,这个10.1.1我们给它一个名字:&b&网段号(Network ID)&/b&,那剩下的3我们也给它一个名字:&b&主机号(Host ID)&/b&,所以一个IP地址由两部分组成:&b&网段号+ 主机号&/b&。&/p&
2.1 相同网段的通信 大多数同学都知道电脑上网需要一个IP地址,一个掩码,还需要一个网关,最后还需要一个域名服务器DNS地址。 橙子公司有三位员工,小明、小美、小丽,他们电脑IP参数分别如下: 小明 IP = 10.1.1.2/24 Default Gateway = 10.1.1.1 DNS Ser…
&p&关于题主的这个问题,我见过的最好文章是Google Chrome团队的Jake Archibald的文章&a href=&///?target=https%3A///2015/tasks-microtasks-queues-and-schedules/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Tasks, microtasks, queues and schedules&i class=&icon-external&&&/i&&/a& ,里边带交互动画。&/p&&p&这个问题和问题描述代码都和这篇文章高度相似。&/p&
关于题主的这个问题,我见过的最好文章是Google Chrome团队的Jake Archibald的文章 ,里边带交互动画。这个问题和问题描述代码都和这篇文章高度相似。
&img src=&/50/v2-6e0766fcbe8baee6a09b066_b.jpg& data-rawwidth=&600& data-rawheight=&800& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/50/v2-6e0766fcbe8baee6a09b066_r.jpg&&&p&RxJS是一个强大的Reactive编程库,提供了强大的数据流组合与控制能力,但是其学习门槛一直很高,本次分享期望从一些特别的角度解读它在业务中的使用,而不是从API角度去讲解。&/p&&h2&RxJS简介&/h2&&p&通常,对RxJS的解释会是这么一些东西,我们来分别看看它们的含义是什么。&/p&&ul&&li&Reactive&/li&&li&Lodash for events&/li&&li&Observable&/li&&li&Stream-based&/li&&/ul&&p&什么是Reactive呢,一个比较直观的对比是这样的:&/p&&p&比如说,abc三个变量之间存在加法关系:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&a = b + c
&/code&&/pre&&/div&&p&在传统方式下,这是一种一次性的赋值过程,调用一次就结束了,后面b和c再改变,a也不会变了。&/p&&p&而在Reactive的理念中,我们定义的不是一次性赋值过程,而是可重复的赋值过程,或者说是变量之间的关系:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&a: = b + c
&/code&&/pre&&/div&&p&定义出这种关系之后,每次b或者c产生改变,这个表达式都会被重新计算。不同的库或者语言的实现机制可能不同,写法也不完全一样,但理念是相通的,都是描述出数据之间的联动关系。&/p&&p&在前端,我们通常有这么一些方式来处理异步的东西:&/p&&ul&&li&回调&/li&&li&事件&/li&&li&Promise&/li&&li&Generator&/li&&/ul&&p&其中,存在两种处理问题的方式,因为需求也是两种:&/p&&ul&&li&分发&/li&&li&流程&/li&&/ul&&p&在处理分发的需求的时候,回调、事件或者类似订阅发布这种模式是比较合适的;而在处理流程性质的需求时,Promise和Generator比较合适。&/p&&p&在前端,尤其交互很复杂的系统中,RxJS其实是要比Generator有优势的,因为常见的每种客户端开发都是基于事件编程的,对于事件的处理会非常多,而一旦系统中大量出现一个事件要修改视图的多个部分(状态树的多个位置),分发关系就更多了。&/p&&p&RxJS的优势在于结合了两种模式,它的每个Observable上都能够订阅,而Observable之间的关系,则能够体现流程(注意,RxJS里面的流程的控制和处理,其直观性略强于Promise,但弱于Generator)。&/p&&p&我们可以把一切输入都当做数据流来处理,比如说:&/p&&ul&&li&用户操作&/li&&li&网络响应&/li&&li&定时器&/li&&li&Worker&/li&&/ul&&p&RxJS提供了各种API来创建数据流:&/p&&ul&&li&单值:of, empty, never&/li&&li&多值:from&/li&&li&定时:interval, timer&/li&&li&从事件创建:fromEvent&/li&&li&从Promise创建:fromPromise&/li&&li&自定义创建:create&/li&&/ul&&p&创建出来的数据流是一种可观察的序列,可以被订阅,也可以被用来做一些转换操作,比如:&/p&&ul&&li&改变数据形态:map, mapTo, pluck&/li&&li&过滤一些值:filter, skip, first, last, take&/li&&li&时间轴上的操作:delay, timeout, throttle, debounce, audit, bufferTime&/li&&li&累加:reduce, scan&/li&&li&异常处理:throw, catch, retry, finally&/li&&li&条件执行:takeUntil, delayWhen, retryWhen, subscribeOn, ObserveOn&/li&&li&转接:switch&/li&&/ul&&p&也可以对若干个数据流进行组合:&/p&&ul&&li&concat,保持原来的序列顺序连接两个数据流&/li&&li&merge,合并序列&/li&&li&race,预设条件为其中一个数据流完成&/li&&li&forkJoin,预设条件为所有数据流都完成&/li&&li&zip,取各来源数据流最后一个值合并为对象&/li&&li&combineLatest,取各来源数据流最后一个值合并为数组&/li&&/ul&&p&这时候回头看,其实RxJS在事件处理的路上已经走得太远了,从事件到流,它被称为lodash for events,倒不如说是lodash for stream更贴切,它提供的这些操作符也确实可以跟lodash媲美。&/p&&p&数据流这个词,很多时候,是从data-flow翻译过来的,但flow跟stream是不一样的,我的理解是:flow只关注一个大致方向,而stream是受到更严格约束的,它更像是在无形的管道里面流动。&/p&&p&那么,数据的管道是什么形状的?&/p&&p&在RxJS中,存在这么几种东西:&/p&&ul&&li&Observable 可观察序列,只出不进&/li&&li&Observer 观察者,只进不出&/li&&li&Subject 可出可进的可观察序列,可作为观察者&ul&&li&ReplaySubject 带回放&/li&&/ul&&/li&&li&Subscription 订阅关系&/li&&/ul&&p&前三种东西,根据它们数据进出的可能性,可以通俗地理解他们的连接方式,这也就是所谓管道的“形状”,一端密闭一端开头,还是两端开口,都可以用来辅助记忆。&/p&&p&上面提到的Subscription,则是订阅之后形成的一个订阅关系,可以用于取消订阅。&/p&&p&下面,我们通过一些示例来大致了解一下RxJS所提供的能力,以及用它进行开发所需要的思路转换。&/p&&h2&示例一:简单的订阅&/h2&&p&很多时候,我们会有一些显示时间的场景,比如在页面下添加评论,评论列表中显示了它们分别是什么时间创建的,为了含义更清晰,可能我们会引入moment这样的库,把这个时间转换为与当前时间的距离:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const diff = moment(createAt).fromNow()
&/code&&/pre&&/div&&p&这样,显示的时间就是:一分钟内,昨天,上个月这样的字样。&/p&&p&但我们注意到,引入这个转换是为了增强体验,而如果某个用户停留在当前视图时间太长,它的这些信息会变得不准确,比如说,用户停留了一个小时,而它看到的信息还显示:5分钟之前发表了评论,实际时间是一个小时零5分钟以前的事了。&/p&&p&从这个角度看,我们做这个体验增强的事情只做了一半,不准确的信息是不能算作增强体验的。&/p&&p&在没有RxJS的情况下,我们可能会通过一个定时器来做这件事,比如在组件内部:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&tick() {
this.diff = moment(createAt).fromNow()
setTimeout(tick.bind(this), 1000)
&/code&&/pre&&/div&&p&但组件并不一定只有一份实例,这样,整个界面上可能就有很多定时器在同时跑,这是一种浪费。如果要做优化,可以把定时器做成一种服务,把业务上需要周期执行的东西放进去,当作定时任务来跑。&/p&&p&如果使用RxJS,可以很容易做到这件事:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Observable.interval(1000).subscribe(() =& {
this.diff = moment(createAt).fromNow()
&/code&&/pre&&/div&&h2&示例二:对时间轴的操纵&/h2&&p&RxJS一个很强大的特点是,它以流的方式来对待数据,因此,可以用一些操作符对整个流上所有的数据进行延时、取样、调整密集度等等。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const timeA$ = Observable.interval(1000)
const timeB$ = timeA$.filter(num =& {
return (num % 2 != 0)
&& (num % 3 != 0)
&& (num % 5 != 0)
&& (num % 7 != 0)
const timeC$ = timeB$.debounceTime(3000)
const timeD$ = timeC$.delay(2000)
&/code&&/pre&&/div&&p&示例代码中,我们创建了四个流:&/p&&ul&&li&A是由定时器产生的,每秒一个值&/li&&li&B从A里面过滤掉了一些&/li&&li&C在B的基础上,对每两个间距在3秒之内的值进行了处理,只留下后一个值&/li&&li&D把C的结果整体向后平移了2秒&/li&&/ul&&p&所以结果大致如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&A: 0
10 11 12 13 14 15 16 17 18 19 20 21
&/code&&/pre&&/div&&h2&示例三:我们来晚了&/h2&&p&RxJS还提供了BehaviourSubject和ReplaySubject这样的东西,用于记录数据流上一些比较重要的信息,让那些“我们来晚了”的订阅者们回放之前错过的一切。&/p&&p&ReplaySubject可以指定保留的值的个数,超过的部分会被丢弃。&/p&&p&最近新版《射雕英雄传》比较火,我们来用代码描述其中一个场景。&/p&&blockquote&&p&郭靖和黄蓉一起背书,黄蓉记忆力很好,看了什么,就全部记得;而郭靖属鱼的,记忆只有七秒,始终只记得背诵的最后三个字,两人一起背诵《九阴真经》。&/p&&/blockquote&&p&代码实现如下:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const 九阴真经 = '天之道,损有余而补不足'
const 黄蓉$ = new ReplaySubject(Number.MAX_VALUE)
const 郭靖$ = new ReplaySubject(3)
const 读书$ = Observable.from(九阴真经.split(''))
读书$.subscribe(黄蓉$)
读书$.subscribe(郭靖$)
&/code&&/pre&&/div&&p&执行之后,我们就可以看到,黄蓉背出了所有字,郭靖只记得“补不足”三个字。&/p&&h2&示例四:自动更新的状态树&/h2&&p&熟悉Redux的人应该会对这样一套理念不陌生:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&当前视图状态 := 之前的状态 + 本次修改的部分
&/code&&/pre&&/div&&p&从一个应用启动之后,整个全局状态的变化,就等于初始的状态叠加了之后所有action导致的状态修改结果。&/p&&p&所以这就是一个典型的reduce操作。在RxJS里面,有一个scan操作符可以用来表达这个含义,比如说,我们可以表达这样一个东西:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const action$ = new Subject()
const reducer = (state, payload) =& {
// 把payload叠加到state上返回
const state$ = action$.scan(reducer)
.startWith({})
&/code&&/pre&&/div&&p&只需往这个action$里面推action,就能够在state$上获取出当前状态。&/p&&p&在Redux里面,会有一个东西叫combineReducer,在state比较大的时候,用不同的reducer修改state的不同的分支,然后合并。如果使用RxJS,也可以很容易表达出来:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const meAction$ = new Subject()
const meReducer = (state, payload) =& {}
const articleAction$ = new Subject()
const articleReducer = (state, payload) =& {}
const me$ = meAction$.scan(meReducer).startWith({})
const article$ = articleAction$.scan(articleReducer).startWith({})
const state$ = Observable
(me, article) =& {me, article}
&/code&&/pre&&/div&&p&借助这样的机制,我们实现了Redux类似的功能,社区里面也有基于RxJS实现的Redux-Observable这样的Redux中间件。&/p&&p&注意,我们这里的代码中,并未使用dispatch action这样的方式去严格模拟Redux。&/p&&p&再深入考虑,在比较复杂的场景下,reducer其实很复杂。比如说,视图上发起一个操作,会需要修改视图的好多地方,因此也就是要修改全局状态树的不同位置。&/p&&p&在这样的场景中,从视图发起的某个action,要么调用一个很复杂的reducer去到处改数据,要么再次发起多个action,让很多个reducer各自改自己的数据。&/p&&p&前者的问题是,代码耦合太严重;后者的问题是,整个流程太难追踪,比如说,某一块状态,想要追踪到自己是被从哪里发起的修改所改变的,是非常困难的事情。&/p&&p&如果我们能够把Observable上面的同步修改过程视为reducer,就可以从另外一些角度大幅简化代码,并且让联动逻辑清晰化。例如,如果我们想描述一篇文章的编辑权限:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const editable$ = bineLatest(article$, me$)
.map(arr =& {
let [article, me] = arr
return me.isAdmin || article.author === me.id
&/code&&/pre&&/div&&p&这段代码的实质是什么?其实本质上还是reducer,表达的是数据的合并与转换过程,而且是同步的。我们可以把article和me的变更reduce到article$和me$里,由它们派发隐式的action去推动editable计算新值。&/p&&p&更详细探索的可以参见之前的这篇文章:&a href=&/?target=https%3A///xufei/blog/issues/42& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&复杂单页应用的数据层设计&i class=&icon-external&&&/i&&/a&&/p&&h2&示例五:幸福人生&/h2&&p&人生是什么样子的呢?&/p&&p&著名央视主持人白岩松曾经说过:&/p&&blockquote&&p&赚钱是为了买房,买房是为了赚钱。&/p&&/blockquote&&p&这两句话听上去很悲哀,却很符合社会现实。(不要在意是不是白岩松说的啦,不是他就是鲁迅,要么就是莎士比亚)&/p&&p&作为程序员,我们可以尝试想想如何用代码把它表达出来。&/p&&p&如果用命令式编程的理念来描述这段逻辑,是不太好下手的,因为它看起来像个死循环,可是人生不就是一天一天的死循环吗,这个复杂的世界,谁是自变量,谁是因变量?&/p&&p&死循环之所以很难用代码表达,是因为你不知道先定义哪个变量,如果变量的依赖关系形成了闭环,就总有一段定义不起来。&/p&&p&但是,在RxJS这么一套东西中,我们可以很容易把这套关系描述出来。前面说过,基于RxJS编程,就好像是在组装管道,依赖关系其实是定义在管道上,而不是在数据上。所以,不存在命令式的那些问题,只要管道能够接起来,再放进去数据就可以了。所以,我们可以先定义管道之间的依赖关系,&/p&&p&首先,从这段话中寻找一些变量,得到如下结果:&/p&&ul&&li&钱&/li&&li&房&/li&&/ul&&p&然后,我们来探索它们各自的来源。&/p&&blockquote&&p&钱从哪里来?&br&出租房子。&br&房子从哪里来?&br&钱挣够了就买。&/p&&/blockquote&&p&听上去还是死循环啊?&/p&&p&我们接着分析:&/p&&blockquote&&p&钱是只有一个来源吗?&br&不是,原始积累肯定不是房租,我们假定那是工资。所以,收入是有工资和房租两个部分组成。&br&房子是只有一个来源吗?&br&对,我们不是贪官,房子都是用钱买的。&/p&&/blockquote&&p&好,现在我们有四个变量了:&/p&&ul&&li&钱&/li&&li&房&/li&&li&工资&/li&&li&房租&/li&&/ul&&p&我们尝试定义这些变量之间的关系:&/p&&ul&&li&工资 := 定时取值的常量&/li&&li&房租 := 定时取值的变量,与房子数量成正比&/li&&li&钱 := 工资 + 房租&/li&&li&房 := 钱.map(够了就买)&/li&&/ul&&p&调整这些变量的定义顺序,凡是不依赖别人的,一律提到最前面实现。尴尬地发现,这四个变量里,只有工资是一直不变的,先提前。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const salary$ = Observable.interval(100).mapTo(2)
&/code&&/pre&&/div&&p&剩下的,都是依赖别人的,而且,没有哪个东西是只依赖已定义的变量,在存在业务上的循环依赖的时候,就会发生这样的情况。在这种情况下,我们可以从中找出被依赖最少的变量,声明一个Subject用于占位,比如这里的房子。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const house$ = new Subject()
&/code&&/pre&&/div&&p&接下来再看,以上几个变量中,有哪个可以跟着确定?是房租,所以,我们可以得到房租与房子数量的关系表达式,注意,以上的salary$、house$,表达的都是单次增加的值,不代表总的值,但是,算房租是要用总的房子数量来算的,所以,我们还需要先表达出总的房子数量:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const houseCount$ = house$.scan((acc, num) =& acc + num, 0).startWith(0)
&/code&&/pre&&/div&&p&然后,可以得到房租的表达式:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const rent$ = Observable.interval(3000)
.withLatestFrom(houseCount$)
.map(arr =& arr[1] * 5)
&/code&&/pre&&/div&&p&解释一下上面这段代码:&/p&&ul&&li&房租由房租周期的定时器触发&/li&&li&然后到房子数量中取最后一个值,也就是当前有多少套房&/li&&li&然后,用房子数量乘以单套房的月租,假设是5&/li&&/ul&&p&房租定义出来了之后,钱就可以被定义了:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const income$ = Observable.merge(salary$, rent$)
&/code&&/pre&&/div&&p&注意,income$所代表的含义是,所有的单次收入,包含工资和房租。&/p&&p&到目前为止,我们还有一个东西没有被定义,那就是房子。如何从收入转化为房子呢?为了示例简单,我们把它们的关系定义为:&/p&&p&一旦现金流够买房,就去买。&/p&&p&所以,我们需要定义现金流与房子数量的关系:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const cash$ = income$
.scan((acc, num) =& {
const newSum = acc + num
const newHouse = Math.floor(newSum / 100)
if (newHouse & 0) {
house$.next(newHouse)
return newSum % 100
&/code&&/pre&&/div&&p&这段逻辑的含义是:&/p&&ul&&li&累积之前的现金流与本次收入&/li&&li&假定房价100,先看看现金够买几套房,能买几套买几套&/li&&li&重新计算买完之后的现金&/li&&/ul&&p&总结一下,这么一段代码,就表达清楚了我们所有的业务需求:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// 挣钱是为了买房,买房是为了赚钱
const house$ = new Subject()
const houseCount$ = house$.scan((acc, num) =& acc + num, 0).startWith(0)
// 工资始终不涨
const salary$ = Observable.interval(100).mapTo(2)
const rent$ = Observable.interval(3000)
.withLatestFrom(houseCount$)
.map(arr =& arr[1] * 5)
// 一买了房,就没现金了……
const income$ = Observable.merge(salary$, rent$)
const cash$ = income$
.scan((acc, num) =& {
const newSum = acc + num
const newHouse = Math.floor(newSum / 100)
if (newHouse & 0) {
house$.next(newHouse)
return newSum % 100
// houseCount$.subscribe(num =& console.log(`houseCount: ${num}`))
// cash$.subscribe(num =& console.log(`cash: ${num}`))
&/code&&/pre&&/div&&p&这段代码所表达出来的业务关系如图:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&
房子数量 &——— 新购房
&/code&&/pre&&/div&&p&注意:在这个例子中,house$的处理方式与众不同,因为我们的业务逻辑是环形依赖,至少要有一个东西先从里面拿出来占位,后续再处理,否则没有办法定义整条链路。&/p&&h2&小结&/h2&&p&本篇通过一些简单例子介绍了RxJS的使用场景,可以用这么一句话来描述它:&/p&&blockquote&&p&其文简,其意博,其理奥,其趣深&/p&&/blockquote&&p&RxJS提供大量的操作符,用于处理不同的业务需求。对于同一个场景来说,可能实现方式会有很多种,需要在写代码之前仔细斟酌。由于RxJS的抽象程度很高,所以,可以用很简短代码表达很复杂的含义,这对开发人员的要求也会比较高,需要有比较强的归纳能力。&/p&&p&本文是入职蚂蚁金服之后,第一次内部分享,科普为主,后面可能会逐步作一些深入的探讨。&/p&&p&蚂蚁的大部分业务系统前端不太适合用RxJS,大部分是中后台CRUD系统,因为两个原因:整体性、实时性的要求不高。&/p&&p&什么是整体性?这是一种系统设计的理念,系统中的很多业务模块不是孤立的,比如说,从展示上,GUI与命令行的差异在于什么?在于数据的冗余展示。我们可以把同一份业务数据以不同形态展示在不同视图上,甚至在PC端,由于屏幕大,可以允许同一份数据以不同形态同时展现,这时候,为了整体协调,对此数据的更新就会要产生很多分发和联动关系。&/p&&p&什么是实时性?这个其实有多个含义,一个比较重要的因素是服务端是否会主动向推送一些业务更新信息,如果用得比较多,也会产生不少的分发关系。&/p&&p&在分发和联动关系多的时候,RxJS才能更加体现出它比Generator、Promise的优势。&/p&
RxJS是一个强大的Reactive编程库,提供了强大的数据流组合与控制能力,但是其学习门槛一直很高,本次分享期望从一些特别的角度解读它在业务中的使用,而不是从API角度去讲解。RxJS简介通常,对RxJS的解释会是这么一些东西,我们来分别看看它们的含义是什么…
&b&&u&人工智能很可能导致人类的永生或者灭绝,而这一切很可能在我们的有生之年发生。&/u&&/b&&br&&br&&br&上面这句话不是危言耸听,请耐心的看完本文再发表意见。这篇翻译稿翻译完一共三万五千字,我从上星期开始翻,熬了好几个夜才翻完,}

我要回帖

更多关于 共享奢饰品 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信