开始

官网
https://uniapp.dcloud.io/

课程地址: web官网已经搜不到当前课程。
https://coding.m.imooc.com/classindex.html?cid=433
image.png

uni-starter

应用开发无需从0开始,uni-starter助力节省2人月
https://mp.weixin.qq.com/s?__biz=MjM5NjExMjc4NA==&mid=2649705083&idx=1&sn=ad45a0f68cc39068a3466e8ccd74b186&chksm=bef5da8489825392206ea22cbbd004411386155b8b36665f17a141ab70857cc66a54ee53ac8b&mpshare=1&scene=23&srcid=1108JqbLfvdC8aXq9uJglnkq&sharer_sharetime=1636375233692&sharer_shareid=63e87f726f4c87f6f60955b60994393e#rd

本机路径
D:\wjw\学习中\uni-app入门到实战 以项目为导向 掌握完整开发流程-wjw

创建的测试项目在本机的路径
C:\Users\wjw\Documents\HBuilderProjects\test

uni-cloud视频教程

https://uniapp.dcloud.io/uniCloud/

https://www.bilibili.com/video/BV17p4y1a71x?p=1

遇到的问题

CloudPath不合法

解决方法
https://www.yuque.com/meirenchangduancuiyanlou/pawgo4/sver3e#jyFy0
cloudPath传文件的名称就可以了。

数据初始化文件

D:\wjw\学习中\uni-app入门到实战 以项目为导向 掌握完整开发流程-wjw\0资料\imooc-news-master\imooc-news\cloudfunctions-aliyun\db_init.json

云数据库初始化教程参考官方文档
https://uniapp.dcloud.io/uniCloud/hellodb.html#dbmigration

  1. // 对象的 key 为集合名称
  2. // key.data 是集合的记录数据,数组内一项为一条数据
  3. // 以部分数据、部分字段为例
  4. {
  5. "article": { // 文章表
  6. "data": [{
  7. "id": "302042",
  8. "title": "疫情风险下的项目管理与紧急预案策略",
  9. "browse_count": 173,
  10. "collection_count": 5,
  11. "comments_count": 0,
  12. "author": {
  13. "id": "8010388",
  14. "author_name": "Java架构师讲师团",
  15. "avatar": "//img1.sycdn.imooc.com/5dafce1a00013fd501400140-160-160.jpg",
  16. "status": "normal"
  17. },
  18. "classify": "后端开发",
  19. "thumbs_up_count": 5,
  20. "create_time": "2020.03.16 17:50",
  21. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><p>大家好,我是风间影月,慕课网<a href=\"https://class.imooc.com/sale/javaarchitect?mc_marking=c6615303b3175f848fccdb52abb833ec&mc_channel=shouji\">《Java架构师成长直通车》</a>课程架构师讲师团成员之一。这篇文章我们一起来聊一聊疫情风险管理。</p>\n</div><div class=\"cl-preview-section\"><h3>引子</h3>\n</div><div class=\"cl-preview-section\"><p>相信大家都经历了疫情,在疫情之下,企业推迟复工,多地封城封路,各行各业都收到了冲击,甚至很多企业都因此倒闭,那么在企业项目管理中,有一点我们需要把控的就是风险管理,遇到风险如何更好的去调控,把损失降低。<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843495928674.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843495928674.jpg\" alt=\"-w677\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>作为项目经理,应该如何面对,如何处理,如何协调资源,这些都是作为一名PM所需要去考虑的。</p>\n</div><div class=\"cl-preview-section\"><p>当各个企业收到延长春节并延迟复工的消息以后,很多企业的项目都停止了,无法正常运作,尤其是一些传统企业收到的打击最大,比如线下物流,工厂等,而互联网企业相对好一些,项目都是在线的,而且大家可以在家远程办公就能整合到一起。(像我们的线下物流就完全停了,对接的工厂端也完全停了,工厂停了,环境监测系统也用不着了,一连串的连带效应)<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496113076.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496113076.jpg\" alt=\"-w638\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>其实不论线上还是线下,项目进度肯定会延期,资源成本提升,这么一来很多项目都是亏本的,甚至还会因为合同而赔钱。那么这个时候项目就应该不能按照原计划进行,而需要适当进行变更,因为这是外部的不可抗力的因素。项目经理首先最要考虑的就是干系人管理,尤其是你的客户,管理客户,打好招呼,伺候好,维护好现有关系,那么项目上的后续事宜可以更好的开展。</p>\n</div><div class=\"cl-preview-section\"><h3>认清风险</h3>\n</div><div class=\"cl-preview-section\"><p>一般项目初期在规划项目编撰合同的时候,相关的风险都应该去有所考虑以及有应对的预案,举几个最简单的例子:</p>\n</div><div class=\"cl-preview-section\"><ul><li>项目开展过程中人员流动或者疾病风险\n<ul><li>招储备人员(外包公司叫做on bench)</li>\n<li>激励政策,也就是TeamBuilding,团队融洽度,每月一次TB,每季度一次项目分红(我们是1500/人)</li>\n</ul></li>\n<li>服务器/硬件设备风险\n<ul><li>自建机房要有容灾预案</li>\n<li>云服务器要有多地主备</li>\n</ul></li>\n<li>成本风险\n<ul><li>项目中涉及到的采购供应,成本管理需要做好,预算多少,会否超支,有没有可能因为投入越来越多而导致项目亏损,或者盈利不多,需要提前规划和准备。</li>\n</ul></li>\n<li>第三方风险\n<ul><li>一个项目中可能会有20%-40%甚至更多的内容会外包给第三方公司,第三方有没有可能跑路?资质够不够?可以多找几家对比,或者额外找一家做备胎。又或者找两三家一起合作,不把鸡蛋放在一个篮子里。并且要定期验收,定期检查质量。<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496291926.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496291926.jpg\" alt=\"-w911\" style=\"width:100%;\"></li>\n</ul></li>\n</ul></div><div class=\"cl-preview-section\"><p>那么像这次疫情,不能归类为普通的风险,应该定性为<code>不可抗力风险</code>,因为你预测不到,而且有时候政府的一个突发性政策就会导致项目无法进行(比如我们的物流项目,和政策息息相关,所以我们很多高层每个季度都要去相关部门参加各种培训,甚至一些资质认证的考试。从而伪实时的跟着政策走)。所以应该纳入免责条款,所以合同里应该要写清,如果不写清除,会有后期的各种纠纷。就比如你公司被人炸了,被陨石击中了,这些都是外部的不可抗力,一般人还真遇不到。像N年前,有个哥们车子被陨石击中,保险公司不赔,为啥,因为这是不可抗力因素呀!<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496433035.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496433035.jpg\" alt=\"-w799\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>如果是平时的风险,一般来说PM或者PMO能去把控或者承担风险责任,但是想这次疫情对所有企业的袭击,就会上升到企业层面,由企业来承当整个公司的风险后果,所有高层都应该出来协调各方项目资源、项目整合、与项目管理。为什么要这么做,这个其实就是统一整合式管理,因为所有的项目全部受影响,就需要统一的整合了,保证一致性。<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496546451.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496546451.jpg\" alt=\"-w764\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><h3>审查风险</h3>\n</div><div class=\"cl-preview-section\"><p>分析在这次风险中的各个环节,对比原有计划所存在的出入点。比如:</p>\n</div><div class=\"cl-preview-section\"><ul><li>特殊物流运输会减少流量\n<ul><li>工厂需求减少</li>\n<li>大型物流车停滞所产生的消耗费(一天不开车,每辆亏损上千)</li>\n</ul></li>\n<li>交通管制导致原有特殊导航系统不可用,油费额外增多</li>\n<li>员工需要自我14天隔离的费用</li>\n<li>每日口罩成本损耗</li>\n<li>远程办公可能会带来的额外成本\n<ul><li>微信视频会议等都免费</li>\n<li>国外的一些团队协作工具收费<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496657408.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496657408.jpg\" alt=\"-w600\" style=\"width:100%;\"></li>\n</ul></li>\n</ul></div><div class=\"cl-preview-section\"><h3>应对风险</h3>\n</div><div class=\"cl-preview-section\"><p>不管遇到任何风险,第一个要做的就是上报,注意,不要越级上报,你是PM,你上面有PMO,所要先汇报给PMO,如果没有PMO就汇报给你的直属经理,比如部门经理或者部门总监。在这个过程中,要响应政府号召,比如排查啊隔离啥的,每日体温测量,人员动向,如果有异常情况,也要及时上报。那么作为项目经理,就需要根据实际情况来适当调整原项目进度计划,适当延后,和参与的相关干系人讨论方案,与客户协调沟通。<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496759816.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496759816.jpg\" alt=\"-w701\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>随后,你要看风险是否可控,是否可以规避,常规的你的下属要离职,那么你就去谈谈,能不能挽留,能不能协调公司资源给他涨薪,了解其诉求,解决其诉求。如果是一些额外成本费用,那么可能还是需要通过上报批准,当然如果小成本你要自己解决也行。像这次疫情,对于一些不可继续的项目,只能停止复工,往后延期延迟,硬着头皮偷偷上一旦被查会承担一些法律责任。比如我们有很多车队因为交通管制无法上路,每天每辆车的停车费用有很多,需要和司机和管理处协调,谈判能否降低停车成本,降低企业成本。<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496892846.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496892846.jpg\" alt=\"-w679\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>第三,转移风险,降低风险影响,一般来说就是弥补,最常见的就是保险业务。我们每辆车都投了巨大的保额,为啥?因为全国这么多车,一旦发生车祸,人员伤亡,你都处理不过来,这个时候就需要有保险的介入,以此把风险转移给保险公司,以最小的时间人力成本来降低风险。那么对于这次疫情,作为PM,需要回顾一下所有的实体资源,看看是否有一些保险可以弥补一定的损失。我们审查以后,对于司机的健康保险是可以有一定的弥补的,有总比没有好。<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843497052573.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843497052573.jpg\" alt=\"-w603\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>第四,主动降低风险,这个其实很简单,就是响应号召,戴口罩,少出门,勤洗手,多消毒,降低自身感染病毒的风险。</p>\n</div><div class=\"cl-preview-section\"><p>第五,接受风险,这个分为主动和被动之分:</p>\n</div><div class=\"cl-preview-section\"><ul><li>主动:实行远程办公,线上的一些项目都可以采取这种方式,互联网企业是较多的,还有一些自媒体啥的,这些完全可以在家里办公,而且可以利用远程协作工具来开展团队管理。但是也要注意,PM要每天定时跟踪每位团队成员的健康度,比如测量体温,观察身体异常。</li>\n<li>被动:这个就没办法了,线下的传统企业几乎都栽进去了,我们必须响应号召,延迟复工,工厂不得开,要开也要得报备审查。如果不行只能停止运作。<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843497175683.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843497175683.jpg\" alt=\"-w683\" style=\"width:100%;\"></li>\n</ul></div><div class=\"cl-preview-section\"><p>第六,要提交应急风险变更通知,因为项目整体肯定会受到影响,所以原计划是肯定要做出变动的,比如最大的成本开支,以及预算,都要跟着市场资源做变动,变动了才能有效调控。如果资源没有,或者成本实在太高,影响项目利润,那么就只能暂缓延迟。</p>\n</div><div class=\"cl-preview-section\"><h3>不可抗力的风险能否免责</h3>\n</div><div class=\"cl-preview-section\"><p>对于类似的不可抗力风险,造成项目滞后,成本超支,项目亏损,企业亏损,能不能获得一定的索赔呢?</p>\n</div><div class=\"cl-preview-section\"><p>对于合同规定,是有不可抗力因素的,如果因为这次疫情,是可以免除部分或者全部的责任,比如常规的,延期赔偿100万,那么这个时候100万完全可以免责。合同双方不能对彼此提出索赔等要求。<br><img class=\"lazyload\" src=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843499344907.jpg\" data-original=\"https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843499344907.jpg\" alt=\"-w1015\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><h3>疫情后的市场机遇</h3>\n</div><div class=\"cl-preview-section\"><p>疫情带来巨大影响,也会带来更大的机遇,比如说把一些线下的发展为线上的:</p>\n</div><div class=\"cl-preview-section\"><ul><li>游戏\n<ul><li>这没啥好说的,不管啥时候都很火,在家没事天天打游戏。</li>\n</ul></li>\n<li>在线医生\n<ul><li>遇到问题,线上诊断,小毛病自己解决,大毛病还是去医院</li>\n</ul></li>\n<li>社区电商\n<ul><li>很多人不出去买菜,有些社区电商崛起了,我朋友从送菜的变成了总代理,吊炸天</li>\n</ul></li>\n<li>短视频\n<ul><li>几亿人的娱乐场所,爸爸妈妈们的茶余饭后</li>\n</ul></li>\n<li>在线教育\n<ul><li>正所谓停课不停学,不去学校上课,老师们在直播间互动啊</li>\n</ul></li>\n<li>健康管家,生物科学\n<ul><li>比如苹果的健康应用,可以检测你的心率啊呼吸啊啥的,如果有问题会提醒你,甚至在你遇到危机的时候自动拨号求救。</li>\n</ul></li>\n<li>远程办公,异地办公\n<ul><li>这个不用多说了,很多人都在家办公,开会、培训,团队协作少不了。</li>\n<li>异地办公,这个是长期在家,很多国外公司就是这样,甚至员工分布在各国,每年去总部一次的,也有。</li>\n</ul></li>\n</ul></div>\n\t\t\t\t\t",
  22. "cover": [
  23. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843495928674.jpg",
  24. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496113076.jpg",
  25. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496291926.jpg",
  26. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496433035.jpg",
  27. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496546451.jpg",
  28. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496657408.jpg",
  29. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496759816.jpg",
  30. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843496892846.jpg",
  31. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843497052573.jpg",
  32. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843497175683.jpg",
  33. "https://lee-blog.oss-cn-shanghai.aliyuncs.com/2020/03/16/15843499344907.jpg"
  34. ],
  35. "mode": "column"
  36. },
  37. {
  38. "id": "301911",
  39. "title": "《Java架构师成长直通车》免费直播课开讲啦(3月18号)",
  40. "browse_count": 293,
  41. "collection_count": 1,
  42. "comments_count": 0,
  43. "author": {
  44. "id": "8010388",
  45. "author_name": "Java架构师讲师团",
  46. "avatar": "//img1.sycdn.imooc.com/5dafce1a00013fd501400140-160-160.jpg",
  47. "status": "normal"
  48. },
  49. "classify": "后端开发",
  50. "thumbs_up_count": 1,
  51. "create_time": "2020.03.13 11:39",
  52. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><h1>直播课题:《 深入探究线程池,攻克面试高频难点》</h1>\n</div><div class=\"cl-preview-section\"><p>Hello,各位小伙伴大家好:</p>\n</div><div class=\"cl-preview-section\"><p>我是慕课网<a href=\"https://class.imooc.com/sale/javaarchitect\">《Java架构师成长直通车</a>》课程的讲师团成员-阿神,很高兴又和大家见面了。<br>\n<br>\n我们知道并发编程一直都是Java的核心技术点,也是面试的高频考点,更是很多同学长久以来一直搞不明白的重难点,即使工作好多年,对这块内容掌握的也不是很到位(虽然这块只是Java 的基础)。</p>\n</div><div class=\"cl-preview-section\"><p>虽然说Java并发编程有很多的知识点要学习,不过在我看来要学好Java并发编程,关键在于优先搞透线程池,而在线程池中,ThreadPoolExecutor类是其最核心的一个类,因此如果要透彻地了解Java中的线程池,就必须先了解这个类以及与之相关的一些核心概念,如:线程池状态、任务的执行、线程池中的线程初始化、任务缓存队列及排队策略、任务拒绝策略、线程池的关闭、线程池容量的动态调整等。</p>\n</div><div class=\"cl-preview-section\"><p>所以3月18日(周三)晚8点的这堂直播课,我就将带大家一起深度剖析ThreadPoolExecutor类的底层源码和实现原理,一起来扎实Java并发编程功力,一起攻克Java并发编程核心重难点。</p>\n</div><div class=\"cl-preview-section\"><h1>课程介绍</h1>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5e61e1880001508106401138.png\" data-original=\"https://img1.sycdn.imooc.com/5e61e1880001508106401138.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><h1>参与方式</h1>\n</div><div class=\"cl-preview-section\"><ol><li>\n<p>添加琳琳微信好友,微信号:mkw-jiagoushi</p>\n</li>\n<li>\n<p>添加上好友后,琳琳会邀请你进入直播群中</p>\n</li>\n<li>\n<p>直播前一天琳琳将发给大家直播间地址</p>\n</li>\n<li>\n<p>直播过程中更有多重福利及红包雨,记得多多关注</p>\n</li>\n</ol></div>\n\t\t\t\t\t",
  53. "cover": [
  54. "https://img1.sycdn.imooc.com/5e61e1880001508106401138.png"
  55. ],
  56. "mode": "base"
  57. },
  58. {
  59. "id": "298045",
  60. "title": "重塑程序员职业发展观",
  61. "browse_count": 6460,
  62. "collection_count": 93,
  63. "comments_count": 15,
  64. "author": {
  65. "id": "4294850",
  66. "author_name": "7七月",
  67. "avatar": "//img1.sycdn.imooc.com/54584e2c00010a2c02200220-160-160.jpg",
  68. "status": "normal"
  69. },
  70. "classify": "后端开发",
  71. "thumbs_up_count": 93,
  72. "create_time": "2019.12.19 12:04",
  73. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><p>大家好我是7七月今天给大家带来我在慕课网的一次分享。分享仅代表个人观点大家听听就好希望能帮助到大家。</p>\n</div><div class=\"cl-preview-section\"><h3>本次分享的主题是 关于程序员的 职业规划希望能帮助大家能开心的度过20岁幸福的过好30岁</h3>\n</div><div class=\"cl-preview-section\"><p>先给大家讲个故事2019年英雄联盟S9冠军FMVP小天在2018年时还默默无闻没有LPL的队伍要他一个人每天独自住在地下室里孤独的打Rank每月薪水大概只有5000块。这与他同期的选手相比简直是少的可怜。直到FPX选中他才有了今天的FMVP。</p>\n</div><div class=\"cl-preview-section\"><p>小天还是幸运的能在LPL中打比赛的选手年薪不会少于100万更何况是获得了世界赛冠军的选手薪水更是不敢想象这还不包括各种商业活动、直播所带来的收益。</p>\n</div><div class=\"cl-preview-section\"><p>但这令人艳羡的收入背后是需要付出及其残酷的努力的残酷到令人可怕。</p>\n</div><div class=\"cl-preview-section\"><p>职业选手的训练是强度是非常高的一天十几个小时的训练+Rank是常有的事儿。很多人觉得打游戏很开心但真的让你一天打十几个小时一般人是受不了的。每天中午起床、训练、比赛、rank一直到深夜凌晨第二天再重复枯燥且乏味。一年也大概只有在春节的冬歇期会有一个月的休息时间。很多选手因为常年的训练腰部、背部、颈椎、手都有不同程度的伤病。</p>\n</div><div class=\"cl-preview-section\"><p>即使这样的付出也很难站在LPL的顶级联赛上。LPL只有17个队伍能活跃在这个顶级联赛的选手全国只有不到100人。职业选手之间的竞争远比其他运动要残酷太多了。更何况职业选手的黄金年纪最多只有5年从18岁到23岁有的选手甚至更短。这比足球、篮球等运动的黄金年纪要短太多了。</p>\n</div><div class=\"cl-preview-section\"><p>没有哪个职业是轻松的在那些耀眼光环的背后其实是无尽的辛酸、泪水与汗水。要想获得成功刻苦的付出是最最基本的要求。</p>\n</div><div class=\"cl-preview-section\"><p>恕我直言我们现在很多人的刻苦的程度是远远不够的。</p>\n</div><div class=\"cl-preview-section\"><p>但我并无意怂恿各位同学都像职业选手这样刻苦。每个人对待生活的要求是不同的有很多人本身对物质要求不高也不愿意去付出努力这并不能说他们有什么问题。生活并不是只有物质才是唯一的快乐。</p>\n</div><div class=\"cl-preview-section\"><p>做一个有趣的人也是个不错的选择</p>\n</div><div class=\"cl-preview-section\"><h3>但人生最可悲的是既有太高物质追求又不肯付出相应的努力。夹在两端是焦虑的本源。</h3>\n</div><div class=\"cl-preview-section\"><p>所以同学们需要想清楚要追求的是什么如果是追求物质那必然要付出极其艰辛的努力除非家里有矿</p>\n</div><div class=\"cl-preview-section\"><p>所以不管你以后想做什么认清自己看看自己愿不愿意为物质付出这么多的辛苦才是最重要的事情。只有认清这一点才能为自己选择一条合适的路。</p>\n</div><div class=\"cl-preview-section\"><p>就如同我们之前谈到的电竞选手即使你一年四季刻苦训练都不一定能站在LPL这个最高级别的联赛上所以刻苦的努力只是一个基本条件还需要有天赋更需要有一些运气。</p>\n</div><div class=\"cl-preview-section\"><p>刻苦+天赋+运气要想获得成功这三者其实缺一不可。</p>\n</div><div class=\"cl-preview-section\"><p>经常有学生会问我编程需不需要天赋</p>\n</div><div class=\"cl-preview-section\"><p>不得不承认其实是需要一些的。天赋可以决定你最终在编程路上的高度像C#之父、Linux之父这些天才之人毫无疑问是有天赋的。不是每个人都能通过努力达到这样的高度。</p>\n</div><div class=\"cl-preview-section\"><p>但人生有趣的地方就在于我们并不想达到这些程序天才的高度我们绝大多数的人其实也只是想让自己通过编程吃饱、穿暖过上小康的生活</p>\n</div><div class=\"cl-preview-section\"><h3>鸡汤告诉我们只要你努力没啥做不到的。这其实不现实。就算做到了也未必是你真的想要的。所以最关键的就是认清自己没必要去做那些付出很多却收益很低的事情。</h3>\n</div><div class=\"cl-preview-section\"><p>就拿程序界的LPL联赛——BAT来说其实国内几百万程序员能在BAT的程序员其实少之又少。所以你确定你拼劲全力就一定能进去吗技术是一方面更多的时候其实进BAT还需要一些运气。</p>\n</div><div class=\"cl-preview-section\"><p>能进去固然很棒但进不去也大可不必觉得人生就没有了希望。很多中小公司的程序员过的未必比BAT差。当然进BAT是很多程序员的执念这个就另当别论了。</p>\n</div><div class=\"cl-preview-section\"><p>我这篇分享没办法帮助大家进BAT但如果你认真看它至少可以让你在程序这条路上走的更远生活过的不会太差。</p>\n</div><div class=\"cl-preview-section\"><p>所以认清自己是大家在规划自己职业路径的时候首先要明确的事情。只有在明确了这一点我们后面的分享才有意义。</p>\n</div><div class=\"cl-preview-section\"><p>我们程序员的职业生涯大概是从20岁开始。20岁我们都在干什么也许我们还在校园的象牙塔里挥洒青春也许我们已经走出了校园进入了职场</p>\n</div><div class=\"cl-preview-section\"><h3>如果我们还在校园里那最重要的其实只有2件事情</h3>\n</div><div class=\"cl-preview-section\"><ul><li>\n<p>好好学习</p>\n</li>\n<li>\n<p>谈个女朋友/男朋友</p>\n</li>\n</ul></div><div class=\"cl-preview-section\"><p>没有比这个更重要的事儿了</p>\n</div><div class=\"cl-preview-section\"><p>如果你所在的学校属于一本以上那我的建议是专注计算机基础理论的学习包括操作系统、计算机原理、算法和数据结构、Java语言的学习。项目可以少做甚至不做。大厂校招是会考虑学校的。</p>\n</div><div class=\"cl-preview-section\"><p>如果你所在的学校不是很好那么应该把实践(项目)放在第一位同时你的期望值必须要调低一点否则你会很焦虑焦虑不是件好事儿。</p>\n</div><div class=\"cl-preview-section\"><p>如果你所在的学校不是很好那么应该把实践(项目)放在第一位同时你的期望值必须要调低一点否则你会很焦虑焦虑不是件好事儿。</p>\n</div><div class=\"cl-preview-section\"><h3>换句话说没有一个好的学历那就必须拿作品来说话作品是一个程序员最好的自描述比你那些花里胡哨的简历要实在很多。很多程序员说没有好的项目这是骗自己无论是参与开源项目还是自己尽可能的11模仿优秀的互联网项目这都是很好的做法。当然最好的还是能参与一些开源的项目。</h3>\n</div><div class=\"cl-preview-section\"><p>这一条同样适用于现在工作不是太好想跳槽去BAT的同学</p>\n</div><div class=\"cl-preview-section\"><p>用能放的上台面的纸面实力弥补学历的不足是同学们去大厂的最好方式。一定要看得见有说服力。如果你有一个Star在2000+的项目其实去大厂很轻松。</p>\n</div><div class=\"cl-preview-section\"><p>这里强烈建议大学生可以学习一下Java这对培养自己的编程思维非常有好处因为Java这个语言是经典编程思维的代表而很多经典编程思维动态语言是不强调或者说非常容易忽略。很多后端能够很轻松的成为全栈开发者的原因是因为他们是从Java开始的编程之路从Java入手学习别的语言比较轻松但反过来从动态语言入手再想学习Java、Go等语言就不是那么轻松。</p>\n</div><div class=\"cl-preview-section\"><p>Java、C#、C++ 三者任意一个即可</p>\n</div><div class=\"cl-preview-section\"><p>一定要选择 这类经典的编程语言</p>\n</div><div class=\"cl-preview-section\"><p>考虑到实用性目前也只有Java最合适</p>\n</div><div class=\"cl-preview-section\"><p>Python和JS都不是最好的首选语言作为二语言最合适。</p>\n</div><div class=\"cl-preview-section\"><h3>为什么 建议是这3个语言。因为这些语言有很多 跨语言的编程思维</h3>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"http://img2.sycdn.imooc.com/5dfae4a50001a51905000256.jpg\" data-original=\"http://img2.sycdn.imooc.com/5dfae4a50001a51905000256.jpg\" alt=\"http://img1.sycdn.imooc.com/5dfae4a50001a51912800653.jpg\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>这是Java中的思维但其实是软件工程中的思维不论是JS还是Python其实都有这样的需求</p>\n</div><div class=\"cl-preview-section\"><p>不会因为是动态语言就不需要</p>\n</div><div class=\"cl-preview-section\"><p>很多同学看不懂源码的根本问题 不是在于自己太菜了 语法不熟悉</p>\n</div><div class=\"cl-preview-section\"><p>还是 这些 软件工程的思想不熟悉不懂得为什么好的框架代码都要写的很复杂</p>\n</div><div class=\"cl-preview-section\"><p>这个时代一个程序员掌握多种语言是再稀松平常不过的事儿了。</p>\n</div><div class=\"cl-preview-section\"><h3>追求多语言不是目的但一个人能够灵活应用多种语言是一种能力的体现</h3>\n</div><div class=\"cl-preview-section\"><p>如果在工作中有条件和能力的话也应该补习下Java。很多在前端同学看起来非常神奇的思维和机制其实在后端编程里是非常常见的比如一些设计模式、IOC容器等概念。</p>\n</div><div class=\"cl-preview-section\"><p>很多人说算法是好程序员和普通程序员的分手岭这句话放在过去是正确的。但现在对于软件这种高度封装的东西要求实在太高了</p>\n</div><div class=\"cl-preview-section\"><h3>思想和设计模式才是 最合适的分手岭</h3>\n</div><div class=\"cl-preview-section\"><p>如果在大学时能少玩游戏多学习一些编程技术差不多是真可以积累2到3年的工作经验出来找工作是比其他人有很大的优势的。主要是现在大学不学习的人太多大家要把握好这个机会。</p>\n</div><div class=\"cl-preview-section\"><p>我们是技术人技术自然是要过硬的。所以在我们的20岁的时候是最好的学习技术的时机。在这个年纪大多数人没有家庭的负担也没有太大的经济压力如果这个时候不去认真学习那么30岁后就没有这个环境和心境了。</p>\n</div><div class=\"cl-preview-section\"><h3>很多同学经常问我老师我觉得我很迷茫我不知道怎么学我也不知道到底要学到什么程度。这个问题在我看来它本质是因为我们本末倒置了技术。技术是一个实践性很强的东西我们太多做技术的还抱着应试教育那种学习模式高考又不考编程并没有一个能决定你人生的考试等着你。</h3>\n</div><div class=\"cl-preview-section\"><p>拿我自己的经历来说我从不为找工作去学习技术。从我学习第一门语言开始我就是为了做点东西和解决问题。高中的时候是为了在文曲星上用Basic语言写个RPG小游戏给自己玩大学时是为了给老师做项目工作了也是为了去解决一个个的问题在解决问题中去学习编程。</p>\n</div><div class=\"cl-preview-section\"><p>但我才工作时写程序到凌晨3、4点是再正常不过的事情了。驱动我的并不是 薪水也不是为了完成任务而是一份成就感我把工作的项目当自己的作品。我的起点就别很多人要高</p>\n</div><div class=\"cl-preview-section\"><p>没有目标的学习确实很容易迷茫。而且很容易学了就忘记了学了也不知道学的到底是个啥因为你没有把自己学习的东西用到实践中。</p>\n</div><div class=\"cl-preview-section\"><p>你需要考虑的是技术的转化上班打工这只是转化的一种途径其实还有很多。</p>\n</div><div class=\"cl-preview-section\"><p>技术要服务于 收益而有了收益反过来 是可以促进你的技术成长的。</p>\n</div><div class=\"cl-preview-section\"><p>这是双向的而不是单向的</p>\n</div><div class=\"cl-preview-section\"><h3>很多同学说我就是不知道目标是什么。这很简单你完全可以参与一个开源项目或者自己给自己虚拟一个项目甚至是可以在慕课购买一个课程以这个课程的项目为雏形不断丰富和完善。我就见过一个同学学了我的电商课程后人家硬是不断的扩展不断的追加新的功能最终做出了一个中等规模的电商小程序有团购、有秒杀。团购的业务你不知道吗秒杀的业务你不知道吗所以说没目标没项目都是借口呀同学们。</h3>\n</div><div class=\"cl-preview-section\"><p>有一个同学看了我的KOA课程后自己去研究了我的开源项目Lin-CMS做了一套完整的应用程序。我其实挺佩服这些同学的这才是真的把一个课程的价值发挥到了极致。</p>\n</div><div class=\"cl-preview-section\"><p>而我们大部分同学仅仅只是 被动的把课看完了并没有动手去做 一些自己想做的东西。技术这东西其实很纯粹无非有两个途径1. 打工赚钱。2.做自己想做的东西</p>\n</div><div class=\"cl-preview-section\"><p>第一个 用处自然是显而易见的但第二个用处很多同学 没有意识到。</p>\n</div><div class=\"cl-preview-section\"><p>如果你依然还是在用教育都已经抛弃的应试态度在学编程那还是建议三思这是走不远的。</p>\n</div><div class=\"cl-preview-section\"><p>编程技术浩如烟海你指望靠学去学完 学好那是不可能的</p>\n</div><div class=\"cl-preview-section\"><p>20到30这10年时间其实是非常短暂的。以前我们谈35岁的程序员危机但现在由于程序的门槛越来越低30岁就是程序员的危机了。不是说30岁和29岁有多大差别其实在生理上没有太大差别但在心里上差别是巨大的。这不仅仅是企业对30岁人的态度你自己其实也会觉得30岁是一个巨大的门槛你已经不再年轻了。</p>\n</div><div class=\"cl-preview-section\"><p>30岁时如果再想着靠技术吃饭基本不太现实。比你便宜的年轻人有你30岁要求的薪水不可能和20岁的人一样你有妻子有孩子怎么可能期望和20岁一样。你觉得你和20岁的人比你的优势是什么经验很抱歉经验其实对于程序员这个行业来说没有那么大的鸿沟因为软件在现代来说已经模式化了它不是高精尖的产业你的经验可能一文不值。</p>\n</div><div class=\"cl-preview-section\"><h3>所以30岁要拼的是综合素质包括沟通能力、领导能力、管理能力、分析问题的能力你的经验只有结合这些能力才能值钱你之前的技术积累只有结合这些能力才能继续发光发热。</h3>\n</div><div class=\"cl-preview-section\"><p>很多做技术的人心里是瞧不起做管理的人的我以前也是觉得技术是纯粹的管理能干啥但最后随着年岁渐长我明白了一个道理想有更高的回报就需要提供给企业和社会更大的价值。一个人只做技术的价值是有限的但管理不同管理可以让一群人的价值最大化这也是为什么管理的薪资肯定是要比普通技术要高不少的原因。</p>\n</div><div class=\"cl-preview-section\"><p>那这些能力怎么培养这些看似很虚却又很重要的能力其实并没有一个像技术一样的学习路线。很多同学说我20岁还年轻我应该先学技术等我技术学好了再考虑这些问题。大错特错。</p>\n</div><div class=\"cl-preview-section\"><h3>这些能力的培养或者说综合素质的培养不要说20岁还年轻这些其实是应该从小就应该培养的。很多时候等我达到了什么目标等我生活安定了等我先找到工作了了我再干什么事情这样的想法和放弃是没区别的。</h3>\n</div><div class=\"cl-preview-section\"><p>我以前做直播的时候跟大家聊说综合素质和重要我推荐大家在学习之余需要多去读书不是读技术书而是读文学、历史。当你的综合素质提高了其实你会发现这些能力的获取途径自然就清晰了。综合素质可以影响我们的很多选择而人生其实就是由一个个选择的结果堆积起来的一次选择会影响到下一次正确的价值观和人生观会影响你每一次选择。</p>\n</div><div class=\"cl-preview-section\"><p>大家有没有想过为什么同样的两个人在同一家企业为什么一个人的薪水可以比另一个高出1倍甚至好几倍为什么每次加薪其他人的幅度都比你高我带过不少团队我心里很清楚是为什么技术绝对不是主要的区别点。都到了一个篮子里了大家耳濡目染朝夕相处谁还比谁的技术能差多少其实主要还是有的人综合素质高能把各种事情处理的仅仅有条又有自己的原则简单来说就是做事儿做的漂亮。</p>\n</div><div class=\"cl-preview-section\"><p>这样在企业里KPI肯定高不了也不可能有前途。没有领导喜欢这样的下属领导的事儿很多的你指望他每一个细节都给你指出来那和他自己去做有啥区别为什么还要你去做。你能帮你的leader省心你在企业里就不会差无论是什么公司都是这个道理。这肯定不是仅仅只有技术就可以办到的真需要大家提高综合素质</p>\n</div><div class=\"cl-preview-section\"><p>其实各行各业都对综合素质要求很高但往往程序员这个职业有点另类大多数人不看重综合素质只是一味的猛学技术。</p>\n</div><div class=\"cl-preview-section\"><p>当然我从不否认技术的重要性只是我们现在很多同学太盲目的追求技术了。但如果你仔细观察身边的人很多技术好的人综合素质其实不差。因为综合素质其实可以帮助你更高效的学习让你知道怎么学习该学哪些不该学哪些。这无疑是提高了学习的效率。简答点来说综合素质可以帮你做出最符合你利益的选择。</p>\n</div><div class=\"cl-preview-section\"><p>综合素质高编程思维其实自然是有的</p>\n</div><div class=\"cl-preview-section\"><p>相信我在不同的课程和分享中其实一直都在强调综合素质甚至是编程思维相比综合素质来说都没有那么重要</p>\n</div><div class=\"cl-preview-section\"><p>但什么是综合素质我觉得很难用语言来描述。我给大家看个文档来说明什么是综合素质在这里插入图片描述</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dfaf472000141b612800910.jpg\" data-original=\"//img1.sycdn.imooc.com/5dfaf472000141b612800910.jpg\" alt=\"图片描述\" style=\"width:100%;\"><br><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dfb073a0001b38512800896.jpg\" data-original=\"//img1.sycdn.imooc.com/5dfb073a0001b38512800896.jpg\" alt=\"图片描述\" style=\"width:100%;\"><br><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dfb074d00016a8412800899.jpg\" data-original=\"//img1.sycdn.imooc.com/5dfb074d00016a8412800899.jpg\" alt=\"图片描述\" style=\"width:100%;\"><br>\n这个文档是 我们团队的一个人做的。完成这样的文档我其实只说了3句话需要有一个sql join的文档。样式做好点。逻辑清晰点</p>\n</div><div class=\"cl-preview-section\"><p>从两个角度来想你是leader你喜欢这样的员工吗</p>\n</div><div class=\"cl-preview-section\"><p>第二个角度如果是你来做你能做到这个程度吗</p>\n</div><div class=\"cl-preview-section\"><p>如果你有这样的能力不会愁未来</p>\n</div><div class=\"cl-preview-section\"><p>这其实就是综合素质的体现</p>\n</div><div class=\"cl-preview-section\"><p>其实各行各业都对综合素质要求很高但往往程序员这个职业有点另类大多数人不看重综合素质只是一味的猛学技术。</p>\n</div><div class=\"cl-preview-section\"><p>当然我从不否认技术的重要性只是我们现在很多同学太盲目的追求技术了。但如果你仔细观察身边的人很多技术好的人综合素质其实不差。因为综合素质其实可以帮助你更高效的学习让你知道怎么学习该学哪些不该学哪些。这无疑是提高了学习的效率。简答点来说综合素质可以帮你做出最符合你利益的选择。</p>\n</div><div class=\"cl-preview-section\"><p>选择比努力重要。有时候对于普通人对于没有逆天改命的能力的人来说真不是假话。</p>\n</div><div class=\"cl-preview-section\"><p>上面的这个文档其实很简单都是现成的资料但在实际工作中本身就不是让大家去弄高科技呀就是踏踏实实把事儿做好做漂亮</p>\n</div><div class=\"cl-preview-section\"><h3>所以你可以仔细观察很多高薪的人其实他们都有一个共同的特点就是能把事做好。能够最大限度的发挥资源的价值尽可能的把事情做到极致。</h3>\n</div><div class=\"cl-preview-section\"><p>所以企业需要什么样的员工这个答案太多了但我认为是可以概括一下的</p>\n</div><div class=\"cl-preview-section\"><p>企业需要的是能把事情做好的员工。很多同学在有人带着你、指导你、事无巨细的告诉你怎么做的情况下能把事做好但这句话其实更准确的来说是需要你主动的、独立的把事情做好。领导其实只应该起到一个协调资源和提供资源的角色怎么做做到什么程度是应该由你自己来决策的。</p>\n</div><div class=\"cl-preview-section\"><h3>好这个话题我们暂时先讲到这里。下面我谈下职业规划的问题。</h3>\n</div><div class=\"cl-preview-section\"><p>其实程序员未来的路相对于其他行业来说实在是太窄了。可选的不多。</p>\n</div><div class=\"cl-preview-section\"><p>这主要是因为程序员是一个是相对比较封闭的群体除了技术综合素养、人际关系、沟通能力、眼界其实都是偏差的。这大概是我觉得程序员这个职业最不好的地方。但程序员必须有一个觉悟就是再你看起来牛逼哄哄的技术其实在很多人眼里他只是个工具。对于绝大多数人来说技术也只是谋生的手段。</p>\n</div><div class=\"cl-preview-section\"><p>由于技术的频繁变迁程序员在技术上也很难形成积累所以其他很多职业都是越老越吃香只有程序员这个职业是走下坡路。所以30岁后的程序员如果没有很好的综合素养真的很难。</p>\n</div><div class=\"cl-preview-section\"><p>其实技术这个事儿越到后面越不值钱是很正常的其他很多职业是先付出才有收益但技术不同它只需要付出少量的精力突击学习就能找到一份还可以工作。这点和其他需要前期投入很多成本的职业不同。这个行业门槛偏低享受了低门槛的优势就必定要在未来付出相应的代价。</p>\n</div><div class=\"cl-preview-section\"><h2>程序员在未来大抵有以下4个方向</h2>\n</div><div class=\"cl-preview-section\"><ul><li>\n<p><strong>技术一条路走到黑</strong></p>\n</li>\n<li>\n<p><strong>半技术、半管理做综合性的复合人才</strong></p>\n</li>\n<li>\n<p><strong>自主创业</strong></p>\n</li>\n<li>\n<p><strong>直接转行</strong></p>\n</li>\n</ul></div><div class=\"cl-preview-section\"><p>我们来一条条分析。</p>\n</div><div class=\"cl-preview-section\"><p>第一条能在技术上一条路走到黑的少之又少。能走这条路的很大程度上是真需要一定的天赋大多数人走不到黑就爬不动了。</p>\n</div><div class=\"cl-preview-section\"><p>第三条这个不多说了能自主创业成功的那是你的能力。如果你有这个能力我觉得基本不需要听其他人的意见你必定有很强的自我选择和分析能力。</p>\n</div><div class=\"cl-preview-section\"><p>第四条也有很多人走但大多数是被迫无奈只能转行。</p>\n</div><div class=\"cl-preview-section\"><p>最实际的目标也被大多数前辈证明了确实可行的只有第二条。有很多同学说做纯管理不是不可以只是没有优势你20岁都在为技术奋斗这些积累需要物尽其用的利用起来。技术是你傍身的优势不能丢掉。只有兼具技术和管理才是你最大的优势。</p>\n</div><div class=\"cl-preview-section\"><p>这就是为什么前面我特别强调技术不是唯一把事做好才是你的目标。我们很多同学体会不到是因为你从来都只站在一个执行者的角度去看待问题和完成任务从来没有站在一个leader的角度去看待问题。你必须和你的leader保持同理心。时间长了你自然就具备了一定的管理和分析能力。</p>\n</div><div class=\"cl-preview-section\"><h3>所以你的目标就是把事情做好而不是把技术学好。技术学好这个要求其实很低把事情做漂亮才是难点。</h3>\n</div><div class=\"cl-preview-section\"><p>把事情做好的一个先决条件是你有一定的大局观和分析能力这一点非常重要这也是为什么很多人到了30岁后无法转技术管理的原因。你20多岁的时候只知道每天耕耘自己的一亩三分田做前端的永远不学服务端做服务端的永远不了解前端你如何能有全局的分析能力和大局观</p>\n</div><div class=\"cl-preview-section\"><p>前端这个称号其实是人家给你画的圈不应该成为你自己的枷锁。其实在我心里从来就没有全栈这在过去本身就没有前后端之分何来全栈之说很多同学说过去不分前后端是因为没有那么多的框架要学习这是本末倒置还是抱着考试的态度在学习。</p>\n</div><div class=\"cl-preview-section\"><p>在我看来现在的前端比过去简单太多了开发一个项目的成本比过去低太多了。jquery时代是没有组件化也没有数据绑定想想只这两点能节约多少成本以前你需要自己用原生的JS模拟类这个概念因为那时候没有ES6想想现在的ES标准。</p>\n</div><div class=\"cl-preview-section\"><h3>所以我不推荐大家把自己贴上前端开发还是后端开发的标签自己给自己打标签的行为是非常愚蠢的。因为你扼杀了你自己的可能性。</h3>\n</div><div class=\"cl-preview-section\"><p>如果你不想选择第一条 技术一条路走到黑那么最好的做法就是前后端通杀没有那么难你觉得难只是因为你提前给了自己心里的暗示不断的告诫自己这玩意儿太难了我学不会。老程序员会告诉你其实编程的本质是逻辑的运算前端和后端本身就没有明显的界限。唯一的差异就是前端不管数据库服务端不管CSS。</p>\n</div><div class=\"cl-preview-section\"><p>事实上现在技术的发展趋势也是模糊了前后端的界限这个就不用我再多说了。</p>\n</div><div class=\"cl-preview-section\"><p>如果你确实要选择第一条路纯技术安身立命我觉得选择一个点使劲儿学习专研是正确的。其他三条路在我们学技术的阶段最好是模糊前后端。</p>\n</div><div class=\"cl-preview-section\"><h3>单纯的认为自己是前端/服务端还是没有跳出打工心态和考试心态。如果你是精研算法或者做数据或者做人工智能、大数据的我推荐你精研一个方向。但我们是做Web的Web相对于其他的方向本身就偏简单、含金量不高再把前后端舍弃一个是说不过去的。单前端或者服务端其实是放弃了自主性不给自己的未来留一些变动的可能。</h3>\n</div><div class=\"cl-preview-section\"><p>第二点还有一个非常重要的学习思路注重细节。一个产品的细节才是灵魂好的程序员和不好的程序员其实在技术本身上差异不大但是好的程序员非常注重细节。如果你写代码的时候努力去做好细节那么长期积累下来学习的效果是非常明显的进步也会非常的快。如果你总是觉得这样差不多就行了写出来的代码千疮百孔那我建议乘早放弃编程。</p>\n</div><div class=\"cl-preview-section\"><p>第三点学习太多方法论不是一件好事情以解决问题为目标才是最好的方法论。没有之一。我们很多同学特别热爱去看方法论方法论本身没错但问题是我们没有坚持下去的毅力也没有这个执行力。方法论的前提是你能很好的执行它。所以我们需要有驱动解决问题就是最好的驱动力。太多同学迷失在方法论的探索里永远在找方法就是不肯去做。</p>\n</div><div class=\"cl-preview-section\"><p>从我的角度来讲我觉得编程唯一给我的乐趣就是能独立自主的做出一个完整的成品产品一切尽在自己掌握不求人的感觉真的很好。</p>\n</div><div class=\"cl-preview-section\"><p>下面是<a href=\"https://class.imooc.com/sale/javafullstack?mc_marking=52fcc90c01b2b7bc72ce31cfe688ae84&mc_channel=shouji\"><strong>Java全栈课</strong></a>的小程序电商能自己做出来的感觉是真的不错。如果你有这样的一个项目无论是找工作、还是做外包都有很大的优势用产品来说话远比花里胡哨的说辞和简历要棒很多。</p>\n</div><div class=\"cl-preview-section\"><p>电商CMS<a href=\"http://sleeve.7yue.pro/#/login\">http://sleeve.7yue.pro/#/login</a></p>\n</div><div class=\"cl-preview-section\"><p>电商小程序</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"http://img2.sycdn.imooc.com/5dfae6020001223205000515.jpg\" data-original=\"http://img2.sycdn.imooc.com/5dfae6020001223205000515.jpg\" alt=\"http://img1.sycdn.imooc.com/5dfae6020001223205560572.jpg\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>希望大家能够明白企业需要的是能做好事的人不是会多少框架的人说一千道一万还是要拿出自己的作品。</p>\n</div><div class=\"cl-preview-section\"><p>最后可能我说了很多同学们还是不清楚应该怎么做。其实很简单五个字</p>\n</div><div class=\"cl-preview-section\"><h2>事事有交代</h2>\n</div><div class=\"cl-preview-section\"><p>做好这点你的人生不会太差</p>\n</div><div class=\"cl-preview-section\"><p>好了 咱们的分享到这里就结束了。</p>\n</div><div class=\"cl-preview-section\"><p>最后简单介绍下我的<a href=\"https://class.imooc.com/sale/javafullstack?mc_marking=52fcc90c01b2b7bc72ce31cfe688ae84&mc_channel=shouji\"><strong>课程</strong></a>。有兴趣的同学可以了解下</p>\n</div><div class=\"cl-preview-section\"><p>要学会 变现</p>\n</div><div class=\"cl-preview-section\"><p>物尽其用 技术也是同理</p>\n</div><div class=\"cl-preview-section\"><p>新Java全栈这个<a href=\"https://class.imooc.com/sale/javafullstack?mc_marking=52fcc90c01b2b7bc72ce31cfe688ae84&mc_channel=shouji\"><strong>课程</strong></a>主要的目的有3个</p>\n</div><div class=\"cl-preview-section\"><ul><li>\n<h3>学习现在<a href=\"https://class.imooc.com/sale/javafullstack?mc_marking=52fcc90c01b2b7bc72ce31cfe688ae84&mc_channel=shouji\">Web开发最主流的技术栈</a>小程序+Vue+Java。没有比这三个更主流的技术栈了。</h3>\n</li>\n<li>\n<h3>培养编程思维。在课程中我们除了有SKU、优惠券、购物车数据同步等非常复杂的业务还有数学矩阵、排列组合、设计模式、IOC容器、依赖注入等编程思维的课程。</h3>\n</li>\n<li>\n<h3>培养综合素质和视野你会看到我是如何从0开始一步步的分析问题、提炼需求最终完成了这个超大的项目。</h3>\n</li>\n</ul></div><div class=\"cl-preview-section\"><p>对于前段开发者来说如何进阶后端以我的角度来看如果你能接受Java那必然优先选择Java它有太多的编程思想完全可以给你打开一个新的世界掌握Java里的思想未来学习其他的语言不是大问题如果你实在学不进Java那还是选择NodeJS/Python。都是动态语言其实在编程思维上的提升其实不大。</p>\n</div><div class=\"cl-preview-section\"><p>关于详细<a href=\"https://class.imooc.com/sale/javafullstack?mc_marking=52fcc90c01b2b7bc72ce31cfe688ae84&mc_channel=shouji\"><strong>课程大纲</strong></a>目前第一阶段已经更新完毕二 三 四阶段每周持续更新中我整理了已经更新完部分的详细大纲导图比宣传页要详细一些供大家参考~<br><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dfb08530001ffa125898333.png\" data-original=\"//img1.sycdn.imooc.com/5dfb08530001ffa125898333.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dfb08730001804327632092.png\" data-original=\"//img1.sycdn.imooc.com/5dfb08730001804327632092.png\" alt=\"图片描述\" style=\"width:100%;\"><br>\n此外课程还提供了两个很好的工具Lin CMS和 Lin UI有兴趣的同学可以了解下。</p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://github.com/TaleLin\">https://github.com/TaleLin</a></p>\n</div><div class=\"cl-preview-section\"><p>有空的同学 帮我们点个star感谢支持</p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://class.imooc.com/sale/javafullstack?mc_marking=52fcc90c01b2b7bc72ce31cfe688ae84&mc_channel=shouji\">http://class.imooc.com/sale/javafullstack</a> 这是全栈课的地址 有兴趣的同学了解下</p>\n</div><div class=\"cl-preview-section\"><p><a href=\"http://www.imooc.com/t/4294850\">http://www.imooc.com/t/4294850</a> 我所有课程的地址感谢大家</p>\n</div>\n\t\t\t\t\t",
  74. "cover": [
  75. "http://img2.sycdn.imooc.com/5dfae4a50001a51905000256.jpg",
  76. "//img1.sycdn.imooc.com/5dfaf472000141b612800910.jpg",
  77. "//img1.sycdn.imooc.com/5dfb073a0001b38512800896.jpg",
  78. "//img1.sycdn.imooc.com/5dfb074d00016a8412800899.jpg",
  79. "http://img2.sycdn.imooc.com/5dfae6020001223205000515.jpg",
  80. "//img1.sycdn.imooc.com/5dfb08530001ffa125898333.png",
  81. "//img1.sycdn.imooc.com/5dfb08730001804327632092.png"
  82. ],
  83. "mode": "column"
  84. },
  85. {
  86. "id": "286282",
  87. "title": "我是个前端开发者,我到底要不要学Node开发?",
  88. "browse_count": 10938,
  89. "collection_count": 71,
  90. "comments_count": 24,
  91. "author": {
  92. "id": "4294850",
  93. "author_name": "7七月",
  94. "avatar": "//img1.sycdn.imooc.com/54584e2c00010a2c02200220-160-160.jpg",
  95. "status": "normal"
  96. },
  97. "classify": "前端开发",
  98. "thumbs_up_count": 71,
  99. "create_time": "2019.05.06 11:45",
  100. "content": "\n\t\t\t\t\t\t<p style=\"text-align:center\"><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5ccfac620001f8d405000344.jpg\" data-original=\"https://img1.sycdn.imooc.com/5ccfac620001f8d405000344.jpg\" alt=\"https://img1.sycdn.imooc.com/5ccfac620001f8d412800880.jpg\" style=\"width:100%;\"></p><p> 春和景明</p><p><br></p><p>我总是不太善于做营销这一块,骨子里有点旧时穷酸知识分子的调调,内心里认定好的东西不需要做过多的修饰,说太多大话我会不好意思。<br></p><p>但我一个朋友言之凿凿的说,这个时代就是个浮躁的年代,你要善于制造焦虑,夸大效果,什么都得学,不学就落后,落后就要挨打。</p><p>反正课又不是药,吃不死人。<br></p><p>按照这个说法,长此以往,人人都得是苏东坡,音乐家、书法家、画家、文学家,噢,他还是个美食家。放在计算机领域,恨不得要求你,上能搞人工智能,下能搞操作系统。<br></p><p>然而世上只有一个苏东坡。人还是得认清自己,事事跟风,样样都学,反而落下个附庸风雅的名号。<br></p><p>所以,认清自己的现状,理智选择知识来学习,这一点尤为重要。没有那么多不学不行的知识,也别指望学个什么课就能脱胎换骨,达到Px,Tx。琴棋书画3年起步,5年入门,10年小成,20年大成,凭什么编程就能速成呢?<br></p><p>这世道,缺的就是清醒。<br></p><p>现在的营销都讲究“以德服人”,然而我觉得,知识这种事儿不能迷信权威,必须以理服人。讲道理,是本篇广告的“独特气质”。<br></p><p>新课是关于<a href=\"https://coding.imooc.com/class/342.html?mc_marking=3bdbadf15a609d995ceabd59428f0f79&mc_channel=shouji\" target=\"_self\">Node.js Koa</a>的课程。那么问题来了,借用姜文的话就是:你XX的给我说说,我XX的到底要不要学XX的Node.js?老子学不动了。<br></p><p>没办法,这行就这样。你不学Vue,总不是跑去学React/小程序/算法与数据结构/TS去了,不断学习根本避免不了。一入编程深似海,从此女神是路人。所以关键在于把时间投在学什么上比较划算,利益可以最大化。从经济学的角度考虑问题,兼顾短期、中期与长期的利益,才是上上佳策。<br></p><p><strong>明确表达我的观点,前端学习</strong><a href=\"https://coding.imooc.com/class/342.html?mc_marking=3bdbadf15a609d995ceabd59428f0f79&mc_channel=shouji\" target=\"_self\"><strong>node.js</strong></a><strong>,短期小收益,中期高收益,长期必然趋势。</strong><br></p><p>诚实的讲,一个前端学习服务端编程,短期收益不大。但是考虑到中长期,投入一门课时间,收益还是很可观的——关键是它保留了你未来进阶/转型的可能性,而这仅仅只需要耗费你18个小时左右的时间。<br></p><p>短期来看,对于绝大多数前端开发者,将服务端技术直接应用到工作中的可能性不大。但短期最大的收益我认为是可以让你独立完成一个项目,比如自己开发一个完整的小程序。<br></p><p>不求人的感觉,真的很好。想想,接个外包项目,以前要分人一半,现在你自己照单全收,棒不棒;自己有个好的idea,挽起袖子就是干,爽不爽。<br></p><p>开个玩笑。但独立完成项目对于每个开发者来说,意义确实太挺重大的。这并不是外包只拿一半的问题,而是程序员价值的问题。能独立开发意味着你具备了不求人,不受人限制,甚至是未来独立工作、创业的可能性;独立开发也可以让你去践行自己的各种想法,不再因为好的想法由于不懂服务端而不能落地;独立开发也可以增强你编程的成就感,弄个项目只有fake的假数据有啥意思。<br></p><p>能自己独立做一个完整的Web项目,感觉就像一个熬了几个通宵的人,洗了个热水澡,伸了一个很爽的懒腰,舒服到骨子里去了。<br></p><p>中期来看,学习服务端编程,符合前端要将“传统后端”赶尽杀绝的气势。赶尽杀绝是不可能的,数据和高并发的架构设计依然是传统服务端开发者的领地。但前端现在的发展确实迫使web开发呈现出了”后端靠后,前端靠中“的趋势。服务端蜕化(不是退化,这种变化是良性的)成数据的提供者,大量的业务与数据合并交给前端来处理,这也是三大框架流行的一个因素。前端的重要性愈发明显。<br></p><p>现在的前后端界限非常的模糊。在中大型的项目中,整体架构是三层的,而不是典型的前后双层的。”后端更后“表达了传统的后端将蜕化到纯数据管理的角色,也就是我们说的“服务”的角色,它只提供比较干净的数据,而数据的整合,传输给前端的最终数据模型,都将由前端开发者自己定义。<br></p><p><strong>简单点来说,前端开发者需要会写“API”接口</strong>。前端需要什么数据结构,前端开发者自己是最清楚的,所以业务数据结构的定义交给前端开发者是比较好的做法,这也是为什么很多中大型项目都有“中间层”这个概念。后端是微服务,专心做并发、性能与数据一致性;中间层整合成业务数据,并向前端提供API输出数据。<br></p><p>你成天喷服务端接口垃圾。好!你自己来写!<br></p><p>长期来看,30岁,算了,算35岁,是一个挺难的点。而这一点对于前端开发者来说尤为具有不确定性。<br></p><p>30来岁还能踏踏实实任劳任怨和大把大把年轻人比赛写代码的人真的不多。别听那些跟你说,50岁还能写代码的鬼话。50岁写代码,那可以是因为兴趣,但绝对不能是因为生活。就像我没事儿画会儿画,那是陶冶情操,靠画画生活,我怕不是要饿死。<br></p><p>对于一个程序员来说,逐步走向技术管理/技术总监的路是比较靠谱的一条程序员之路。不是说,35岁后你就不能写代码了,而是35岁之后,只写代码啥都不管的人,你所贡献的价值必定达不到你35岁时的薪资要求。薪资达不到,怎么平衡而立之年”老男人“的自尊心,又如何负担沉重的家庭负担?这个年纪的人需要做团队的决策者,给团队提供超越技术的价值。<br></p><p>不要类比尤大这种星光一样的人物,这并不多见。换个角度,能做出点名气的,混的还不错的,哪个是一点服务端都不懂的呢?<br></p><p>业界有一条不成文的定律,谁离数据更近,谁才有话语权。一个公司掌控技术全局的人必然是偏”后“的。所以我们可以看到大多数公司的核心技术管理者都是服务端出身,纯前端出身完全不懂服务端的CTO/技术总监,少之又少。但只懂服务端,完全不知道啥是三大框架的技术总监遍地都是。是不是不公平?但这就是现状。<br></p><p>不是前端就不能做技术总监/CTO,而是前端独立发展的时间太短。纯前端入门编程,在过去是没有的,绝大多数年纪在30岁以上的都是Java/C#/PHP语言入门编程的。所以,纯前端能否胜任全局的技术管理者,还无法得到有效的验证。<br></p><p>所以35岁这道坎,前端开发者怎么迈过去,还是个未知数。尽可能向服务器靠一靠,是一个不错的选择。反正也不是让你精通服务器开发,也不是让你去搞高并发分布式,但你必须要了解,要能自己独立做做中小项目。<br></p><p>退一万步讲,熟悉服务端API的开发绝对是面试加分项。<br></p><p>以上建议仅针对走技术路线的同学,未来要去做生意的、搞艺术的,或者家里有矿的,不在建议范围内。<br></p><p>此外,学习服务端编程也不是只有<a href=\"https://coding.imooc.com/class/342.html?mc_marking=3bdbadf15a609d995ceabd59428f0f79&mc_channel=shouji\" target=\"_self\">Node.js</a>这一个方向,事实上,如果你心大一点,我觉得Python比Node.js更好,反正这两兄弟都差不多,服务端都搞不过Java。但如果你不想学其他语言,Node.js是你最低成本进阶服务端开发的选择。学一门语言并不是像你想象中的那么容易,如果会JavaScript,选择继续依附这个语言也挺好,至少你不用担心它”死“掉。<br></p><p>前端是框架竞争太残酷,但JavaScript一统天下;而服务端是语言竞争太残酷,昨天还是”世界上最好的语言”,今天就莫名其妙被唱衰了,我他喵的也不知道为啥。青出于蓝胜于蓝到未必,那大概就是一代新人胜旧人吧。<br></p><p>好了,最后要详细讲下课程。<a href=\"https://coding.imooc.com/class/342.html?mc_marking=3bdbadf15a609d995ceabd59428f0f79&mc_channel=shouji\" target=\"_self\"><strong>《</strong><strong>纯正商业应用——Node.js Koa2开发微信小程序后端》</strong></a>到底讲了什么?Node.js有三大应用,前端工程化、Web开发和中间层。本课程主要是讲最为实用的Koa Web开发,并会给大家讲解中间层的概念和意义(不是重点)。工程化和Node.js原生接口基本没讲。<br></p><p><strong>1. </strong> 新课程在业务上将配套我去年的小程序课程<a href=\"https://coding.imooc.com/class/251.html?mc_marking=bea1182dc0a4f4e99b7531fb2e143357&mc_channel=shouji\" target=\"_self\">《纯正商业应用——微信小程序开发实战》</a>,完成《旧岛小样》小程序的服务端开发,帮助同学们对完成完整的项目。前后分离,非必须配套购买,但在前端课程中讲过的知识点不会在新课程中重复。比如回调、Promise、部分ES语法。如果JS基础不好,还是建议看看前端课程。<br></p><p>当然,这都是业务。但熟悉我课程的同学应该知道,我的课,从来不以业务为主。编程思维、优质代码、更好的应用框架才是课程的核心。<br></p><p><strong>2. </strong> 在新课程中,我们将二次开发KOA。说实话,KOA简直就是裸奔,精简的让人发指。我们当然不能裸奔,所以我们必须穿上漂亮的衣服,舒舒服服的去逛街。二次开发是非常要必要的。我说的二次开发并仅仅只是用上koa-body-parser,koa-router,koa-static这些常见的必备中间件。我们需要巧妙使用koa-router分离路由;需要做路由的自动加载来偷懒;需要编写Lin-Validator中间件来构建验证层;需要编写全局异常处理中间件;需要为每个错误编码还需要为权限编写中间件。<br></p><p>很多同学应该知道egg.js这个知名的Koa框架,但本课程解决的问题是egg.js没有解决的。事实上egg.js给出的只是一个企业级别的框架,但并没有给出像TP、SpringBoot、Flask框架中那些及其有用工具类、校验器、专门针对API优化的异常处理,甚至ORM层也没有给出范例代码。因为目标不一样,egg.js给出的是一个宏大的架子,怎么写代码并没有限定。但我的这门新课程给出的是一套小而优雅的Web API解决方案,我始终认为对于项目开发,没有选择就是最好的选择,先让自己开发更有效率是重中之重。有兴趣的同学,可以在未来把这门课程中代码的写法、工具类移植到egg.js中,两全其美。<br></p><p><strong>3. </strong> 课程中少不了要深入理解下Koa三件套:中间件、洋葱模型、async和await。这个应该都被讲烂了,但新课中会让大家知道:</p><ul class=\" list-paddingleft-2\"><li><p>为什么一定要保证洋葱模型?不保证洋葱模型的后果是什么?</p></li><li><p>Koa中的中间件调用到底是同步还是异步?是不是一定要在中间件调用时加上async和await?不加又能怎样?</p></li><li><p>asnyc和await大家都会用,但当我们面临复杂的异步嵌套调用的时候,当我们面对Promise的时候,如何转化成async和await呢?await的本质到底是什么呢?</p></li></ul><p>相信你听完课程后会有一些不同的理解。<br></p><p><strong>4. </strong> 异步编程模型。排除语言的门槛,Node.js其实相对于Python、PHP等动态语言的同步框架,编程难度是比较高的。async和await降低了异步的难度,但是异步编程依然非常容易出错,尤其是在链式嵌套调用和异步异常处理上。这也是本课程要讲的重点。<br></p><p><strong>5.</strong> Node.js中间层的概念。虽然新课程不是以中间层为主的项目,但对于Node.js中间层这个重要的应用还是必须要讲讲的。由于新课程需要调用后端的数据服务,所以刚好可以体现中间层的概念。<br></p><p><strong>6.</strong> Sequelize 以ORM的方式操作MySQL。没有选择MongoDB。一个公司不可能招聘一个只懂MongoDB不懂关系型数据库的后端开发者。可以不学MongoDB,但是不能不学关系型数据库。不懂关系型数据库,谈不上入门了服务端。当然,如果你Mongo+MySQL文体两开花,那更好。<br></p><p><strong>7. </strong> JSON序列化。凡是要做API,必然要给出一个灵活的JSON序列化方案,新课程讲给出几个层级的序列化方案来,包括Sequelize实体层,Scope层,Koa层。<br></p><p><strong>8.</strong> 与微信小程序的对接方案,这里涉及到令牌的处理与无感知登录方案。小程序中的无感知方案和Web有所差异,它更简单一些。<br></p><p><strong>9. </strong> 加强对JS这个语言的应用和理解。异步编程太能体现JS的特点了;原型链的理解也是本课程的一个重点。<br></p><p>最后,是最重要的一点。<strong>整个课程的Koa核心库构建思路其实来源于Lin-CMS-Koa这个开源项目</strong>。换句话说,学完这门课程,基本上入门了Lin-CMS的服务端开发,因为从接口到写法,新课程都和Lin-CMS保持同步。事实上我们推出Lin-CMS-KOA就是为了让前端开发者能够独立完成一整套CMS,必须是一体化的JS技术栈。<br></p><p>当然,Lin-CMS前端是Vue构建,如果还不了解Vue的同学,慕课网黄轶老师和DellLee老师的Vue课是绝佳的选择。<br></p><p>赶在新课程上线的前一天,我们发布了Lin-CMS Vue/Koa/Flask的beta稳定版。新版本重写了路由机制,优化UI布局方案,新版UI看起来更加清爽整洁。未来的Java版本也已经提上了日程。<br></p><p><br></p><p><img class data-ratio=\"0.5604166666666667\" data-class=\"lazyload\" src=\"https://mmbiz.qpic.cn/mmbiz_png/E9CyEN06ol5eEY2XDkdSt1g8LxjZEHNdWPJn2UKgTAzib2G1icHw34KQicUeJJbYt5YGdkE12f41kfsfUcKuE2t8A/640?wx_fmt=png\" data-original=\"https://mmbiz.qpic.cn/mmbiz_png/E9CyEN06ol5eEY2XDkdSt1g8LxjZEHNdWPJn2UKgTAzib2G1icHw34KQicUeJJbYt5YGdkE12f41kfsfUcKuE2t8A/640?wx_fmt=png\" data-type=\"png\" data-w=\"1920\" height=\"auto\" width=\"2558\" _width=\"677px\" crossorigin=\"anonymous\" data-fail=\"0\" style=\"width:100%;\" title alt=\"https://img3.sycdn.imooc.com/5ccfad2a00014b1225581434.jpg\"> </p><p><br></p><p>demo传送门:</p><p> <a href=\"http://face.cms.7yue.pro\" _class=\"lazyload\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC\" data-original=\"http://face.cms.7yue.pro\">http://face.cms.7yue.pro</a> </p><p><br></p><p>github传送门:</p><p><a href=\"https://github.com/TaleLin\" _class=\"lazyload\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC\" data-original=\"https://github.com/TaleLin\">https://github.com/TaleLin</a><br> </p><p><br></p><p>感谢一路有你们!祝你们前程似锦,快意人生!</p>\n\t\t\t\t\t",
  101. "cover": [
  102. "https://img1.sycdn.imooc.com/5ccfac620001f8d405000344.jpg",
  103. "https://mmbiz.qpic.cn/mmbiz_png/E9CyEN06ol5eEY2XDkdSt1g8LxjZEHNdWPJn2UKgTAzib2G1icHw34KQicUeJJbYt5YGdkE12f41kfsfUcKuE2t8A/640?wx_fmt=png"
  104. ],
  105. "mode": "image"
  106. },
  107. {
  108. "id": "294509",
  109. "title": "为什么说要学习全新的原生 JavaScript?",
  110. "browse_count": 9167,
  111. "collection_count": 50,
  112. "comments_count": 8,
  113. "author": {
  114. "id": "5027812",
  115. "author_name": "快乐动起来呀",
  116. "avatar": "//img2.sycdn.imooc.com/54584cb50001e5b302200220-160-160.jpg",
  117. "status": "normal"
  118. },
  119. "classify": "前端开发",
  120. "thumbs_up_count": 50,
  121. "create_time": "2019.10.30 00:05",
  122. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><h3 align=\"center\">开场白</h3>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/389.html?mc_marking=789c4437ab8c45726819b836e16f034e&mc_channel=shouji\">JavaScript</a> 是前端开发工程师最重要的技能,没有之一。在 Vue.js、React.js、Koa、Echarts 等框架风靡一时的背景下,原生的 JavaScript 就可以被抛弃了吗?答案是否定的。</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">typeof</span> 10n<span class=\"token punctuation\">;</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>请看下这个代码的执行结果是什么?(能一眼看出结果的同学请迅速离场)。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5db855c800013c6102680255.jpg\" data-original=\"//img1.sycdn.imooc.com/5db855c800013c6102680255.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>这个正确的直接结果是:<a href=\"https://coding.imooc.com/class/389.html?mc_marking=789c4437ab8c45726819b836e16f034e&mc_channel=shouji\">bigint</a>。</p>\n</div><div class=\"cl-preview-section\"><p>那么 <a href=\"https://coding.imooc.com/class/389.html?mc_marking=789c4437ab8c45726819b836e16f034e&mc_channel=shouji\">bigint</a> 是用来解决什么问题的呢?从字面意思就能猜出 big 的含义,我们知道 Number 基本类型可以精确表示的最大整数是 2^53,如果超出了这个范围那就会出错了,所以在 ES10 提出了 bigint 数据类型来解决这个问题。</p>\n</div><div class=\"cl-preview-section\"><p>开场白结束了,我的观点也表达完了:<strong>原生能力是框架所不能取代的</strong>。</p>\n</div><div class=\"cl-preview-section\"><h3 align=\"center\">辩证关系</h3>\n</div><div class=\"cl-preview-section\"><p>此时此刻,很多同学还处于朦胧中:原生JavaScript不能被框架所取代,那他俩的关系是什么?需要我二选一吗?</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p> Javascript框架是指以Javascript语言为基础搭建的编程框架</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><p>这是百度百科的定义,换句话说框架本质上还是原生的 JavaScript,如果 JavaScript 不具备的能力,框架也注定无能为力。其次,框架更注重的是效率的提升而非能力。比如 Vue.js 让大家编写组件更容易了让我们写业务的开发效率提升了,但是不能说 Vue.js 让原生 JavaScript 的能力增强了。反过来说原生 JavaScript 能在框架之上做什么?那就是写业务逻辑了或者补充框架能力(插件)。比如我们经常使用 Webpack 框架,如果我们想写一个 Webpack 插件还是需要用原生 JavaScript 来编写的。当然就算我们使用 Vue、React 这样的框架,业务逻辑也是需要原生 JavaScript来写的。</p>\n</div><div class=\"cl-preview-section\"><h3 align=\"center\">JS升级:ES6</h3>\n</div><div class=\"cl-preview-section\"><p>前端开发者一路从 ES3 写到 ES5,2015年发布了 <a href=\"https://coding.imooc.com/class/389.html?mc_marking=789c4437ab8c45726819b836e16f034e&mc_channel=shouji\">ES6</a> 让原生 JavaScript 能力大幅提升,Class、Proxy、Generator等专用语法和能力标准化,也逐步得到各大浏览器的兼容。对于前端开发者来说我们可以极度精简代码和提升开发效率,不妨看下:</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>“初始化一个数组,要求数组的长度是 5,每个元素的默认值是 0”</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><p>这道题看似非常简单,我们可能会这样来写代码:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">for</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\"><</span> <span class=\"token number\">5</span><span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n arr<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>可是如果你学习过 ES6 的语法,就会知道 Array 新增的原型对象方法上有个 fill 的 API,它可以轻松实现这个题目,代码如下:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">fill</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>这就是新的语法赋予 JavaScript 新的能力,如果我们不持续学习新的语法,写出来的代码很难是最简、最优雅、性能最好。当然,阅读其他同学或者开源代码的时候也不一定能看懂。那么 <a href=\"https://coding.imooc.com/class/389.html?mc_marking=789c4437ab8c45726819b836e16f034e&mc_channel=shouji\">ES6</a> 到底新增或者增强了哪些能力呢?我们来看下图谱:</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d8f69b50001377b44544894.png\" data-original=\"//img1.sycdn.imooc.com/5d8f69b50001377b44544894.png\" alt style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>从这个图谱不能看出 ES6 增加了很多新的语法,比如 Class、Generator、Proxy、Iterator 等。它们可以解决类、异步、代理、自定义遍历等功能。不如我们再来看个小示例:</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>实现类与继承。</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><p>在 ES6 之前实现类与继承都是借助函数来实现的,在继承方面也是利用原型链。代码如下:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">Component</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n Object<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n writable<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span> <span class=\"token comment\">// jklls</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>这段代码的含义是定义一个组件类,类定义了一个属性 id,这个 id 是随机、只读的。ES6 有了专门的语法来定义类。</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">constructor</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n Object<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n writable<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>在语义上看 ES6 的写法更容易读懂。不信,我们在看下继承的写法:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">Component</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n Object<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n writable<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">SubComponent</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n Component<span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\nSubComponent<span class=\"token punctuation\">.</span>prototype <span class=\"token operator\">=</span> Component<span class=\"token punctuation\">.</span>prototype\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SubComponent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">constructor</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n Object<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n writable<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">SubComponent</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SubComponent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>上下代码对比可以看出来 ES6 的方式要舒服很多,也更容易阅读。借助这个题我们再来思考 ES6 这个写法还能继续优化吗?比如 Object.defineProperty 方法在构造函数里显得那么格格不入。有没有更优雅的写法呢?不妨试试 ES6 新的语法 Proxy?</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">constructor</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>proxy <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Proxy</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n id<span class=\"token punctuation\">:</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token keyword\">get</span> <span class=\"token function\">id</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>proxy<span class=\"token punctuation\">.</span>id\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>利用 Proxy 和 Class getter 方式就能保证 id 是只读的,在 proxy 实例化的时候也能保证 id “随机”、“唯一”。有同学会说这个代码有漏洞,proxy 还可以修改会导致 id 也可以被修改。说的没错,但是低估了 proxy 的能力,你再看:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">constructor</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>proxy <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Proxy</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n id<span class=\"token punctuation\">:</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">set</span> <span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> key<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token keyword\">get</span> <span class=\"token function\">id</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>proxy<span class=\"token punctuation\">.</span>id\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>proxy<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">4</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>要知道 proxy 下面可以放很多跟 id 一样的内容,这样我们就不会一个一个用 Object.defineProperty 去显示的定义“只读”。用 class getter + proxy 的方式写起来“不露痕迹”,大家是否享受这种写法呢?当然,proxy 还有很多用武之地,比如把保护数据、数据校验等等。</p>\n</div><div class=\"cl-preview-section\"><p>如果大家没过瘾,我们再看一个更强大的功能:<a href=\"https://coding.imooc.com/class/389.html?mc_marking=789c4437ab8c45726819b836e16f034e&mc_channel=shouji\">自定义遍历</a>。</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>“我们数据库里存放着很多图书的作者,这些作者按照图书的类别进行分类,现在想遍历所有作者该怎么办?”</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">let</span> authors <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n allAuthors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n fiction<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token string\">'Agatha Christie'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'J. K. Rowling'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Dr. Seuss'</span>\n <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n scienceFiction<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token string\">'Neal Stephenson'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Arthur Clarke'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Isaac Asimov'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Robert Heinlein'</span>\n <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n fantasy<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token string\">'J. R. R. Tolkien'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'J. K. Rowling'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Terry Pratchett'</span>\n <span class=\"token punctuation\">]</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>我们希望可以对 authors 进行遍历并得到所有作者的名单。</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> author <span class=\"token keyword\">of</span> authors<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>author<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>本希望可以这做可是浏览器报错了,告诉我们 authors 是不可遍历的。那我们只能通过遍历所有 key 的方式来实现:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> key <span class=\"token keyword\">in</span> authors<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">let</span> r <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> k <span class=\"token keyword\">in</span> authors<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n r <span class=\"token operator\">=</span> r<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>authors<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span>k<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>r<span class=\"token punctuation\">)</span>\n <span class=\"token comment\">// [\"Agatha Christie\", \"J. K. Rowling\", \"Dr. Seuss\", \"Neal Stephenson\", \"Arthur Clarke\", \"Isaac Asimov\", \"Robert Heinlein\", \"J. R. R. Tolkien\", \"J. K. Rowling\", \"Terry Pratchett\"]</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>虽然用 <a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">ES5</a> 的方式实现了,可是我们仍希望用 for…of 的方式来实现,简单便捷。<a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">ES6</a> 增加了 Iterator 让任意数据结构可以实现自定义遍历器。直接上代码:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\">authors<span class=\"token punctuation\">[</span>Symbol<span class=\"token punctuation\">.</span>iterator<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">let</span> allAuthors <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>allAuthors\n <span class=\"token keyword\">let</span> keys <span class=\"token operator\">=</span> Reflect<span class=\"token punctuation\">.</span><span class=\"token function\">ownKeys</span><span class=\"token punctuation\">(</span>allAuthors<span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">let</span> values <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">next</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>values<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>keys<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n values <span class=\"token operator\">=</span> allAuthors<span class=\"token punctuation\">[</span>keys<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span>\n keys<span class=\"token punctuation\">.</span><span class=\"token function\">shift</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n done<span class=\"token punctuation\">:</span> <span class=\"token operator\">!</span>values<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span>\n value<span class=\"token punctuation\">:</span> values<span class=\"token punctuation\">.</span><span class=\"token function\">shift</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>我们只需要对 authors 这个数据结构增加 Iterator 遍历器接口即可用 for…of 的方式来遍历了,浏览器不再报错了。有没有很惊艳?</p>\n</div><div class=\"cl-preview-section\"><h3 align=\"center\">JS升级:ES7+</h3>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d8f699a0001564612000675.jpg\" data-original=\"//img1.sycdn.imooc.com/5d8f699a0001564612000675.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>其实 ES6 之后 ES7、ES8、ES9、ES10相继诞生,它们让原生 JavaScript 的能力再次提升。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d8f69de0001551540782346.png\" data-original=\"//img1.sycdn.imooc.com/5d8f69de0001551540782346.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>虽然从 ES7 开始没有像 ES6 那样带来大版本的改动,但是能力的提升仍不可忽视。正则表达式是我们日常开发经常使用的技能,从 ES9 开始就支持正则表达式的分组命名捕获。</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p> 在指定的日期字符串中提取年、月、日数据</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><p>在ES9之前不得不这样做:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">let</span> t <span class=\"token operator\">=</span> <span class=\"token string\">'2019-06-07'</span><span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\">/(\\d{4})-(\\d{2})-(\\d{2})/</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 2019</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">[</span><span class=\"token number\">2</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 06</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">[</span><span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 07</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>现在就可以这样做了:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\">console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2019-06-07'</span><span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\">/(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})/</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// [\"2019-06-07\", \"2019\", \"06\", \"07\", index: 0, input: \"2019-06-07\", groups: {…}]</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>这段代码的返回值 groups 已经是 Object 了,具体的值是:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\">groups<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>year<span class=\"token punctuation\">:</span> <span class=\"token string\">\"2019\"</span><span class=\"token punctuation\">,</span> month<span class=\"token punctuation\">:</span> <span class=\"token string\">\"06\"</span><span class=\"token punctuation\">,</span> day<span class=\"token punctuation\">:</span> <span class=\"token string\">\"07\"</span><span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>这个 Object 的 key 就是正则表达式中定义的,也就是把捕获分组进行了命名。想获取这些捕获可以这样做:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">let</span> t <span class=\"token operator\">=</span> <span class=\"token string\">'2019-06-07'</span><span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\">/(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})/</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// [\"2019-06-07\", \"2019\", \"06\", \"07\", index: 0, input: \"2019-06-07\", groups: {…}]</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">.</span>groups<span class=\"token punctuation\">.</span>year<span class=\"token punctuation\">)</span> <span class=\"token comment\">// 2019</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">.</span>groups<span class=\"token punctuation\">.</span>month<span class=\"token punctuation\">)</span> <span class=\"token comment\">// 06</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">.</span>groups<span class=\"token punctuation\">.</span>day<span class=\"token punctuation\">)</span> <span class=\"token comment\">// 07</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>通过这个简单的实例,不难看出<a href=\"https://coding.imooc.com/class/389.html?mc_marking=789c4437ab8c45726819b836e16f034e&mc_channel=shouji\">全新的 JavaScript 语法</a>会改变我们之前书写的习惯,用新的能力解决问题;当然还有很多能力能解决之前搞不定的问题,有没有很期待?</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5db862a00001dcef02550255.gif\" data-original=\"//img1.sycdn.imooc.com/5db862a00001dcef02550255.gif\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>················<br>\n欢迎关注课程:<br><a href=\"https://coding.imooc.com/class/389.html?mc_marking=789c4437ab8c45726819b836e16f034e&mc_channel=shouji\">《再学JavaScript ES(6-10)全版本语法大全》</a>(新课限时优惠)</p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/315.html?mc_marking=1b4ca998cac4dd155a725dbbaa47cf6a&mc_channel=shouji\">JavaScript版 数据结构与算法</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/129.html?mc_marking=1782be68aed014c2e93a5ca131300998&mc_channel=shouji\">前端跳槽面试必备技巧</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">Vue全家桶+SSR+Koa2全栈开发美团网</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/98.html?mc_marking=270155e6d1a2e60478491e691bedc297&mc_channel=shouji\">ES6零基础教学 解析彩票项目</a></p>\n</div>\n\t\t\t\t\t",
  123. "cover": [
  124. "//img1.sycdn.imooc.com/5db855c800013c6102680255.jpg",
  125. "//img1.sycdn.imooc.com/5d8f69b50001377b44544894.png",
  126. "//img1.sycdn.imooc.com/5d8f699a0001564612000675.jpg",
  127. "//img1.sycdn.imooc.com/5d8f69de0001551540782346.png",
  128. "//img1.sycdn.imooc.com/5db862a00001dcef02550255.gif"
  129. ],
  130. "mode": "column"
  131. },
  132. {
  133. "id": "293012",
  134. "title": "领略原生 JavaScript ES6~ES10 的魅力",
  135. "browse_count": 5415,
  136. "collection_count": 49,
  137. "comments_count": 11,
  138. "author": {
  139. "id": "5027812",
  140. "author_name": "快乐动起来呀",
  141. "avatar": "//img2.sycdn.imooc.com/54584cb50001e5b302200220-160-160.jpg",
  142. "status": "normal"
  143. },
  144. "classify": "前端开发",
  145. "thumbs_up_count": 49,
  146. "create_time": "2019.09.28 22:11",
  147. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d8f699a0001564612000675.jpg\" data-original=\"//img1.sycdn.imooc.com/5d8f699a0001564612000675.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>作为<a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">前端开发工程师</a>,盲目追逐框架似乎有点舍本逐末,要知道基本功才是硬核。<a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">JavaScript</a> 的语法这几年一直在更新,不管我们是框架的核心开发者还是业务重塑者,学习下最新的 JavaScript 语法和能力是非常有好处的。下面我们通过几个小示例来看下新语法的强大之处:</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>“初始化一个数组,要求数组的长度是 5,每个元素的默认值是 0”</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><p>这道题看似非常简单,我们可能会这样来写代码:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">for</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\"><</span> <span class=\"token number\">5</span><span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n arr<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>可是如果你学习过 ES6 的语法,就会知道 Array 新增的原型对象方法上有个 fill 的 API,它可以轻松实现这个题目,代码如下:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">fill</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>这就是新的语法赋予 JavaScript 新的能力,如果我们不持续学习新的语法,写出来的代码很难是最简、最优雅、性能最好。当然,阅读其他同学或者开源代码的时候也不一定能看懂。那么 ES6 到底新增或者增强了哪些能力呢?我们来看下图谱:</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d8f69b50001377b44544894.png\" data-original=\"//img1.sycdn.imooc.com/5d8f69b50001377b44544894.png\" alt style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>从这个图谱不能看出 ES6 增加了很多新的语法,比如 Class、Generator、Proxy、Iterator 等。它们可以解决类、异步、代理、自定义遍历等功能。不如我们再来看个小示例:</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>实现类与继承。</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><p>在 ES6 之前实现类与继承都是借助函数来实现的,在继承方面也是利用原型链。代码如下:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">Component</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n Object<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n writable<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span> <span class=\"token comment\">// jklls</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>这段代码的含义是定义一个组件类,类定义了一个属性 id,这个 id 是随机、只读的。ES6 有了专门的语法来定义类。</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">constructor</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n Object<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n writable<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>在语义上看 ES6 的写法更容易读懂。不信,我们在看下继承的写法:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">Component</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n Object<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n writable<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">SubComponent</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n Component<span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\nSubComponent<span class=\"token punctuation\">.</span>prototype <span class=\"token operator\">=</span> Component<span class=\"token punctuation\">.</span>prototype\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SubComponent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">constructor</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n Object<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n writable<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">SubComponent</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">SubComponent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>上下代码对比可以看出来 ES6 的方式要舒服很多,也更容易阅读。借助这个题我们再来思考 ES6 这个写法还能继续优化吗?比如 Object.defineProperty 方法在构造函数里显得那么格格不入。有没有更优雅的写法呢?不妨试试 ES6 新的语法 Proxy?</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">constructor</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>proxy <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Proxy</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n id<span class=\"token punctuation\">:</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token keyword\">get</span> <span class=\"token function\">id</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>proxy<span class=\"token punctuation\">.</span>id\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>利用 Proxy 和 Class getter 方式就能保证 id 是只读的,在 proxy 实例化的时候也能保证 id “随机”、“唯一”。有同学会说这个代码有漏洞,proxy 还可以修改会导致 id 也可以被修改。说的没错,但是低估了 proxy 的能力,你再看:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">constructor</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>proxy <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Proxy</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n id<span class=\"token punctuation\">:</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">set</span> <span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> key<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token keyword\">get</span> <span class=\"token function\">id</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>proxy<span class=\"token punctuation\">.</span>id\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> com <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncom<span class=\"token punctuation\">.</span>proxy<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">4</span>\ncom<span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> <span class=\"token number\">3</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>com<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>要知道 proxy 下面可以放很多跟 id 一样的内容,这样我们就不会一个一个用 Object.defineProperty 去显示的定义“只读”。用 class getter + proxy 的方式写起来“不露痕迹”,大家是否享受这种写法呢?当然,proxy 还有很多用武之地,比如把保护数据、数据校验等等。</p>\n</div><div class=\"cl-preview-section\"><p>如果大家没过瘾,我们再看一个更强大的功能:自定义遍历。</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>“我们数据库里存放着很多图书的作者,这些作者按照图书的类别进行分类,现在想遍历所有作者该怎么办?”</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">let</span> authors <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n allAuthors<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n fiction<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token string\">'Agatha Christie'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'J. K. Rowling'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Dr. Seuss'</span>\n <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n scienceFiction<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token string\">'Neal Stephenson'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Arthur Clarke'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Isaac Asimov'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Robert Heinlein'</span>\n <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n fantasy<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>\n <span class=\"token string\">'J. R. R. Tolkien'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'J. K. Rowling'</span><span class=\"token punctuation\">,</span>\n <span class=\"token string\">'Terry Pratchett'</span>\n <span class=\"token punctuation\">]</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>我们希望可以对 authors 进行遍历并得到所有作者的名单。</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> author <span class=\"token keyword\">of</span> authors<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>author<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>本希望可以这做可是浏览器报错了,告诉我们 authors 是不可遍历的。那我们只能通过遍历所有 key 的方式来实现:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\"><span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> key <span class=\"token keyword\">in</span> authors<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">let</span> r <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> k <span class=\"token keyword\">in</span> authors<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n r <span class=\"token operator\">=</span> r<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>authors<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span>k<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>r<span class=\"token punctuation\">)</span>\n <span class=\"token comment\">// [\"Agatha Christie\", \"J. K. Rowling\", \"Dr. Seuss\", \"Neal Stephenson\", \"Arthur Clarke\", \"Isaac Asimov\", \"Robert Heinlein\", \"J. R. R. Tolkien\", \"J. K. Rowling\", \"Terry Pratchett\"]</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>虽然用 <a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">ES5</a> 的方式实现了,可是我们仍希望用 for…of 的方式来实现,简单便捷。<a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">ES6</a> 增加了 Iterator 让任意数据结构可以实现自定义遍历器。直接上代码:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-js\"><code class=\"prism language-js\">authors<span class=\"token punctuation\">[</span>Symbol<span class=\"token punctuation\">.</span>iterator<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">let</span> allAuthors <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>allAuthors\n <span class=\"token keyword\">let</span> keys <span class=\"token operator\">=</span> Reflect<span class=\"token punctuation\">.</span><span class=\"token function\">ownKeys</span><span class=\"token punctuation\">(</span>allAuthors<span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">let</span> values <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">next</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>values<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>keys<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n values <span class=\"token operator\">=</span> allAuthors<span class=\"token punctuation\">[</span>keys<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span>\n keys<span class=\"token punctuation\">.</span><span class=\"token function\">shift</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n done<span class=\"token punctuation\">:</span> <span class=\"token operator\">!</span>values<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span>\n value<span class=\"token punctuation\">:</span> values<span class=\"token punctuation\">.</span><span class=\"token function\">shift</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>我们只需要对 authors 这个数据结构增加 Iterator 遍历器接口即可用 for…of 的方式来遍历了,浏览器不再报错了。有没有很惊艳?其实 ES6 之后 ES7、ES8、ES9、ES10相继诞生,它们让原生 JavaScript 的能力再次提升。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d8f69de0001551540782346.png\" data-original=\"//img1.sycdn.imooc.com/5d8f69de0001551540782346.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>不学习原生 JavaScript 新技能注定会让我们错过更多魅力之城,一起结对学 <a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">ES6~ES10</a>吧。</p>\n</div><div class=\"cl-preview-section\"><p>·····················<br><strong>欢迎关注课程:</strong></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/315.html?mc_marking=1b4ca998cac4dd155a725dbbaa47cf6a&mc_channel=shouji\">JavaScript版 数据结构与算法</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/129.html?mc_marking=1782be68aed014c2e93a5ca131300998&mc_channel=shouji\">前端跳槽面试必备技巧</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/280.html?mc_marking=636a3b779c984db5fc3d979d3793d71c&mc_channel=shouji\">Vue全家桶+SSR+Koa2全栈开发美团网</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/98.html?mc_marking=270155e6d1a2e60478491e691bedc297&mc_channel=shouji\">ES6零基础教学 解析彩票项目</a></p>\n</div>\n\t\t\t\t\t",
  148. "cover": [
  149. "//img1.sycdn.imooc.com/5d8f699a0001564612000675.jpg",
  150. "//img1.sycdn.imooc.com/5d8f69b50001377b44544894.png",
  151. "//img1.sycdn.imooc.com/5d8f69de0001551540782346.png"
  152. ],
  153. "mode": "column"
  154. },
  155. {
  156. "id": "300475",
  157. "title": "面试过程中应该避免的几种情况(最后附送前端面试题)",
  158. "browse_count": 9446,
  159. "collection_count": 38,
  160. "comments_count": 8,
  161. "author": {
  162. "id": "4427201",
  163. "author_name": "双越",
  164. "avatar": "//img2.sycdn.imooc.com/5a9fc8070001a82402060220-160-160.jpg",
  165. "status": "normal"
  166. },
  167. "classify": "前端开发",
  168. "thumbs_up_count": 38,
  169. "create_time": "2020.02.10 22:40",
  170. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/419.html?mc_marking=6cb7d66d14ce48575d233fad0bda38bd&mc_channel=shouji\">前言</a></h2>\n</div><div class=\"cl-preview-section\"><p>2020 春招在即,准备跳槽的同学应该也都在准备中。除了要准备技术知识点之外,还应该知道面试时容易犯的一些错误。</p>\n</div><div class=\"cl-preview-section\"><p>本片文章,我将回顾一下自己作为面试官,曾经面试过的几个负面的案例,分享出来。大家可以作为参考,看看是不是这样做有些不合适。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/419.html?mc_marking=6cb7d66d14ce48575d233fad0bda38bd&mc_channel=shouji\">案例1 - 深入问题中,誓死不肯放弃</a></h2>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>永不放弃,不达目的誓不罢休 —— 在面试或者编程时,是不可取的</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><h3>案例描述</h3>\n</div><div class=\"cl-preview-section\"><p>A 同学来面试,我出的第一个问题是一个有关逻辑、算法的问题,并不是一个什么新技术。按照正常的流程,A 就说:先让我思考两分钟。我说:可以。</p>\n</div><div class=\"cl-preview-section\"><p>然后,两分钟,三分钟,四分钟,五分钟,过去了。期间我看他写、算很认真,没好意思打断他。到了五分钟,我问:有思路了吗?他答:再给我两分钟,我很快就做出来了。</p>\n</div><div class=\"cl-preview-section\"><p>再然后,两分钟,三分钟,四分钟,五分钟,过去了。我再问:有思路了吗?他答:再等一下,我一定要把这道题目做出来!!!(我没注意他说这句话是否咬着牙说的)</p>\n</div><div class=\"cl-preview-section\"><p>15 分钟过去了,依然没有解答出来,我不得不<strong>强制叫停</strong>,真的需要强制。然后给他解释这道问题,正确的解题思路是什么。但是 —— 他又开始跟我掰扯,问他这种思路对不对 …… 最后,20 多分钟过去了,这道题才算结束。<strong>然而,我的面试时间只有 60 分钟</strong>。</p>\n</div><div class=\"cl-preview-section\"><h3>案例分析</h3>\n</div><div class=\"cl-preview-section\"><p>一道题的成败,决定不了你能否面试通过,搞不出来那就放弃吧,没关系的。而你在第一道题就浪费 20 多分钟,剩下的时间就不够全面考察你的其他能力。毕竟面试官也有其他事情要做,不能一直陪着你。</p>\n</div><div class=\"cl-preview-section\"><p>更重要的是,<strong>遇到问题第一时间应该选择的是沟通,而不是自己攻坚</strong>。面试时遇到问题,可以向面试官求助,让他提示一下。工作中遇到问题,像这样自己攻坚是绝对不可取的,项目将会因为你的攻坚而延期。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/419.html?mc_marking=6cb7d66d14ce48575d233fad0bda38bd&mc_channel=shouji\">案例2 - 生无可恋 & 无所谓的脸</a></h2>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>公司是招程序员,不是招大爷</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><h3>案例描述</h3>\n</div><div class=\"cl-preview-section\"><p>B 大哥来面试,全程死板着脸,面无表情,生无可恋。一脸什么都不在乎的样子,都不正眼看自己一眼。</p>\n</div><div class=\"cl-preview-section\"><p>而且,在思考、回答问题的时候,也不见他积极主动的思考。拿过一个问题来,看一看觉得会多少就说多少,不会的也不想想、不写写、不思考,就直接说不会。</p>\n</div><div class=\"cl-preview-section\"><h3>案例分析</h3>\n</div><div class=\"cl-preview-section\"><p><strong>在实际做项目过程中,沟通和协作是非常重要</strong>。如果因为个人性格问题,而导致沟通不畅,技术再好也不会考虑。</p>\n</div><div class=\"cl-preview-section\"><p>我们作为一个个体,保持自己的个性,无论是哪方面的个性,都没有问题。但作为一个项目组成员,就不能太有个性。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/419.html?mc_marking=6cb7d66d14ce48575d233fad0bda38bd&mc_channel=shouji\">案例3 - Vue 生命周期,忘了</a></h2>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>你说:Vue 生命周期不记得了,但没关系,我再看看两天文档很快就能拿上手的。我想说:既然这么简单,为何不看两天文档再出来面试?也不耽误事儿。</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><h3>案例描述</h3>\n</div><div class=\"cl-preview-section\"><p>C 同学是一名工作经验比较丰富的程序员,简历也不错,工作背景也很好。只不过,此前一直做其他技术栈,Vue 好久没做了。但是没关系,这种简历我们一样非常喜欢,很欢迎来参加面试。</p>\n</div><div class=\"cl-preview-section\"><p>C 同学如约过来面试,基础知识、设计能力、项目经验都非常好。但是问到 Vue 的一些基本使用时,却答不出来。例如问 Vue 组件生命周期,写不出来,或者能写出来一部分漏掉非常关键的一个生命周期。</p>\n</div><div class=\"cl-preview-section\"><p>然后,C 同学会说:这个好久没做,忘了,再看看文档应该就很快能熟悉起来。</p>\n</div><div class=\"cl-preview-section\"><h3>案例分析</h3>\n</div><div class=\"cl-preview-section\"><p>Vue 是现在最常用的框架之一(还有 React webpack 等等),作为候选人,无论用过没用过,都应该在面试之前去复习、恶补一下。哪怕捡着最关键的地方补一补,读一读文档。组件生命周期,是理解 Vue 或者其他框架的核心,面试也是必考的。</p>\n</div><div class=\"cl-preview-section\"><p>明知道是重点内容,面试必考内容,而不去准备。这样眼高手低,会让人担心,入职之后是不是也这样。例如:这个代码很简单,不用写注释了;这个功能很简单,不用写设计方案和单元测试了 ……</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/419.html?mc_marking=6cb7d66d14ce48575d233fad0bda38bd&mc_channel=shouji\">案例4 - 看简历至少是 CTO</a></h2>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>你那么厉害,干嘛要来搞前端呢?</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><h3>案例描述</h3>\n</div><div class=\"cl-preview-section\"><p>hr 转来了一份 D 同学的简历,打开一看,满满的三大页。我一看这技能点,真的是全栈中的 VIP 。</p>\n</div><div class=\"cl-preview-section\"><p>从前端到客户端,再到跨端。从后端到数据库,再到运维。从 js 到 java ,再到 python ,最后到 C++ 。从算法,到大数据,再到 AI 。</p>\n</div><div class=\"cl-preview-section\"><p>最后,吓得我没敢邀约。</p>\n</div><div class=\"cl-preview-section\"><h3>案例分析</h3>\n</div><div class=\"cl-preview-section\"><p>也许这 D 同学真的如此 NB ,但是这种水平真的不适合搞前端。但是,如果 D 同学真的有心想要做前端开发,我坚信他的简历一定有水分。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/419.html?mc_marking=6cb7d66d14ce48575d233fad0bda38bd&mc_channel=shouji\">案例5 - 不想做一线开发</a></h2>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>绝大部分工作岗位,都是招干活的。所以,要看清岗位要求,再去面试。</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><h3>案例描述</h3>\n</div><div class=\"cl-preview-section\"><p>E 老哥工作时间很久了,大大小小的公司也都带过,项目经验也很丰富。从基础知识,到框架,再到项目经验,也都比较全面。不能说很优秀,但整体 70-80 分没问题。这已经很不错了。</p>\n</div><div class=\"cl-preview-section\"><p>但面试到最后,他表达说,自己不想再做一线开发了。无论是什么原因吧,反正想做一些管理岗位或者项目经理的角色,想带人。</p>\n</div><div class=\"cl-preview-section\"><p>当然,最后肯定没成功,因为这个岗位招聘的就是一线开发人员。</p>\n</div><div class=\"cl-preview-section\"><h3>案例分析</h3>\n</div><div class=\"cl-preview-section\"><p>其实自己有自己的工作要求,这不是问题,而且是好事儿。但你也要看清楚招聘岗位要求,看看公司想要招什么。否则你来了,折腾了半天,结果双方都浪费时间。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/419.html?mc_marking=6cb7d66d14ce48575d233fad0bda38bd&mc_channel=shouji\">案例6 - 挑战面试官</a></h2>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>你是来面试的,是来争取工作机会的,不是来跟面试官争论的。</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><h3>案例描述</h3>\n</div><div class=\"cl-preview-section\"><p>曾经面试过 F 老哥,记得问到 Vue 原理的一个问题时,我跟他解释这个问题的流程。当我讲到一个非常细节的问题时,他突然兴致起来,跟我说:这个地方你说错了。然后说了自己的答案,还拍着胸脯确定无疑。</p>\n</div><div class=\"cl-preview-section\"><p>说实话,当时我还真有点心虚,难道是我记错了?然后我就嗯啊的答应着,顺着他说。后来我回去查了查,发现自己没记错。</p>\n</div><div class=\"cl-preview-section\"><h3>案例分析</h3>\n</div><div class=\"cl-preview-section\"><p>当然,F 老哥还不算是挑战面试官了,算不上挑战。当然我也是个大大咧咧的人,也不会在乎程序员的技术争论,这很正常。</p>\n</div><div class=\"cl-preview-section\"><p>但是作为候选人,建议你千万不要做任何 善意的/非善意的 ,纠正/挑战 面试官的事情。哪怕他说错了,你就那么听一听,就忍不了了吗?</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/419.html?mc_marking=6cb7d66d14ce48575d233fad0bda38bd&mc_channel=shouji\">最后,附送前端常考的面试题</a></h2>\n</div><div class=\"cl-preview-section\"><p>以下几个问题是面试常考,和社区热议的题目,可以借机对自己做一个小测试。<strong>PS:只有问题没有答案。对问题有疑问的,欢迎给我留言哈。</strong></p>\n</div><div class=\"cl-preview-section\"><h3>Vue 面试题</h3>\n</div><div class=\"cl-preview-section\"><ul><li>v-if 和 v-show 的区别。</li>\n<li>为何 v-for 中使用 key(要说明原理)?</li>\n<li>描述 Vue 生命周期(有父子组件的情况下)。</li>\n<li>Vue 组件通讯的常见方式。</li>\n<li>描述组件渲染和更新的过程(开放型题目,自由发挥)。</li>\n<li>用 Vue 设计一个购物车,请设计组件结构,设计 vuex 数据结构。</li>\n</ul></div><div class=\"cl-preview-section\"><h3>React 面试题</h3>\n</div><div class=\"cl-preview-section\"><ul><li>React 组件通讯的常见方式。</li>\n<li>JSX 本质是什么?</li>\n<li>context 是什么,有何用途?</li>\n<li>shouldComponentUpdate 的深入理解。</li>\n<li>描述 redux 单项数据流。</li>\n<li>setState 是同步还是异步?</li>\n<li>用 React 设计一个 todolist ,请设计组件结构,设计 state 数据结构。</li>\n</ul></div><div class=\"cl-preview-section\"><h3>webpack 面试题</h3>\n</div><div class=\"cl-preview-section\"><ul><li>前度代码为何要进行构建和打包?</li>\n<li>module chunk bundle 分别是什么意思,有何区别?</li>\n<li>loader 和 plugin 的区别。</li>\n<li>webpack 如何实现懒加载?</li>\n<li>webpack 常见性能优化方式(开放型题目,自由发挥)。</li>\n<li>babel-runtime 和 babel-polyfill 的区别。</li>\n</ul></div><div class=\"cl-preview-section\"><p>最后,祝大家都能拿到满意 offer !</p>\n</div>\n\t\t\t\t\t",
  171. "cover": [],
  172. "mode": "base"
  173. },
  174. {
  175. "id": "293310",
  176. "title": "为何前端面试会考察 nodejs ?",
  177. "browse_count": 4893,
  178. "collection_count": 26,
  179. "comments_count": 5,
  180. "author": {
  181. "id": "4427201",
  182. "author_name": "双越",
  183. "avatar": "//img2.sycdn.imooc.com/5a9fc8070001a82402060220-160-160.jpg",
  184. "status": "normal"
  185. },
  186. "classify": "前端开发",
  187. "thumbs_up_count": 26,
  188. "create_time": "2019.10.10 21:22",
  189. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><p>相信很多同学学习 <a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">nodejs</a> 都是为了应对面试,或者看别人学过会了,自己也要学。但是你有没有深入思考过,为何面试时要考察 nodejs ,为何 nodejs 逐渐成为前端程序员的必备技能?</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">为了你自己?</a></h2>\n</div><div class=\"cl-preview-section\"><p>有些同学可能会说,学习 <a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">nodejs</a> 会让自己前后端都懂,慢慢成长为全栈工程师,巴拉巴拉 —— 就此打住!</p>\n</div><div class=\"cl-preview-section\"><p>不要一上来就说一些个人的技术理想,虽然能有且坚持技术理想的人也少之又少。<strong>技术的使用,永远都是为了解决问题</strong>,你自己的成长只有你自己关心,公司从来不关心一个普通员工的技术成长。公司也从来不关心你用什么技术,什么语言来实现需求,反正只要高效稳定的做出来,后续能保证扩展、快速迭代即可。</p>\n</div><div class=\"cl-preview-section\"><p>其实大部分公司号称有很多员工培养机制,各种技术分享,各种公款买书。但是经历过的人应该都明白,那些分享基本听完就忘,而且很多大牛的分享都是为了自己的技术 KPI 。公款买书和自己买书,也就为自己省了几十块钱,书买来看不看,还得靠你自己。如果你自己能主动看书,相比于学到的知识还差那几十块钱吗?</p>\n</div><div class=\"cl-preview-section\"><p>总之,公司面试时对于候选人的各种要求,目的都是为了更高效的产出工作,要符合部门或者公司的利益。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">为了能了解 server 端合作伙伴</a></h2>\n</div><div class=\"cl-preview-section\"><p>在日常工作中,一个 web 项目都会有前端和 server 端,而日常的项目开发,功能迭代,也都需要前端和 server 端配合完成。所以,server 端的接口人,是前端工程师的重要合作伙伴。</p>\n</div><div class=\"cl-preview-section\"><p>server 端的工作模式是什么,它的输入输出是什么,你如果了解甚至做过,那你们的合作将会效率大增。但是,如果前端完全不了解 <a href=\"https://coding.imooc.com/class/320.html?mc_marking=79eafdd0eb8b5dd4114bdf9b50eae184&mc_channel=shouji\">server</a> 端,同时 server 端也不了解前端,那双方合作起来肯定误会不断。特别是如果项目需求一天一变的情况下,而这种情况并不少见。我们无权要求 server 端必须了解前端,只能要求自己更多的了解 server 端。</p>\n</div><div class=\"cl-preview-section\"><p>例如,在一个项目做技术方案设计和评审时,前端和 server 端要确定接口,一般包括 method、输入和输出格式。还会讨论,某些功能实现到底是前端来做,还是 server 来做,哪个更合适。此时,如果你不知道 server 端如果工作,那在这件事儿上你只能听之任之,任人摆布。如果你了解甚至做过 server 端的话,其中有些不合理的地方你就能提出来,既能展示自己的能力,又能保证项目顺利进行。</p>\n</div><div class=\"cl-preview-section\"><p>再例如,万一遇到什么被甩锅或者撕逼的事儿(这种事儿也并不少见),你了解 server 端和你不知道 server 端,绝对是两种不同的“战争”方式。</p>\n</div><div class=\"cl-preview-section\"><p>nodejs 天生就是给前端程序员做 server 的神器,相比于 php python 等其他语言,让前端程序员更容易的入门并接触 server 端开发。做过 nodejs server 端,你就能知道用 php java python 开发 server 是什么套路,只是语言不一样而已。</p>\n</div><div class=\"cl-preview-section\"><p>因此,在 nodejs 出现或者普及之前,岗位招聘要求也一般会写上“了解一门后端语言者优先录用”之类的话语。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">为了能承担更多的工作</a></h2>\n</div><div class=\"cl-preview-section\"><p>前端程序员只能且必须做前端页面吗?同时 server 端只能做后端接口吗?—— 当然不是,我有两个例子:</p>\n</div><div class=\"cl-preview-section\"><ul><li>我呆过的两个大型互联网公司,都见过一些测试平台,是由测试人员开发并维护的</li>\n<li>Angular 最初就是几个搞 java 的人写出来的,所以很多概念前端人员都看不懂</li>\n</ul></div><div class=\"cl-preview-section\"><p>其实作为公司,作为领导,他们从来都不会限制自己的员工或者下属部门的工作范围,返回会激发他们的创造性,恨不得什么事儿你都能搞定 —— 当然也得保证可靠性,不能胡来。</p>\n</div><div class=\"cl-preview-section\"><p>我们一个前端部门,难道所有的接口都必须依赖于 server 部门的人来开发吗?难道所有的产品也都必须 server 部门的人来配合吗?—— 这个是不一定的。一般来说,公司核心业务的接口肯定必须由 server 部门来统一维护,但是一些其他的工具性或者 CMS 类型的产品,业务复杂度不高,我们完全可以自己来闭环。一些事情能自己闭环,对于公司来说就是节省了人力,而且解耦了部门之间没必要的业务联系,也就提高了工作效率。</p>\n</div><div class=\"cl-preview-section\"><p>另外,在一些大公司,人员较多,组织层次较多,业务复杂,领导也多。这种情况下,领导之间占地盘或者抢地盘就很正常了,所以此时更需要员工是“全才”。有些比较看好的项目,只要上层领导审批通过,自己部门的人就悄悄做了,不需要其他组来配(抢)合(食)。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">为了从前端到大前端</a></h2>\n</div><div class=\"cl-preview-section\"><p>传统的前后端合作方式是</p>\n</div><div class=\"cl-preview-section\"><ul><li>前后端分离,通过 http 接口连接</li>\n<li>前端:开发页面,调用 http 接口获取、渲染数据</li>\n<li>后端:实现 http 接口,接收和返回数据</li>\n</ul></div><div class=\"cl-preview-section\"><p>这样做的好处就是前后端职责分明,各司其职。而坏处也很明显:<strong><a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">http 接口很容易混杂</a></strong>。特别是系统比较大,业务复杂,开发新功能升级旧功能,一段时间之后,你会发现 http 接口列表已经混乱不堪。</p>\n</div><div class=\"cl-preview-section\"><p>因为上述 http 接口,其实就是业务接口,而且需要前端人员,后端人员都需要参与研发和维护。参与的人和角色越多,业务越复杂,业务变化也频繁,接口混乱的程度也就越大。接口混乱了,那么前端和后端的沟通也就混乱了,弄不好还要就某个历史问题撕逼俩小时,散会后再心理暗骂半天。严重影响工作效率。</p>\n</div><div class=\"cl-preview-section\"><p>我们无法决定业务复杂度以及业务变化频率,但我们可以从系统设计上来减少参与业务的人和角色。即让后端人员更“靠后”一些,不要参与到业务中来,而更多关注数据和底层计算层面。其中前端也会更“靠后”一些,负责开发业务相关的 http 接口,即<strong>大前端</strong>模式。大前端模式还可以无缝接入 SSR ,无论是 vue React 框架还是传统的 nodejs 模板引擎都可以。</p>\n</div><div class=\"cl-preview-section\"><ul><li>大前端</li>\n<li>开发前端页面,调用接口层</li>\n<li>接口层开发业务接口 & 实现 SSR</li>\n<li>后端:关注于数据和服务,提供 http 接口或者 rpc 协议,供接口层调用</li>\n</ul></div><div class=\"cl-preview-section\"><p>大前端模式,让前端人员既开发页面,又实现业务接口,这样能最大程度的保障接口统一,也没有那么多的跨部门能沟通成本,也就提高了工作效率。而大前端最重要的环节,就是 nodejs 这一层,因为我们要用它来实现接口层。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">总结</a></h2>\n</div><div class=\"cl-preview-section\"><p>俗话说无利不起早,公司也是无利不面试。也只有找到了面试的目的,和公司的需求,才能让自己有更明确的学习方向。</p>\n</div><div class=\"cl-preview-section\"><p>····························<br><strong>欢迎关注课程:</strong></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/388.html?mc_marking=30fc15cce247df044d234982dbcb15da&mc_channel=shouji\">《Node.js-Koa2框架生态实战-从零模拟新浪微博》</a>(限时优惠中)</p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/320.html?mc_marking=79eafdd0eb8b5dd4114bdf9b50eae184&mc_channel=shouji\">《前端晋升全栈工程师必备课程Node.js 从零开发 web server 博客项目》</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/190.html?mc_marking=ee24e06c9d52f85746f87c10045ffe4b&mc_channel=shouji\">《揭秘一线互联网企业 前端JavaScript高级面试 》</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/115.html?mc_marking=7fedde0f6ffb70e41252a92c52f6fa9b&mc_channel=shouji\">《前端JavaScript面试技巧 》</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/99.html?mc_marking=9b4a0b1dfc8eadaab59ce4521ecce94d&mc_channel=shouji\">《React高级实战 打造大众点评 WebApp 》</a></p>\n</div>\n\t\t\t\t\t",
  190. "cover": [],
  191. "mode": "base"
  192. },
  193. {
  194. "id": "289159",
  195. "title": "【算法杂谈4】神一样的随机算法",
  196. "browse_count": 5768,
  197. "collection_count": 70,
  198. "comments_count": 9,
  199. "author": {
  200. "id": "108955",
  201. "author_name": "liuyubobobo",
  202. "avatar": "//img2.sycdn.imooc.com/5347593e00010cfb01400140-160-160.jpg",
  203. "status": "normal"
  204. },
  205. "classify": "人工智能",
  206. "thumbs_up_count": 70,
  207. "create_time": "2019.07.10 14:02",
  208. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><p>这篇文章,我们从一道经典面试题开始来探讨这个问题。这个面试题有很多形式,但其实背后的<a href=\"https://coding.imooc.com/class/207.html?mc_marking=a8c5a56d4c08823568d16224c24dc2ab&mc_channel=shouji\">算法</a>是一致的。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>这个问题是:</p>\n</div><div class=\"cl-preview-section\"><p><strong>设计一个公平的洗牌算法</strong></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>1.</strong></p>\n</div><div class=\"cl-preview-section\"><p>看问题,洗牌,显然是一个随机算法了。随机算法还不简单?随机呗。把所有牌放到一个数组中,每次取两张牌交换位置,随机 k 次即可。</p>\n</div><div class=\"cl-preview-section\"><p>如果你的答案是这样,通常面试官会进一步问一下,k 应该取多少?100?1000?10000?</p>\n</div><div class=\"cl-preview-section\"><p>很显然,取一个固定的值不合理。如果数组中有 1000000 个元素,随机 100 次太少;如果数组中只有 10 个元素,随机 10000 次又太多。一个合理的选择是,随机次数和数组中元素大小相关。比如数组有多少个元素,我们就随机多少次。</p>\n</div><div class=\"cl-preview-section\"><p>这个答案已经好很多了。但其实,连这个问题的本质都没有触及到。此时,面试官一定会狡黠地一笑:这个算法公平吗?</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>我们再看问题:设计一个<strong>公平</strong>的洗牌算法。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>2.</strong></p>\n</div><div class=\"cl-preview-section\"><p>问题来了,对于一个洗牌算法来说,什么叫“公平”?这其实是这个问题的实质,我们必须定义清楚:什么叫公平。</p>\n</div><div class=\"cl-preview-section\"><p>一旦你开始思考这个问题,才触及到了这个问题的核心。<strong>在我看来,不管你能不能最终给出正确的算法,如果你的思路是在思考对于洗牌算法来说,什么是“公平”,我都觉得很优秀。</strong></p>\n</div><div class=\"cl-preview-section\"><p>因为背出一个算法是简单的,但是这种探求问题本源的思考角度,绝不是一日之功。别人告诉你再多次“要定义清楚问题的实质”都没用。这是一种不断面对问题,不断解决问题,逐渐磨炼出来的能力,短时间内无法培训。</p>\n</div><div class=\"cl-preview-section\"><p>这也是我经常说的,<strong>面试不是标准化考试,不一定要求你给出正确答案。面试的关键,是看每个人思考问题的能力。</strong></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>说回我们的洗牌算法,什么叫公平呢?一旦你开始思考这个问题,其实答案不难想到。洗牌的结果是所有元素的一个排列。一副牌如果有 n 个元素,最终排列的可能性一共有 n! 个。公平的洗牌算法,应该能<strong>等概率地给出这 n! 个结果中的任意一个。</strong></p>\n</div><div class=\"cl-preview-section\"><p>如思考虑到这一点,我们就能设计出一个简单的暴力算法了:对于 n 个元素,生成所有的 n! 个排列,然后,随机抽一个。</p>\n</div><div class=\"cl-preview-section\"><p>这个算法绝对是公平的。但问题是,复杂度太高。复杂度是多少呢?O(n!)。因为,n 个元素一共有 n! 种排列,我们求出所有 n! 种排列,至少需要 n! 的时间。</p>\n</div><div class=\"cl-preview-section\"><p>有一些同学可能对 O(n!) 没有概念。我本科时就闹过笑话,正儿八经地表示 O(n!) 并不是什么大不了不起的复杂度。实际上,这是一个比指数级 O(2^n) 更高的复杂度。因为 2^n 是 n 个 2 相乘;而 n! 也是 n 个数字相乘,但除了 1,其他所有数字都是大于等于 2 的。当 n>=4 开始,n! 以极快的的速度超越 2^n。</p>\n</div><div class=\"cl-preview-section\"><p>O(2^n) 已经被称为指数爆炸了。O(n!) 不可想象。</p>\n</div><div class=\"cl-preview-section\"><p>所以,这个算法确实是公平的,但是,时间不可容忍。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>3.</strong></p>\n</div><div class=\"cl-preview-section\"><p>我们再换一个角度思考“公平”这个话题。其实,我们也可以认为,公平是指,**对于生成的排列,每一个元素都能等概率地出现在每一个位置。**或者反过来,<strong>每一个位置都能等概率地放置每个元素。</strong></p>\n</div><div class=\"cl-preview-section\"><p>这个定义和上面的<strong>最终洗牌结果,可以等概率地给出这 n! 个排列中的任意一个</strong>,是等价的。这个等价性,可以证明出来。并不难。如果正在学习概率论的同学,还比较习惯概率论处理问题的思想,应该能很快搞定:)</p>\n</div><div class=\"cl-preview-section\"><p>基于这个定义,我们就可以给出一个简单的算法了。说这个算法简单,是因为他的逻辑太容易了,就一个循环:</p>\n</div><div class=\"cl-preview-section\"><pre><code>for(int i = n - 1; i >= 0 ; i -- )\n\tswap(arr[i], arr[rand() % (i + 1)])\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>这么简单的一个<a href=\"https://coding.imooc.com/class/207.html?mc_marking=a8c5a56d4c08823568d16224c24dc2ab&mc_channel=shouji\">算法</a>,可以保证上面我所说的,对于生成的排列,**每一个元素都能等概率的出现在每一个位置。**或者反过来,<strong>每一个位置都能等概率的放置每个元素。</strong></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>大家可以先简单的理解一下这个循环在做什么。其实非常简单,i 从后向前,每次随机一个 [0…i] 之间的下标,然后将 arr[i] 和这个随机的下标元素,也就是 arr[rand() % (i + 1)] 交换位置。</p>\n</div><div class=\"cl-preview-section\"><p>大家注意,由于每次是随机一个 [0…i] 之间的下标,所以,我们的计算方式是 rand() % (i + 1),要对 i + 1 取余,保证随机的索引在 [0…i] 之间。</p>\n</div><div class=\"cl-preview-section\"><p>这个算法就是大名鼎鼎的 <strong>Knuth-Shuffle,即 Knuth 洗牌算法。</strong></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>这个算法的原理,我们稍后再讲。先来看看 Knuth 何许人也?</p>\n</div><div class=\"cl-preview-section\"><p>中文名:高纳德。算法理论的创始人。我们现在所使用的各种算法复杂度分析的符号,就是他发明的。上世纪 60-70 年代计算机算法的黄金时期,近乎就是他一手主导的。他的成就实在太多,有时间单独发文介绍,但是,我觉得一篇文章是不够的,一本书还差不多。</p>\n</div><div class=\"cl-preview-section\"><p>大家最津津乐道的,就是他所写的《The Art of Computer Programming》,简称 TAOCP。这套书准备写七卷本,然后,到今天还没有写完,但已经被《科学美国人》评为可以媲美相对论的巨著。</p>\n</div><div class=\"cl-preview-section\"><p>微软是 IT 界老大的年代,比尔盖茨直接说,如果你看完了这套书的第一卷本,请直接给我发简历。<br><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a2360001b48f09000309.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a2360001b48f09000309.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>至于这套书为什么写的这么慢?因为老爷子写到一半,觉得当下的文字排版工具都太烂,于是<strong>转而发明出了现在流行的LaTex文字排版系统…</strong></p>\n</div><div class=\"cl-preview-section\"><p>另外,老爷子可能觉得当下的编程语言都不能完美地表现自己的逻辑思想,还<strong>发明了一套抽象的逻辑语言,用于这套书中的逻辑表示…</strong></p>\n</div><div class=\"cl-preview-section\"><p>下面这张照片是他年轻的时候。这张照片是我在斯坦福大学计算机学院的橱窗拍的。<br><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a24300017abc09001200.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a24300017abc09001200.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>下面的话和大家共勉:</p>\n</div><div class=\"cl-preview-section\"><blockquote>\n<p>A programmer who subconsciously views himself as an artist will enjoy what he does and will do it better.</p>\n<p>Donald E. Knuth 1978</p>\n</blockquote>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>所以,我从来都不认为自己只是一名工程师而已。<strong>我是艺术家:)</strong></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>4.</strong></p>\n</div><div class=\"cl-preview-section\"><p>是时候仔细的看一下,这个简单的算法,为什么能做到保证:<strong>对于生成的排列,每一个元素都能等概率的出现在每一个位置</strong>了。</p>\n</div><div class=\"cl-preview-section\"><p>其实,简单的吓人:)</p>\n</div><div class=\"cl-preview-section\"><p>在这里,我们模拟一下算法的执行过程,同时,对于每一步,计算一下概率值。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>我们简单的只是用 5 个数字进行模拟。假设初始的时候,是按照 1,2,3,4,5 进行排列的。</p>\n</div><div class=\"cl-preview-section\"><p>那么,根据这个算法,首先会在这五个元素中选一个元素,和最后一个元素 5 交换位置。假设随机出了 2。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a29e00011b4e09000273.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a29e00011b4e09000273.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>下面,我们计算 2 出现在最后一个位置的概率是多少?非常简单,因为是从 5 个元素中选的嘛,就是 1/5。实际上,根据这一步,任意一个元素出现在最后一个位置的概率,都是 1/5。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a2af00011c7f09000351.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a2af00011c7f09000351.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><p>下面,根据这个算法,我们就已经不用管 2 了,而是在前面 4 个元素中,随机一个元素,放在倒数第二的位置。假设我们随机的是 3。3 和现在倒数第二个位置的元素 4 交换位置。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a2ec0001d93009000342.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a2ec0001d93009000342.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>下面的计算非常重要。3 出现在这个位置的概率是多少?计算方式是这样的:</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a5380001fe5a09000332.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a5380001fe5a09000332.jpg\" alt=\"图片描述\" style=\"width:100%;\"><br>\n其实很简单,因为 3 逃出了第一轮的筛选,概率是 4/5,但是 3 没有逃过这一轮的选择。在这一轮,一共有4个元素,所以 3 被选中的概率是 1/4。因此,最终,3 出现在这个倒数第二的位置,概率是 4/5 * 1/4 = 1/5。</p>\n</div><div class=\"cl-preview-section\"><p>还是 1/5 !</p>\n</div><div class=\"cl-preview-section\"><p>实际上,用这个方法计算,任意一个元素出现在这个倒数第二位置的概率,都是 1/5。</p>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><p>相信聪明的同学已经了解了。我们再进行下一步,在剩下的三个元素中随机一个元素,放在中间的位置。假设我们随机的是 1。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a548000190f409000436.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a548000190f409000436.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>关键是:1 出现在这个位置的概率是多少?计算方式是这样的:<br><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a5540001c69809000332.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a5540001c69809000332.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>即 1 首先在第一轮没被选中,概率是 4/5,在第二轮又没被选中,概率是 3/4 ,但是在第三轮被选中了,概率是 1/3。乘在一起,4/5 * 3/4 * 1/3 = 1/5。</p>\n</div><div class=\"cl-preview-section\"><p>用这个方法计算,任意一个元素出现在中间位置的概率,都是 1/5。</p>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><p>这个过程继续,现在,我们只剩下两个元素了,在剩下的两个元素中,随机选一个,比如是4。将4放到第二个位置。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a56100019de509000449.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a56100019de509000449.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>然后,4 出现在这个位置的概率是多少?4 首先在第一轮没被选中,概率是 4/5;在第二轮又没被选中,概率是 3/4;第三轮还没选中,概率是 2/3,但是在第四轮被选中了,概率是 1/2。乘在一起,4/5 * 3/4 * 2/3 * 1/2 = 1/5。</p>\n</div><div class=\"cl-preview-section\"><p>用这个方法计算,任意一个元素出现在第二个位置的概率,都是 1/5。<br><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a56d0001b1ad09000340.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a56d0001b1ad09000340.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><p>最后,就剩下元素5了。它只能在第一个位置呆着了。</p>\n</div><div class=\"cl-preview-section\"><p>那么 5 留在第一个位置的概率是多少?即在前 4 轮,5 都没有选中的概率是多少?</p>\n</div><div class=\"cl-preview-section\"><p>在第一轮没被选中,概率是 4/5;在第二轮又没被选中,概率是 3/4;第三轮还没选中,概率是 2/3,在第四轮依然没有被选中,概率是 1/2。乘在一起,4/5 * 3/4 * 2/3 * 1/2 = 1/5。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a5b10001d89b09000480.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a5b10001d89b09000480.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>算法结束。</p>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><p>你看,在整个过程中,每一个元素出现在每一个位置的概率,都是 1/5 !</p>\n</div><div class=\"cl-preview-section\"><p>所以,这个算法是公平的。</p>\n</div><div class=\"cl-preview-section\"><p>当然了,上面只是举例子。这个证明可以很容易地拓展到数组元素个数为 n 的任意数组。整个算法的复杂度是 O(n) 的。</p>\n</div><div class=\"cl-preview-section\"><p>通过这个过程,大家也可以看到,同样的思路,我们也完全可以从前向后依次决定每个位置的数字是谁。不过从前向后,代码会复杂一些,感兴趣的同学可以想一想为什么?自己实现一下试试看?</p>\n</div><div class=\"cl-preview-section\"><p>(因为生成 [0, i] 范围的随机数比生成 [i, n) 范围的随机数简单,直接对 i+1 求余就好了。)</p>\n</div><div class=\"cl-preview-section\"><p>怎么样,<strong>是不是很酷?</strong></p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a5a000015bfa10800608.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a5a000015bfa10800608.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>5.</strong></p>\n</div><div class=\"cl-preview-section\"><p>这个算法除了洗牌,还能怎么用?</p>\n</div><div class=\"cl-preview-section\"><p>其实,在很多随机的地方,都能使用。比如,扫雷生成随机的盘面。我们可以把扫雷的二维盘面先逐行连接,看作是一维的。之后,把 k 颗雷依次放在开始的位置。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a5c30001eeca10801115.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a5c30001eeca10801115.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>然后,我们运行一遍 Knuth 洗牌算法,就搞定啦:</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5d25a5d0000116c410801121.jpg\" data-original=\"//img1.sycdn.imooc.com/5d25a5d0000116c410801121.jpg\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p><strong>是不是很酷?</strong></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>这就是我喜欢算法的原因。在我眼里,<strong><a href=\"https://coding.imooc.com/class/207.html?mc_marking=a8c5a56d4c08823568d16224c24dc2ab&mc_channel=shouji\">算法</a>从来不是枯燥的逻辑堆砌,而是神一样的逻辑创造。</strong></p>\n</div><div class=\"cl-preview-section\"><p>尽管这个世界很复杂,但竟也如此的简洁,优雅。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>大家加油!:)</strong></p>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><p>后记:</p>\n</div><div class=\"cl-preview-section\"><p>写完这篇文章后,对于文中介绍的 Knuth 大神的中文名是什么,产生了一些小争议。其实,我在文中写错了。为此,我又在我的公众号上写了一篇文章澄清。</p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://mp.weixin.qq.com/s?__biz=MzU4NTIxODYwMQ==&mid=2247484344&idx=1&sn=b534995fb6c5fca3cb49c400282cf7cc&chksm=fd8cacfecafb25e8d594c2b0530cc823a6ead335d76f61007b3f87d22ca396de4c281f386557&token=362759831&lang=zh_CN#rd\"><strong>不小心,较真儿了:高德纳和特朗普</strong></a></p>\n</div><div class=\"cl-preview-section\"><p>学无止境啊:)</p>\n</div><div class=\"cl-preview-section\"><p>···························<br><strong>欢迎关注新课:</strong><br><a href=\"https://coding.imooc.com/class/370.html?mc_marking=6ff301434a6c6c3520dcc6fc3f39f742&mc_channel=shouji\">《玩转图论算法 从入门到精通》(限时优惠中)</a></p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5da0d7d7000196e510000500.png\" data-original=\"//img1.sycdn.imooc.com/5da0d7d7000196e510000500.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div>\n\t\t\t\t\t",
  209. "cover": [
  210. "//img1.sycdn.imooc.com/5d25a2360001b48f09000309.jpg",
  211. "//img1.sycdn.imooc.com/5d25a24300017abc09001200.jpg",
  212. "//img1.sycdn.imooc.com/5d25a29e00011b4e09000273.jpg",
  213. "//img1.sycdn.imooc.com/5d25a2af00011c7f09000351.jpg",
  214. "//img1.sycdn.imooc.com/5d25a2ec0001d93009000342.jpg",
  215. "//img1.sycdn.imooc.com/5d25a5380001fe5a09000332.jpg",
  216. "//img1.sycdn.imooc.com/5d25a548000190f409000436.jpg",
  217. "//img1.sycdn.imooc.com/5d25a5540001c69809000332.jpg",
  218. "//img1.sycdn.imooc.com/5d25a56100019de509000449.jpg",
  219. "//img1.sycdn.imooc.com/5d25a56d0001b1ad09000340.jpg",
  220. "//img1.sycdn.imooc.com/5d25a5b10001d89b09000480.jpg",
  221. "//img1.sycdn.imooc.com/5d25a5a000015bfa10800608.jpg",
  222. "//img1.sycdn.imooc.com/5d25a5c30001eeca10801115.jpg",
  223. "//img1.sycdn.imooc.com/5d25a5d0000116c410801121.jpg",
  224. "//img1.sycdn.imooc.com/5da0d7d7000196e510000500.png"
  225. ],
  226. "mode": "column"
  227. },
  228. {
  229. "id": "26624",
  230. "title": "满满干货,bobo老师诚挚分享:锻炼内功,高效学习,如果有什么秘诀的话,那就都在这里了:)",
  231. "browse_count": 12530,
  232. "collection_count": 87,
  233. "comments_count": 13,
  234. "author": {
  235. "id": "108955",
  236. "author_name": "liuyubobobo",
  237. "avatar": "//img2.sycdn.imooc.com/5347593e00010cfb01400140-160-160.jpg",
  238. "status": "normal"
  239. },
  240. "classify": "职场生活",
  241. "thumbs_up_count": 87,
  242. "create_time": "2018.04.18 16:40",
  243. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><p>大家好。我是讲师liuyubobobo:)</p>\n</div><div class=\"cl-preview-section\"><p>我的新课程<a href=\"https://coding.imooc.com/class/207.html?mc_marking=a8c5a56d4c08823568d16224c24dc2ab&mc_channel=shouji\">《玩转数据结构》</a>已经上线了。这是我在慕课网做的第五门实战课。在这个课程中,我将系统的讲解数据结构领域的内容,从最基础的数组开始,涵盖数组,栈,队列,链表,二分搜索树,集合,映射,堆,线段树,字典树,并查集,AVL,红黑树,哈希表。从初级到进阶,让你成为数据结构的大牛:)</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5ad7065c0001164714310597.png\" data-original=\"//img1.sycdn.imooc.com/5ad7065c0001164714310597.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>在慕课网做了这么多实战课,时间长了,收到了越来越多的同学的问题。除了专业知识上的问题,遇到的最多的问题,就是学习方法方面的问题了。由于我本人不是学习方法的专家,所以不敢轻易说自己的学习方法多么先进。更重要的是,我不认为有一种固定的“好”的学习方法是适合所有同学。如果存在那样的方法的话,学习就太简单了:)由于每个人的背景不同,擅长不同,每个人都有适合自己的不同的学习方法;与此同时,不同的领域,由于其本质不同,学习方法也应该是不同的。我坚信:每个人去寻找适合于自己的学习方法,是每个人一生的话题。别人叙述的所谓的“学习方法”,只能作为参考用。</p>\n</div><div class=\"cl-preview-section\"><p>不过我在慕课网做答疑的过程中,确实看到了很多同学的学习方法,有改进的空间。在这里,我想简单总结一些我见过的,认为可能不是那么得当的学习方法,仅供参考。这篇文章不是一套“如何学习”的理论,而是一些零碎的个人见解。更像是在总结“学习中的那些坑”,不过,在我写完这篇文章之后,回头看,我觉得,我自己的高效学习的秘诀,其实都在这篇文章里了:)</p>\n</div><div class=\"cl-preview-section\"><p>新课上线,在这里,我把这些秘诀毫无保留的向大家分享:)</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>1)不要完美主义!</strong></p>\n</div><div class=\"cl-preview-section\"><p>我观察到的大多数同学犯得最最最最大的“错误”,就是在学习上“完美主义”。乃至后续很多其他的问题,在我看来都和这个问题是直接相关的。</p>\n</div><div class=\"cl-preview-section\"><p>举个最经典的例子,也是我经常举的例子,背英语单词(在这里我们先不聊背英语单词是不是好的英语学习方法,我们只看如果我们想要背英语单词的话,应该怎么背)。我发现很多同学拿着红宝书,第一个list都没翻过去就放弃了。这是因为每天背完第一个list以后第二天会发现:第一个list还是有很多单词没掌握,然后就继续背第一个list。然后一周后,发现自己第一个list都搞不定,觉得英语好难,彻底放弃了。这就是“完美主义”:不把第一个list“彻底”掌握不肯继续前进。这样是不对的。背了一个list,能多记一个词,都是进步。就算一个词都没记住,模糊有了印象,也是一种进步。我们不应该过度着眼于我们还不够完美。学习不是要么0分,要么100分的。80分是收获;60分是收获;20分也是收获。有收获最重要。但是因为着眼于自己的不完美,最终放弃了,那就是彻底的0分了。</p>\n</div><div class=\"cl-preview-section\"><p>仔细想,这种“完美主义害死人”的例子特别多。我看到过很多同学,其实是在学习的路上,被自己的“完美主义”逼得“放弃了”——由于学习中有一点没有做好,遭受到了一点点挫折,最终就放弃了整个学习计划。每个人都一定要接受自己的不完美。想开一点:我们都不是小升初考了满分,才能上初中的;也不是中考考了满分,才能读高中的;更不是高考考了满分,才能念大学的;将来也不会是大学所有科目都是满分,才能出来工作。<strong>不完美其实是常态,根本不会影响我们学习更多更深入的内容。但是在自学过程中,很多同学却要求自己在自己制定的每一步计划中都达到“完美”,才进行下一步。最终结果,通常都是“放弃”:(</strong></p>\n</div><div class=\"cl-preview-section\"><p>可能有的同学会跳出来反驳我:学习当然要认真啊!在这里,我必须强调,我所说的“不要完美主义”,和“学习认真”是不冲突的。什么是“完美主义”,什么又是“囫囵吞枣”,这是一个“度”,每个人其实不一样。不要“完美主义”,不代表学习可以草率前行。每个人都必须要找到适合自己的学习节奏。我的经验是:在一些情况下,问自己一句:是不是自己又犯“完美主义”的毛病了:)</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>2)不要过度“学习路径依赖”,学习要冲着自己的目标去。</strong></p>\n</div><div class=\"cl-preview-section\"><p>什么意思?就是现在信息太发达了,对于大多数领域的知识,网上会有很多所谓的“学习路径”。我不是说这些学习路径没有用,但是不能“过度”依赖这些所谓的学习路径。</p>\n</div><div class=\"cl-preview-section\"><p>比如,很多同学想学机器学习,大多数学习路径都会告诉你,机器学习需要数学基础。于是,很多同学就转而学习数学去了,非要先把数学学好再去学机器学习。可是发现数学怎么也学不好(在这里,可能完美主义的毛病又犯了),而机器学习却一点儿都没学。最终放弃了机器学习,非常可惜。其实,如果真正去接触机器学习,就会发现,至少在入门阶段,机器学习对数学的要求没有那么高。正因为如此,我一直建议:只要你在本科接触过高数,线数,概率这些科目的基础概念,想学机器学习,就去直接学习机器学习。学习过程中发现自己的数学不够用,再回头补数学。在这种情况下,数学学习得也更有目标性,其实效果更好。在这里,我忍不住要打一个我的课程广告,入门机器学习不妨尝试我的<a href=\"https://coding.imooc.com/class/169.html?mc_marking=3619ce94e65f21e9edd48505a6bcd575&mc_channel=shouji\">《Python3入门机器学习》</a>,学过的同学都说好:)</p>\n</div><div class=\"cl-preview-section\"><p>类似这样的例子还有很多,很多同学想学习做ios app,就先去精通swift语言,或者想做android app,就先去精通java语言。在我看来大可不必。以我的经验,只要你有一门编译型语言基础,大概看一下这些语言的基础语法,就可以直接上手ios或者android app的开发了。先能做出一个最基本的app,在这个过程中,就会意识到语言特性的意义,再回头深入研究语言也不迟。此时还能结合真实的开发任务去理解语言特性,比没有上手app开发,抽象地理解语言特性,有意义的多。</p>\n</div><div class=\"cl-preview-section\"><p>再比如,我的<a href=\"https://coding.imooc.com/class/71.html?mc_marking=01de8c81730ff68d7b5dd9471e1c6758&mc_channel=shouji\">《算法与数据结构》</a>课程和<a href=\"https://coding.imooc.com/class/82.html?mc_marking=41fd8da81151c0a122ab9daab4a775fd&mc_channel=shouji\">《玩转算法面试》</a>课程,在视频中都是使用C++进行编码的。虽然我一再强调对预算法的学习,语言不重要,但还是有很多同学表示,要先把C++学透,再回来把课程中的算法学好。这是完全没必要的。事实上,在我的这两门课程中,我看到的收获最大的同学,是那些能够把课程中的算法思想理解清楚,然后用自己熟悉的语言去实现的同学:)</p>\n</div><div class=\"cl-preview-section\"><p>依然是:不要“过度”学习路径依赖,什么叫“过度”,每个人的标准不一样。每个人都需要寻找自己的那个“度”。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>3) 不要迷信权威的“好”教材。</strong></p>\n</div><div class=\"cl-preview-section\"><p>不是说权威教材不好,而是每一本教材都有其预设的读者群,如果你不在这个预设的读者群的范畴里,教材再好也没用。最简单的例子:再好的高数教材,对于小学生来说,都是一堆废纸。</p>\n</div><div class=\"cl-preview-section\"><p>我经常举的一个例子是《算法导论》。我个人建议如果你是研究生或者博士生,已经有了一定的算法底子,才应该去阅读《算法导论》,我在我的课程的问答区,也谈过如何学习使用算法导论。但是对大多数本科同学,尤其是第一次接触算法的同学,《算法导论》实在不是一个好的教材。但很可惜,很多同学在学习中有上面的两个毛病,既过度路径依赖,别人说《算法导论》好,学习算法要走学《算法导论》这个路径,自己就不探索其他更适合自己的学习路径了,一头扎进《算法导论》里;同时还“完美主义”,对于《算法导论》的前几章,学习的事无巨细,但其实接触了很多在初学算法时没必要学习的内容。最后终于觉得自己学不下去了,放弃了对“算法”整个学科的学习。认为算法太难了。</p>\n</div><div class=\"cl-preview-section\"><p>诚然,算法不容易,但是,一上来就抱着《算法导论》啃,实在是选择了一条完全没必要的,更难的,甚至可能是根本走不通的路。对于一个领域的学习,了解市面上有什么好的教材是必要的,单也不能迷信权威教材。每个人必须要去探索学习如何寻找适合自己的学习材料。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>4)不要看不起“薄薄”的“傻”教材,这些你看不起的学习材料,可能是你入门某个领域的关键。</strong></p>\n</div><div class=\"cl-preview-section\"><p>很多同学问我最初学习算法的是什么教材,我告诉他们是这本教材:<a href=\"https://book.douban.com/subject/1173877/\">《算法设计与分析基础》</a> 在这里,我完全没有推荐这本教材的意思。事实上,现在我有点儿“鄙视”这本教材。因为我在学习它的过程中,发现这本教材有很多错误(帮助它纠正错误其实也提高了我的水平:)当然,现在这本书的版本可能也和我当时学习的版本不同了,大部分错误应该已经纠正了。)但它确实是我的一本很重要的算法启蒙教材。关键原因是,它够薄。</p>\n</div><div class=\"cl-preview-section\"><p>在大多数时候,如果有人问我教材推荐,基本上我的回答都是,如果是入门水平:随便找一本在京东,亚马逊,豆瓣上,评分不太差的“薄”的教材,就ok了。在这里,关键字是够“薄”。因为“薄”的教材能让你以最快的速度看完,对整个学科有一个全盘的认识:这个领域是做什么的?解决什么问题了?整体解决问题的思路是怎样?解决问题的方法大致是怎样划分的?一些最基础的方法具体是怎样的。这些在初学阶段是至关重要!是让你全盘把握整个领域脉络的。虽然通过这么一本薄薄的教材,你的脉络把握肯定不够全面细致,但比没有强太多!我看过不少同学,一上来学习《算法导论》,关于复杂度分析的笔记做了好几页,然后就放弃了,可是连什么是动态规划都不知道。这样完全没有对“算法”这个领域有全面的认识,甚至可以说根本没有学过“算法”!先用薄教材入门,再找“厚”教材,细细体会其中的细节,是我百试不爽的学习方法。</p>\n</div><div class=\"cl-preview-section\"><p>另外,在这里,我还要强调“入门教材”,很多教材虽然够“薄”,但不是“入门教材”。大家要注意。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>5)不要迷信单一教材。</strong></p>\n</div><div class=\"cl-preview-section\"><p>很多同学理解了要找“薄”教材入门的道理,却还是非要我推荐一本具体的“薄”教材,说实话,很多时候这让我有点儿哭笑不得。因为我随便推荐一本,我确实不敢保证它是“最好的”,“最适合你的”,但是各个领域那么多教材,我又不可能都一一看过,一一比较过。最最重要的是,我的学习经验告诉我,在大多数情况下,学习不是一本固定教材可以搞定的。非要找到一本“最适合自己的”教材,然后就一头扎进去,其实是不科学的。我印象很深刻,我读本科的时候,那会儿申请了一个项目,要做一个网站(那时候服务端都用ASP.NET),我一口气从图书馆借了10本ASP.NET的教材,然后以一本最薄的书为主干去看,发现这本书介绍不清楚的概念,马上就从其他书里找答案。通常不同的作者对同一个事物从不同的角度做解读,是能够帮助你更深刻的认识一个概念的。基本上一个月的时间,我就从一个完全的网站搭建小白,做出了这个项目需要的那个网站。这个习惯我一直延续,研究生的时候,对什么领域感兴趣了,第一件事就是到图书馆,借十本相关书籍回来翻看。</p>\n</div><div class=\"cl-preview-section\"><p>但是,大多数同学喜欢仅仅扎进一本书里,一旦选定了自己的学习材料,就对其他材料充耳不闻,甚至是排斥的心理。这种做法,一方面又是“完美主义”的表现——非要把这本教材学透;另一方面,其实也是“犯懒”的表现,不愿意多翻翻,多看看,自己多比较比较,自己去寻找最适合自己的材料,一味地盲目相信所谓“大神”的推荐,殊不知,这些推荐,不一定是更适合自己的材料;更何况,还有很多大神,明明是靠不出名的“薄”教材入的门,但给别人做推荐的时候,就突然变成自己是算法奇才,自幼阅读《算法导论》而所成的神话了:)</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>6)实践</strong></p>\n</div><div class=\"cl-preview-section\"><p>前面说了很多和教材选择相关的话题,但对于计算机领域的学习来说,教材的意义其实远远小于实践的意义。如果仅仅是看学习材料就是学习的话,那么慕课网的视频后期处理人员就是水平最高的工程师了。因为每段视频,他们都需要看一遍。但是,很显然,仅仅是看视频,是无法学到知识的。对于计算机领域的学习来说,真正动手实践去编程是异常重要的。怎么夸大其中的作用都不过分。这就好比学游泳,必须下水去游泳;或者学开车,必须亲自上路。否则你说的再头头是道,一个小学生文化水平的人,只要他开过车,游过泳,都能在这两个领域瞬间秒杀你。</p>\n</div><div class=\"cl-preview-section\"><p>很多同学都说我的算法讲得好,其实,我一直认为,这其中的一个最简单的秘诀就是:我带领大家把大多数算法都非常细致的实现了一遍;或者对其中的应用进行了非常具体的实践。反观大多数高校教育,对于算法或者机器学习这种一定程度偏理论的学习,通常非常不强调实践。最终的结果是学习者只是接受了很多抽象的概念,但对其中具体的实现细节,却是云里雾里。我见过太多同学,都明白什么是O(n<sup>2)复杂度,什么是O(nlogn)的复杂度,却问我对于100万的数据规模,为什么自己的选择排序运行起来就没反应了。答案很简单:O(n</sup>2)的复杂度太慢了,100万的数据规模太大了,一般家用计算机转选择排序一时半会儿是转不完的。这些同学一定理解O(n^2)的算法比O(nlogn)的算法慢,却没有真正实践过,不知道这个差距到底是多少。</p>\n</div><div class=\"cl-preview-section\"><p>在我的课程中,经常遇到有些同学提出这样的问题:这个算法的某句话(或者某段逻辑),为什么要写成A的样子,而不是B的样子?这种问题其实很好,但我觉得解决方法也很简单,实际的去把算法改写成B的样子,实际的运行试试看,看会发生什么。如果发生了错误,仔细分析一下,为什么会有错误?如果没有错误,具体比较一下:A和B两种不同的写法,为什么都正确?又有什么区别?真正的学习上的提高,就发生在这个过程中。我当然可以告诉给同学们一个结果,但是自己亲自实践一遍,相比阅读我给出的一个答案,自己对其中问题理解的深刻程度,是完全不可比拟的。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>7)debug非常非常重要。</strong></p>\n</div><div class=\"cl-preview-section\"><p>我看到的另一类“经典”问题就是:老师,这个代码为什么错了,然后贴一大段代码。这种问题背后,依然是,透露着学习方法的不对劲:提问的同学懒得debug。在计算机领域,debug近乎和实践是一个意思。如果只是把材料上的代码“抄”一遍,这不叫实践,这叫抄代码。小学生也能做。但是“抄”一遍,不小心没抄对,发生了错误,然后自己一点一点调试,找到错误的根源,这叫真的实践。小学生不能做。(当然,我更推崇的是:自己理解了算法的逻辑,按照自己的理解,把算法写出来:)</p>\n</div><div class=\"cl-preview-section\"><p>不过很多同学不喜欢debug,我当然理解。其实谁都不喜欢debug,但是,debug才是最重要的能力。(通常在一个领域里,你最不喜欢做的事情,就是这个领域的核心竞争力:)是计算机领域异常重要的一项技能。我见过的所有计算机领域的“高手”,不管是在哪个细分领域,都无一例外,是个debug好手。我经常告诉大家,在实际工作中,其实debug的时间要占你真正编程时间的70%。如果你做一个项目,根本不需要debug,要么是你的项目对你来说太简单了,要么是你根本没有接触到这个项目的核心。</p>\n</div><div class=\"cl-preview-section\"><p>debug不仅仅是找到代码错误,解决错误的手段,其实更是一个重要的学习手段。通过debug,看看自己写的程序执行逻辑,哪里和自己设想的不一致?再回头看自己哪里想错了,或者想漏了,分析一下自己为什么想错了,或者想漏了,等等等等,依然是,进步就是发生在这个过程的。</p>\n</div><div class=\"cl-preview-section\"><p>在我的算法课程中,很多同学对递归想不明白,我的建议都是:用一个小数据量,一步步跟踪程序,看看程序到底是怎么运行的。通常这么做,1个小时的时间,就足以让你深刻理解递归程序的运转逻辑。可是,很多同学懒得花这1个小时的时间,最终的结果是,花了一个下午,对着代码生看,硬想,最终还是没有理解程序的运转逻辑。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>8)量变到质变。</strong></p>\n</div><div class=\"cl-preview-section\"><p>还有很多同学,对于算法的一些问题,会问:老师,你是怎么想到用这样的方法的?对于这类问题,我的回答一般都是:你见的还不够多。</p>\n</div><div class=\"cl-preview-section\"><p>不知道是不是受高中阶段学习的影响,有一些同学特别执着于就着一个单一的问题,寻找其中的“解题路径”。当然,我不是说这是完全错误的,但也有一个“度”。我的经验是:与其把时间花在这里,不如去见更多问题。比如动态规划,是算法学习的一个难点,很多同学在学会了背包问题的解法之后,总是执着于去追寻:是怎么想到这种状态定义的方法的。可能是我个人水平有限,我无法清楚地解释是如何想到这种状态定义的方法的。但是我的经验告诉我:再去看,去实践100个动态规划相关的问题,然后回头看背包问题,你会发现这种状态定义的方式非常自然。仅仅对着一个问题思考,很多时候都是死胡同。你见识的还不够多,就不足以帮助你总结出更加“普遍”的问题解决的规律。当你见得足够多的时候,一切就都变得很自然,所谓的“量变到质变”。</p>\n</div><div class=\"cl-preview-section\"><p>不过,大多数同学在这个环节都会“犯懒”,企图通过一个问题就理解问题的本质,这其实和企图通过一本教材就精通一个领域的想法是一样的,是不现实的,不可能的。同时,这里又包含着学习过程中的“完美主义”的思想,遇到一个问题一定要把它想的无比透彻。但是我的经验告诉我:大多数问题,其实都是需要“回头看”的。随着你对一个领域理解的越深入,回头再去看那些曾经的问题,都会产生新的视角,对于很多曾经想不明白的问题也豁然开朗。这也是“进步”的根源。如果卡在一个问题上不前进,不给自己“回头看”的机会,甚至最后是放弃了,就什么也没有学会了。</p>\n</div><div class=\"cl-preview-section\"><p>所以,很多时候,你发现对一些问题“百思不得其解”,或许不是因为自己“笨”,而是因为“还不够努力”:)</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p><strong>9)最后,一定要相信时间的力量。</strong></p>\n</div><div class=\"cl-preview-section\"><p>有一天,在我的一个算法课程群里,有个滴滴的后端大神发招聘,结果大家七嘴八舌的就议论开了,大致主题思想就是:自己什么时候能够成为滴滴的后端大神。这位滴滴的后端大神今年32岁;大多数议论的同学,其实连22岁都不到。我告诉他们,其实10年后,你们就是大神。</p>\n</div><div class=\"cl-preview-section\"><p>这其实很好理解,回想十年前,也就是12岁的你,和现在的你比较,是不是天壤之别?如果把你扔到一堆12岁的小朋友中间,22岁的你是不是就是个大神?同理,32岁的人,已经在业界摸爬滚打了那么多年,扔回到22岁的大学生中间,当然是大神:)</p>\n</div><div class=\"cl-preview-section\"><p>很多时候,所谓的“大神”并不神秘,很多时候,仔细观察,会发现时间有着不可磨灭的作用。只要你没有虚度时间,每天都在进步,通常结果都不会太差的。如果再加上一点点机遇,可能就不仅仅是大神。</p>\n</div><div class=\"cl-preview-section\"><p>愿大家也早日成为大神。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>最后硬广:在计算机专业成为大神的路上,算法和数据结构是绝对绕不开的内容。在我的新课<a href=\"https://coding.imooc.com/class/207.html?mc_marking=a8c5a56d4c08823568d16224c24dc2ab&mc_channel=shouji\">《玩转数据结构》</a>中,我也从另一个角度阐述了为什么。最近美国制裁中兴禁止卖给中兴芯片的事件搞得沸沸扬扬。算法和数据结构,就是每一个计算机技术人的“芯”,怎么强调都不为过。我的所有课程,都是围绕着这个“芯”来讲解的。有一名同学的课程评论特别让我感动:</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5ad702d70001a59008640155.png\" data-original=\"//img1.sycdn.imooc.com/5ad702d70001a59008640155.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>这恰恰就是我的课程设计目标。希望通过我的课程学习,每位同学都能获得不同的,真正的,计算机技术内力的提高。</p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>我的所有课程传送门:<a href=\"https://www.imooc.com/u/108955\">https://www.imooc.com/u/108955</a></p>\n</div><div class=\"cl-preview-section\"><br></div><div class=\"cl-preview-section\"><p>我的新课传送门:<a href=\"https://coding.imooc.com/class/207.html?mc_marking=a8c5a56d4c08823568d16224c24dc2ab&mc_channel=shouji\">《玩转数据结构,从入门到进阶》</a></p>\n</div><div class=\"cl-preview-section\"><p>大家加油!</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5ad7065c0001164714310597.png\" data-original=\"//img1.sycdn.imooc.com/5ad7065c0001164714310597.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><hr></div><div class=\"cl-preview-section\"><p>关于我的更多文章,欢迎关注我的个人公众号:是不是很酷:)<br><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5e17558c000196e510000500.png\" data-original=\"//img1.sycdn.imooc.com/5e17558c000196e510000500.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div>\n\t\t\t\t\t",
  244. "cover": [
  245. "//img1.sycdn.imooc.com/5ad7065c0001164714310597.png",
  246. "//img1.sycdn.imooc.com/5ad702d70001a59008640155.png",
  247. "//img1.sycdn.imooc.com/5ad7065c0001164714310597.png",
  248. "//img1.sycdn.imooc.com/5e17558c000196e510000500.png"
  249. ],
  250. "mode": "column"
  251. },
  252. {
  253. "id": "291507",
  254. "title": "培训班出身如何构建自己的知识体系?",
  255. "browse_count": 5077,
  256. "collection_count": 72,
  257. "comments_count": 9,
  258. "author": {
  259. "id": "2255006",
  260. "author_name": "bobby",
  261. "avatar": "//img2.sycdn.imooc.com/58d9c48f0001ad0304070270-160-160.jpg",
  262. "status": "normal"
  263. },
  264. "classify": "职场生活",
  265. "thumbs_up_count": 72,
  266. "create_time": "2019.08.23 11:09",
  267. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><h5>1. 如何看待英语和数学在编程中的重要性?</h5>\n<pre><code>先给出结论吧: 重要但是不紧急。\n英语和数学属于四象限原则中的: 重要但不紧急,同样属于这个象限的还包括基础知识:数据结构、算法、计算机网络、操作系统等\n这个象限属于很容易被忽略,大部分人的思维都是把精力放在学习哪些紧急并且重要的知识点上很容易忽略这个象限的知识点,\n可以这样一句话描述这个象限的知识点的重要性:其他知识点决定你能走多快,而这些知识点决定你能走多远。\n既然这个问题是说明英语和熟悉的重要性,那我们来说一下这两点到底重不重要:\n 1. 重要!\n 2. 学不好这两门也能学会编程并能达到很多职位的需求\n 3. 想要学好编程英语和数学相当有必要\n 4. 不一定需要首先学好英语和数学,也可以在学习编程的过程中去学习数学和英语\n\n那英语和数学在编程中的重要性体现在哪里?\n 英语:\n 1. 专业词汇\n 2. 看英语文章\n 1. 很多优秀的库和框架或者官方文档都是英文的\n 2. 大量的源码中都会用英语作为注释\n 3. 很多优秀的问题社区(stackoverflow)也是英文的\n 4. 很多时候你也需要写英文文档和英文注释\n 数学:\n 1. 算法很重要,数学思维和逻辑能力\n 2. 很多算法都会有数学公式(机器学习算法尤其突出)\n 3. 大量的问题的深入原理背后都是数学支撑的\n</code></pre>\n</div><div class=\"cl-preview-section\"><h5>2. 培训班和自学真的有区别吗?</h5>\n<pre><code>培训班和自学没有本质的区别:都是要学习!\n编程的学习有一个很重要的特性:学懂是第一位\n很多知识是属于别人给你说了你就知道了,比如很多库和框架的使用,但是灵活运用需要配合实战项目或者大量的项目反复训练。\n另一些知识属于即使讲解了很多人貌似听懂了但实际上没有弄懂,比如很多算法,算法中有一些算法不属于唯一答案,比如动态规划,需要配合大量的题目才能彻底弄懂\n还有一些很重要但是不紧急的知识点比如数据库的底层原理,很多库和框架的底层原理(elasticsearch,kafka等),这些很多时候没人能提醒你该学或者告诉你,需要工作中去学习。\n\n其实上面可以看到我们要学习的知识非常多,这些很难在一两年时间内就搞懂。\n那接下来我们看看作为初学者,这里说到的初学者不仅仅只是转行或者刚毕业的人,对于有工作经验的人来说,比如andriod开发转python开发,转人工智能开发,这个时候也属于初学者。\n对于初学者来说更加紧急和重要的是什么呢(PPT)?\n 1. 全面的知识结构体系,比如有哪些实际的项目能锻炼人?每个方向的知识点都很多,但是哪些知识点更重要?哪些是必学的?\n 2. 具体到需要学习的知识点的时候?有哪些库或者框架是必学的?\n\n培训班的好处:\n\n 1. 快速建立知识体系结构\n 2. 少走弯路(有些弯路一旦走了可能会耽误你1、2年时间)\n 3. 培训班教的也是知识,知识不应该计较从哪里来的\n 4. 培训班集中的学习时间和氛围是自学比不了的(自学很难达到集中不间断的学习5,6个月)\n 5. 明确的目标能让学习更加高效\n 6. 好的培训机构和好的老师很重要\n 7. 对于转行的人来说,自学难度很大,培训班能够帮你快速入行\n\n培训班的缺点:\n 1. 参差不齐的质量\n 2. 明确的目的性导致没有大量的讲解重要但不紧急的基础知识\n 3. 后续的学习还需要自己\n 4. 虽然集中的时间已经很长(5个月以上),但是相对于个人的长期成长需要学习的知识还是很短\n\n个人学习到底有低效相信对于绝大部分的人来说都是很清楚的,但是个人学习依然很重要:\n \n 1. 长期不间断的学习\n 2. 找一个大牛(工作和生活中)模仿或者交流\n 3. 明确自己的目的(比如一个后端需要学习前端,就要集中学习,需要学习人工智能也是一样)\n 4. 自学的作用不可替代\n 5. 找到适合自己学习的方法\n 6. 有些知识点听不懂可能是别人讲不明白,多看看不同人的不同角度的讲解。 不要质疑自学的重要性和能力\n 7. 自己没有从事的行业也可以适当关注\n</code></pre>\n</div><div class=\"cl-preview-section\"><h4>3. 为什么培训班出来的就不受待见?</h4>\n<pre><code>培训班因为是短期内需要帮助找工作所以课程规划的时候更多的是框架和库的使用:\n 1. 这样本身没有问题,毕竟绝大部分公司面试还是很看重框架和库的\n 2. 底层知识缺乏,很容易导致很多人开始工作能胜任,但是随着系统越来越大,由于缺乏底层导致很多人胜任不了\n 3. 培训机构包装简历,能力和简历上的不一样\n\n无论如何,学到了就是学到了,不应该计较从哪里来,为什么很多公司强调不喜欢培训机构出来的,是为了提高招聘效率,不是否认培训机构的作用\n就像很多机构招聘喜欢通过学历筛选简历一样,毕竟很多hr面临的是一个绩效问题,所以也喜欢从高概率的人群中去筛选\n但是对于有工作经验的人来说,工作经历和项目经历更加重要,所以不用担心将来,现在需要做的就是把知识学到手。\n\n其实招聘公司不喜欢培训机构出来的和很多公司喜欢在学历上卡人是一样的原理:提高筛选效率!\n</code></pre>\n</div><div class=\"cl-preview-section\"><h5>4. 大公司和小公司更看重什么?</h5>\n<pre><code>大公司:\n 算法(只占一部分)\n 基础\n 计算机网络、操作系统、数据结构、计算机原理等\n\t框架和库的底层原理(数据库底层存储原理,es和kafka等底层存储原理)\n\t某些框架的核心流程\n \n 原因:大公司有钱等到新人成长学习,更加关心的是遇到问题分析和解决问题的能力,因为大公司开发的项目更加注重稳定性和并发性等,这些问题需要解决会涉及到\n 分析底层,分析底层往往需要我们具备底层的基础知识,但是底层基础知识本身比较枯燥而且学习周期长,相比较而已选择更懂底层基础的就显得更加划算了,大公司有很多\n 项目都是对大型项目的维护和很多优化工作,这些对基础要求比较高\n中小公司:\n 实际项目经验\n 库和框架的熟悉程度\n \n 中小公司面临的是快速开发和新项目的开发过程,没有那么多钱支撑很多新人的学习,要知道程序员一天的工资可不低,在钱不够的情况下还等你学习慢慢学习不现实\n 所以要求一上来就开发显得更实际了,很多中小公司新人一进公司(特别是前端)基本上当天就开始写页面,这在大公司不太可能,光是熟悉已有的代码和规范都需要不少时间\n\n大公司和小公司都很重视实际项目经验,但是侧重点可能会不一样(因人和因公司有差异)\n大公司和小公司这样筛选无可厚非,小公司面临生存,大公司需要维护和优化\n</code></pre>\n</div><div class=\"cl-preview-section\"><h4>5. 如何避免培训出来以后被歧视?</h4>\n<pre><code>1. 包装简历(初学者找工作)\n\t简历是可以包装的,比如工作年限(查学历没办法,但是相关经验年限确可以自己写),所有公司都在意工作年限。\n\t项目经验可以包装,但是一定不要写的太多,一定要写自己熟悉的和最好是做过的\n\t有些能吹的不好说(因为有些面试官真的会吃这一套)\n2. 巩固基础(工作以后)\n\t巩固基础是一个长期的过程,想要一下子学好不现实,配合做实际工作学习效果更佳\n3. 公司最喜欢招聘的是有相关工作经验的\n4. 项目经验很重要,但是要慎重,一旦写上简历了,对方如果对你的项目经验感兴趣会追着问(你遇到哪些问题?是怎么解决的?)\n</code></pre>\n</div><div class=\"cl-preview-section\"><h4>6. 如何在职场上走的更远?</h4>\n<pre><code>1. 保持学习\n 学习是当今以及以后永远不变的一个趋势,只有保持不断的学习才会让我们不会受到年龄的限制和不会被淘汰,it技术尤其明显。低廉的升级成本会导致技术变化很快。所以我们需要保持不断的学习态度。\n2. 保持兴趣\n 长期学习最大的动力就是兴趣,所以我们需要保持兴趣。兴趣和学习在很大程度上是相互促进的,也就是说兴趣会促进学习,学习也会促进兴趣。\n3. 除了做好本职工作以外,可以提出更多的建设性意见\n 职场是非常在意产出的,比学习更重要。但是这种产出在很多时候不一定要以技术的方式表现,在中小型公司尤其突出,比如在产品设计上多提一些意见,比如在产品的框架设计上考虑远一点,比如通过技术的手段帮助营销提高效率。这些都不一定是技术产出,但是却决定了你的不可替代性,这里需要重点提醒大家就是:一定要有主动性!!!,很多事情不是主动来找你,而是你主动提出来的!!!,所以不要抱怨你没有机会。\n4. 多从产品和测试的角度考虑问题\n 产品质量和稳定性不论是对小企业还是大企业来说第一位,质量的保障最重要关卡就是测试,所以对于很多有经验的人来说会把测试看的很重要,很多有经验的程序员在开始写代码之前就会在脑子里过一下应该有哪些测试点,所以对于我们来说也是一样的。\n5. 关注架构层面的知识\n \n 刚进公司我们把精力都放在自己负责的模块上无可厚非,但是有两三年工作经验以后我们不管是学习还是有新项目的时候我们要多从架构层面去考虑问题:\n 1. 可以使用哪些组件?是考虑稳定性?是考虑生态?是考虑性能?\n 2. 使用什么语言?优缺点是什么?\n 3. 代码和模块设计的时候是否应该考虑到扩展性?\n 4. 代码之间如何尽量降低耦合性?\n 5. 如何将服务拆分?\n 6. 如何让组员之间能够快速的沟通和替换?\n</code></pre>\n</div><div class=\"cl-preview-section\"><h5>7. 职业迷茫的时候应该怎么办?</h5>\n<pre><code>1. 会在哪些方面产生迷茫?\n 方向深入\n 换方向\n 如何技术变现\n2. 迷茫的时候改怎么办?\n\t1. 学好技术\n\t2. 关注新领域\n3. 技术可以追求一专多能\n4. 封装好自己的可重用的代码,为以后工作或者私人项目做准备\n5. 变现途径\n \n 外包\n 长期维护一个自己的项目(开源或者商业)\n 程序员客栈\n 非it技术类\n 数据服务(分词服务,某些难的技术点)\n</code></pre>\n</div><div class=\"cl-preview-section\"><h5>8. 如何构建自己的知识体系?</h5>\n<pre><code>1. 先语言\n2. 有些基础是通用的:\n\t语法基础\n\t数据结构\n\t计算机网络\n\t操作系统原理\n\t数据库\n\t mysql\n\t redis\n\t mongodb\n\t elasticsearch\n\t 数据库优化\n\t \n\t基础算法(排序,图,树,数组等)\n\n3. 不同的方向需要学习的差异比较大\n\t1. web开发\n\t\t框架\n\t\t高并发\n\t\t分布式(分布式锁、哈希一致性)\n\t\t各种组件(redis、kafka)\n\t\tnosql(mongodb、elasticsearch)\n\t\t图数据库(neo4j)\n\t\t数据库的原理和各种优化\n\t2. 数据分析\n\t\t机器学习\n numpy\n pandas\n Matplotlib\n \n\t3. 人工智能\n\t 深度学习\n 高等数学\n 线性代数\n 统计学\n\t4. 爬虫\n\t 计算机网络\n http协议详细\n ip代理原理\n js和html等前端知识\n scrapy等异步爬虫框架\n selenium和动态网页\n 抓包\n js逆向\n\t5. app开发\n\t 混合开发\n flutter\n react native\n Android\n ios\n\t6. 物联网开发\n\t7. 游戏开发\n</code></pre>\n</div><div class=\"cl-preview-section\"><h5>9. 有哪些高效的学习方法?</h5>\n<pre><code>如果说学习有一种万能的方法那都是扯淡,不同的学习方法对于不同的人来说效果不一样,不过为了学好编程,有一些常用的方法还是很有效的,大家根据自己的情况可以考虑一下:\n\n1. 算法靠刷题(leetcode, kaggle)\n\t算法最好的掌握方式就是刷题,这点和我们学习基础学科比如数学,物理等一样。我们很难通过具体的实战来概括各种算法\n2. 多做项目(架构、代码规范等)\n 做项目还是以解决问题为主,兼顾考虑如何提升代码的重用性和稳定性。如果很多项目都是重复的技术点,对于学习来说效率也比较低,但是如何每次做相同的代码都能去重构之前已有的代码对自身学习确是很好的。\n3. 关注一些技术博客\n 慕课网、微博、知乎、公众号、优秀的github开源项目都是很好的学习地,有时候你关注的某个博客不经意间推的某一篇文章能帮你大忙。也能拓宽你的视野。\n4. 写自己的博客,记录下自己的问题和解决过程。\n 自己遇到的问题以及解决方案记录下来,不论是对你以后找工作还是分享给别人以及交朋友都是非常好的,甚至可能给你带来意想不到的收获,不过要尽量原创。写博客的过程也能发现自己理解不到位的地方。\n5. 多和牛人交流\n 这一点放在最后是以为很多人可能都没有机会和大牛一起共事并有机会经常讨论问题,所以如果你遇到了,一定要紧跟着他。长期的共事你会发现你的收获可能远超过上面的方法。\n</code></pre>\n</div><div class=\"cl-preview-section\"><h5>10. 这两项最重要的技能必须要掌握!!!</h5>\n<pre><code>1. debug和看错误栈\n\n http://coding.imooc.com/learn/questiondetail/138046.html\n\n debug是一项非常非常重要的技能,配合debug的是错误栈。\n 对于完全不懂编程的同学来说不懂debug和看不懂错误栈可以理解。\n 对于已经学过以后还是不知道怎么debug,比如如何打断点?如何进入函数?如何跳过某一行代码?如何执行到下一个断点?如何查看变量的值真的说不过去。这是每个编程人员都应该懂得的基础。\n 我不是很建议使用print这种方式去debug的,在pycharm中直接以debug方式运行再好不过了。这点我在我的所有课程中都是尽量使用debug方式讲解的。\n \n 错误栈对于debug简直就是标配啊,不要怕错误栈,有错误栈是好事。\n \n \n2. 信息搜索(google+stackoverflow)\n\n 我更加推荐使用google(上google也是程序员的一个基本,而且我发现google上python的问题比用百度多太多而且都是很对症的。)\n 之前也尝试用google和百度尝试过分别搜索java和python,发现百度搜索java更好用(可能和我搜索的问题有关),但是python一定是google比百度好用太多。\n 最好用的就是google里面搜索出来的stackoverflow上的问题。\n \n google搜索也是一种能力,如何去找到自己应该通过哪些关键字搜索也是一种能力。\n \n 有很多问题不用搜索答案而是应该找到你自己的答案上的问题,比如自己的代码造成的key error, 如果我们能从错误栈中找到具体是我们自己写的某一行代码出错,这个时候你去google很有可能找不到答案,这种问题解决很简单。\n 很多异常比如不是我们自己的代码而是使用的某个库的代码中出现了这个异常那么就可以去google了。\n \n3. 看源码(有时候文档无法兼顾所有接口的用法,这个时候需要懂看源码)\n没有必要一味的求取看源码和懂原理,先学会使用\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>···············<br><strong>欢迎关注课程:</strong></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\">从零起步 系统入门Python爬虫工程师</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/200.html?mc_marking=a01120f2fb86e9f48ae322891c21d16b&mc_channel=shouji\">Python高级核心技术97讲</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/131.html?mc_marking=d031c71db291fc706002cb77ce64b4a4&mc_channel=shouji\">引爆潮流技术 Vue+Django REST framework打造生鲜电商项目</a></p>\n</div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/78.html?mc_marking=0d1ef9e4873859826694a9707dca04b4&mc_channel=shouji\">Python2.7到3.6完美升级 强力django+杀手级xadmin</a></p>\n</div>\n\t\t\t\t\t",
  268. "cover": [],
  269. "mode": "base"
  270. },
  271. {
  272. "id": "283364",
  273. "title": "小白如何入门爬虫",
  274. "browse_count": 6638,
  275. "collection_count": 42,
  276. "comments_count": 10,
  277. "author": {
  278. "id": "2255006",
  279. "author_name": "bobby",
  280. "avatar": "//img2.sycdn.imooc.com/58d9c48f0001ad0304070270-160-160.jpg",
  281. "status": "normal"
  282. },
  283. "classify": "后端开发",
  284. "thumbs_up_count": 42,
  285. "create_time": "2019.03.19 11:46",
  286. "content": "\n\t\t\t\t\t\t<p>在学习<a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\" target=\"_blank\">爬虫</a>之前我们需要明白的一个问题:</p><p><strong> <a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\" target=\"_self\">爬虫能做什么?</a></strong></p><p>爬虫除了能够获取互联网的数据以外还能够帮我们完成很多繁琐的手动操作,这些操作不仅仅包括获取数据,还能够添加数据,比如:</p><p> 1. 投票<br> 2. 管理多个平台的多个账户(如各个电商平台的账号)<br> 3. 微信聊天机器人</p><p>实际的应用远不止上面这些,但是上面的应用只是除开数据本身的应用而已,数据本身的应用也是很广的:<br> 1. 机器学习语料库<br> 2. 垂直领域的服务(二手车估值)<br> 3. 聚合服务(去哪儿网,美团)<br> 4. 新闻推荐(今日头条)<br> 5. 预测和判断(医疗领域)<br> <br>所以爬虫能做的功能非常多,也就造就了爬虫的需求也是越来越旺盛,但是很多有过后端开发的人员却觉得爬虫很简单,很多人觉得爬虫用一个库(requests)去获取一个html然后解析就行了,实际上爬虫真的这么简单吗?<br><br> 首先回答学习之前我们来问几个问题:<br> <br> 1. 如果一个网页需要登录才能访问,怎么办?<br> 2. 对于上面的问题,很多人说模拟登录就行了,但实际上很多网站会采用各种手段去加大模拟登录的难度,如:各种验证码,登录逻辑的各种混淆和加密、参数的各种加密,这些问题都怎么解决?<br> 3. 很多网站只能手机登录怎么办?<br> 4. 很多网站为了用户体验和服务器优化,会将一个页面的各个元素采用异步加载或者js加载的方式完成?这些你有能力分析出来吗?<br> 5. 作为一个网站,各种反爬的方案也是层出不穷,当你的爬虫被反爬之后,你如何去猜测对方是怎么反爬的?<br> 6. 一个爬虫怎么发现最新的数据?如何发现一个数据是否被更新了?<br><br>如果你只是做一个简单的爬虫,比如你的爬虫就是一次性的,一次性获取某个网站的某些数据这样当然就简单了,但是你要做一个爬虫服务,你就必须要面对上面的问题,这上面还没有提到数据的提取和解析等等:<br><br></p><p> 综合上述问题接下来看一下我们要学习什么:</p><p> <a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\" target=\"_blank\"><strong> 第一阶段 基础入门</strong></a>:</p><p> 1. 计算机网络的基础,包括:tcp/ip协议、socket网络编程、http协议<br> 2. 前端的基础:主要是javascript基础和ajax基础<br> 3. python的基础语法<br> 4. 数据库的基础:任何一个数据库都行,但是强烈建议学习mysql或者postgresql<br> 5. html解析的基础:beautifulsoup的使用、xpath和css选择器<br> 6. html下载的基础:urllib或者requests使用<br> 7. 数据保存的基础:如果你要使用的是关于数据库(mysql)的话可以使用pymysql、接下来使用peewee,如果你需要使用的是文档数据库(mongodb)的话,可以选择pymongo,然后使用mongoengine<br> <br><a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\" target=\"_self\"><strong> 第二阶段 爬虫实战</strong></a><br> <br> 经过前面的阶段,你只是具备了最基本的爬虫知识而已,想要真正的抓取爬虫你还需要更进一步的学习<br> <br> 1. 模拟登录:你需要知道cookie和session登录的原理、如果需要针对性的抓取微博等你还需要知道oauth2.0的具体过程<br> 2. 动态网页分析技术: 最基本的方法是通过分析js和html等基础方法,但是很多网站会将这部分逻辑做的很复杂,所以你需要进一步学习selenium和chromedriver相关的基础<br> 3. 验证码的识别:<br> 这里包括最基本的验证码识别,比如ocr识别等,对于更复杂的验证码如果想要自己去识别的话你还得懂机器学习和图像识别技术,简单的方法就是调用第三方服务<br> 4. 对于反爬,你需要懂nginx的基本配置,你需要更一步熟悉http协议的细节<br> 5. 爬虫的开发需要配置多线程开发,所以你需要更加懂多线程的开发,这里包括了线程间通信和线程同步等基础<br> <br> <a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\" target=\"_blank\"><strong> 第三阶段 爬虫监控和运维</strong></a><br> <br> 一个爬虫上线生产环境你得监控你的爬虫吧,监控一个爬虫你最好是用页面管理吧,所以你得懂:<br> <br> 1. linux基础,用于部署服务<br> 2. docker基础,docker部署的优势和流行相信大家都懂的<br> 3. django或者flask,因为我们需要开发页面去监控爬虫<br> <br><a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\" target=\"_blank\"><strong> 第四个阶段 爬虫框架和分布式爬虫</strong></a><br> <br> 1. 你得懂至少一门爬虫框架scrapy或者pyspider<br> 2. 懂了scrapy你还需要知道scrapy-redis知道如何去解决分布式爬虫的问题<br> 3. 你得懂分布式存储的方案:hadoop的一套解决方案<br> 4. 你得懂mongodb文档数据库<br> 5. 你得懂elasticsearch搜索引擎<br> 6. 你得懂kafaka这种分布式发布订阅消息系统<br> 7. 分布式相关的基础如分布式锁等你需要知道原理<br> <br><a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\" target=\"_blank\"><strong> 第五个阶段 爬虫的应用</strong><br> </a><br> 这个阶段就是属于应用的领域了,比如你要做人工智能,你得懂人工智能的相关知识,你如果做数据分析你得学习数据分析的基本知识,如果你是想做web服务你需要学习web开发的基础,如果你是想做搜索引擎和推荐系统你得懂相关的基础才行。</p><p>·····································</p><p><strong>欢迎关注课程:</strong></p><p><strong><a href=\"https://coding.imooc.com/class/325.html?mc_marking=5c1e3f6cdb5438f2a12b29220c268688&mc_channel=shouji\" target=\"_blank\">从零起步 系统入门Python爬虫工程师</a>(新课)</strong></p><p><a href=\"https://coding.imooc.com/class/131.html?mc_marking=d031c71db291fc706002cb77ce64b4a4&mc_channel=shouji\" target=\"_blank\"><strong>引爆潮流技术 Vue+Django REST framework打造生鲜电商项目</strong></a></p><p><strong><a href=\"https://coding.imooc.com/class/78.html?mc_marking=0d1ef9e4873859826694a9707dca04b4&mc_channel=shouji\" target=\"_blank\">Python2.7到3.6完美升级 强力django+杀手级xadmin</a></strong></p>\n\t\t\t\t\t",
  287. "cover": [],
  288. "mode": "base"
  289. },
  290. {
  291. "id": "295301",
  292. "title": "多人实时互动之各WebRTC流媒体服务器的比较",
  293. "browse_count": 1939,
  294. "collection_count": 4,
  295. "comments_count": 2,
  296. "author": {
  297. "id": "4873493",
  298. "author_name": "李超",
  299. "avatar": "//img4.sycdn.imooc.com/5b9876c60001ffc914821482-160-160.jpg",
  300. "status": "normal"
  301. },
  302. "classify": "移动开发",
  303. "thumbs_up_count": 4,
  304. "create_time": "2019.11.09 18:41",
  305. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/387.html?mc_marking=14d51eb28e859859dae4fe1063d81518&mc_channel=shouji\">前言</a></h2>\n</div><div class=\"cl-preview-section\"><p>随着网络基础设施的提高,音视频实时通信越来越成为人们日常生活和工作中必不可少的需求。2011年 WebRTC的出现,则更加速了这种需求变为现实的可能性。</p>\n</div><div class=\"cl-preview-section\"><p>熟悉 <a href=\"https://coding.imooc.com/class/387.html?mc_marking=14d51eb28e859859dae4fe1063d81518&mc_channel=shouji\">WebRTC</a> 的同学应该都知道,WebRTC规范只定义了实时通信中客户端的行为,而没有规范服务端(包括哪些信令、数据如何流转)的行为。所以,你可以使用WebRTC库方便的实现 1:1 实时通信,但对于多人实时互动,光依靠 WebRTC库显然就无法完成要求了。</p>\n</div><div class=\"cl-preview-section\"><p>那我们该如何实现多人实时互动通信呢?</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/387.html?mc_marking=14d51eb28e859859dae4fe1063d81518&mc_channel=shouji\">WebRTC 流媒体服务器</a></h2>\n</div><div class=\"cl-preview-section\"><p>要想实现多人的实时互动,如音视频会议、在线教育这类产品,我们必须使用 WebRTC + WebRTC流媒体服务器这种方案。</p>\n</div><div class=\"cl-preview-section\"><p>目前有很多比较有名的开源流媒体服务器,如 Janus、Medooze、Mediasoup、Licode(OWT)、Jitsi等等。这些流媒体服务器各有优缺点,下面我就对这几种流媒体服务器作下简要的介绍与比较。</p>\n</div><div class=\"cl-preview-section\"><p>通过本文,你将知道各 WebRTC 流媒体服务器的优缺点,并依俱它们的优缺点选择出更适合你的那款WebRTC流媒体服务器。</p>\n</div><div class=\"cl-preview-section\"><h3>Mediasoup</h3>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dc698450001a9f712760906.png\" data-original=\"//img1.sycdn.imooc.com/5dc698450001a9f712760906.png\" alt=\"Mediasoup架构图\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>上图是Mediasoup整体架构图,通过该图我们可以知道 Mediasoup 流媒体服务器是由 Nodejs 和 Mediasoup(C++) 两部分组成。</p>\n</div><div class=\"cl-preview-section\"><ul><li>Nodejs,负责 Mediasoup 的信令接收与业务管理。如创建/消毁房间,创建/关闭生产者,创建/关闭消费者等。</li>\n<li>Mediasoup(C++),这是一个单独的程序,但该程序无法直接启动。因为它在内部会判断是否是 Nodejs 将它启动起来了。只有在Nodejs 的 Mediasoup 管理模块加载之后,再将 Mediasoup(C++)启动起来,这样它才能正常工作。</li>\n</ul></div><div class=\"cl-preview-section\"><p>在众多的 WebRTC 流媒体服务器中,Mediasoup 可以说是性能最优秀的WebRTC流媒体服务器。它使用 C++ 作为开发语言,底层使用 libuv 处理 I/O 事件。</p>\n</div><div class=\"cl-preview-section\"><p>有很多人对 Nodejs 比较诟病,认为 Nodejs 提拱不了高性能的流媒体服务器。实际上,如果按照传输的 Nodejs 应用开发出的流媒体服务器肯定是不能胜任这项工作的。但对于 Mediasoup 来讲,它只不过使用 Nodejs 做 信令处理 及 业务的管理 工作,所以它的负担并不重。对性能要求高的是媒体数据流的转发工作,而这部分工作是由 Mediasoup(C++)部分实现的。Nodejs 与 Mediasoup之间通过管道进行通信。</p>\n</div><div class=\"cl-preview-section\"><p>严格意义上来说,Mediasoup是单进程的。但你不要以为这就影响了它的性能。实际上,它是使用单进程的方式将服务器上CPU某个 <code>核</code> 充分利用好,然后在业务层控制进程的个数。比如说你的服务器是个 8 核的CPU,那么在业务层你就该启动 8 个Mediasoup进程。通过这种方式来达到对 CPU 的充分利用。</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dc698e400014cb711420915.png\" data-original=\"//img1.sycdn.imooc.com/5dc698e400014cb711420915.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>Mediasoup中的每个进程称为一个 Worker, 你也可以把它理解为一个<code>节点</code>,在每个 Worker 中可以有多个 Router。对于 Router,你站在不同的解度可以有不同的理解。如果你占在应用层的角度,你可以把它理解为一个房间;如果你站在数据流转的角度,可以把它理解为一个路由器,数据通过 <code>路由器</code> 转发给目标用户。</p>\n</div><div class=\"cl-preview-section\"><p>想了解更多Mediasoup的细节,可以观看我的视频课 <a href=\"https://coding.imooc.com/class/387.html\">《百万级高并发WebRTC流媒体服务器设计与开发》</a>,在这个视频中我对 Mediasoup 源码做了深入剖析。</p>\n</div><div class=\"cl-preview-section\"><h3>Janus</h3>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dc6990d00013b0b16260978.png\" data-original=\"//img1.sycdn.imooc.com/5dc6990d00013b0b16260978.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>上面这张图是 Janus 的整体架构图。在这张图中我们可以看到, 从大的方面说 Janus 由 Janus CORE、Janus Plugin 以及信令接口三部分组成。</p>\n</div><div class=\"cl-preview-section\"><ul><li>信令接口,Janus 支持的信令协议比较多,如 HTTP、WebSocket、RabbitMQ 等。这些信令协议使得 Janus 具有非常好的接入性。因为很多公司喜欢各种不同的协议,如有的喜欢 websocket,有的喜欢http,proto等。因此 Janus 在信令接入方面具有很大的优势。</li>\n<li>Janus Plugin,Janus 的业务管理是按照 Plugin 的方式管理的,因此你可以在Janus中根据自己的需要实现自己的业务插件。实际上,对于一般性的需求 Janus 已经相关的插件。如:\n<ul><li>VideoRoom,用于多人音视频互动,像音视频会议,在线教育都可以通过该插件来实现。</li>\n<li>VideoCall,用于 1:1 的音视频通信。</li>\n<li>SIP,用于与传统电话设备对接。</li>\n<li>Streaming,用于广播,也就是我们通常所说的一人共享,多人观看的直播模式。</li>\n<li>TextRoom,它是一个聊天室,通过它可以进行文本聊天。</li>\n<li>RecordPlay,用于录制和回放。</li>\n</ul></li>\n<li>Janus Core 是Janus的核心,其作用是处理流的转发,各种协议的接入。以浏览器为例,要想让浏览器接入到 WebRTC 流媒体服务器上,那流媒体服务器必须要支持 STUN、DTLS、SRTP、ICE 等协议。而 Janus Core 就是专门做这事儿的。</li>\n</ul></div><div class=\"cl-preview-section\"><p><a href=\"https://coding.imooc.com/class/387.html?mc_marking=14d51eb28e859859dae4fe1063d81518&mc_channel=shouji\">Janus</a> 是由 C语言开发的,因此它的性能非常优秀。要说不足的话,janus 底层没有使用 epoll 这类异步I/O事件处理机制,这应该说是它的一大缺陷;另外,Janus还使用 glib 库,由于 glib 库对于国内的很多开发同学来说用的比较少,所以会有一定的学习成本。</p>\n</div><div class=\"cl-preview-section\"><p>整体上看,Janus采用了插件的架构设计方案。这种方案非常适合于有多种业务模型或业务经常发生变化的公司或项目。另外 Janus 支持多种消息传输协议,这对于开发人员来说具有极大的吸引力。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/387.html?mc_marking=14d51eb28e859859dae4fe1063d81518&mc_channel=shouji\">Medooze</a></h2>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"//img1.sycdn.imooc.com/5dc699260001242a12760906.png\" data-original=\"//img1.sycdn.imooc.com/5dc699260001242a12760906.png\" alt=\"图片描述\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>Medooze 的整体架构与 Mediasoup 类似,不过它的信令处理、业务管理以及媒体数据的转发功能都是放在 Nodejs下进行统一管理的。实际上,这样的管理方式也不会对性能造成什么影响,因为重的媒体流的转发工作仍然是使用的 C++ 在 Nodejs 底层实现的。</p>\n</div><div class=\"cl-preview-section\"><p>Medooze 的业务功能要比 Mediasoup 强大,像服务端录制、推流这些 Mediasoup 没有的功能它都支持。但它性能没有 Mediasoup 做的极致,在Medooze的底层使用的poll来处理I/O事件,poll与epoll性能相差距大。除此之外,Medooze的业务逻辑也没有Mediasoup简洁;另外与 Janus 相比,它的业务管理不如 Janus 灵活,Janus 的插件管理方式显然要优于 Medooze 和 mediasoup。</p>\n</div><div class=\"cl-preview-section\"><p>但总的来说,Medooze还是一款非常不错的 WebRTC 流媒体服务器。虽然有一些小的暇疵,但还是非常不错的一款流媒体服务器。</p>\n</div><div class=\"cl-preview-section\"><p>想了解更多 Medooze 细节的同学可以看我的专栏 <a href=\"https://time.geekbang.org/column/article/136000\">《从0打造音视频直播系统》</a>。</p>\n</div><div class=\"cl-preview-section\"><h2><a href=\"https://coding.imooc.com/class/387.html?mc_marking=14d51eb28e859859dae4fe1063d81518&mc_channel=shouji\">小结</a></h2>\n</div><div class=\"cl-preview-section\"><p>通过上面的描述,我想你应该对目前主流的 WebRTC 流媒体服务器有了一个大体的了解。所以在选型上你可以按照自己团队的能力进行评估到底该用那个流媒体服务器。</p>\n</div><div class=\"cl-preview-section\"><p>如果你团队能力比较强,可以做底层开发,那么建议你使用 <a href=\"https://coding.imooc.com/class/387.html?mc_marking=14d51eb28e859859dae4fe1063d81518&mc_channel=shouji\">Mediasoup</a>。因为 Mediasoup 不关心应用层,它关注的是底层数据如何高效的流转,代码简洁、高效,性能极佳。</p>\n</div><div class=\"cl-preview-section\"><p>如果你们要做的业务种类比较多,变化比较快,那建议你选择使用 Janus 作为流媒体服务器。将你的业务做成一个插件放到 Janus上很快就能实现你们的业务需求。</p>\n</div><div class=\"cl-preview-section\"><p>如果你们的业务变化不大,除了追求性能外,还需要录制、推流之类的功能,那么你可以选择使用Medooze,它可以很好的满足你们的需求。</p>\n</div><div class=\"cl-preview-section\"><p>当然,除了上面我介绍到的几款比较流行的 WebRTC 流媒体服务器外,还有一些其它的流媒体服务器,如 Licode、OWT、Jitsi等也可以选择。</p>\n</div><div class=\"cl-preview-section\"><p>Licode 之所以名气比较大,是因为它推出的时间比较早。而 OWT 是 Licode 的一个变种,它在 Licode上实现了 SFU 功能。看一下 Licode 代码你就会发现,Licode 实现了一套完整的音视频会议系统,对于这样一套系统它的实现非常复杂。如果你的团队没有音视频方面的开发人才的话,可以考虑Licode,将它搭建出来之后就可以直接使用了。但如果你有业务变化想修改它就太麻烦了。</p>\n</div><div class=\"cl-preview-section\"><p>Jitsi 上层是使用 Java 语言开发的,但底层也是使用的 C/C++ 语言。它通过 JNI 来实现Java与 C/C++之间的通信。在 2018 年有机构做过一次性能评测,当时 Jitsi 表现比较差强人意,不知现在是否已经有了改进。</p>\n</div><div class=\"cl-preview-section\"><p>以上就是对几款 WebRTC流媒体服务器的比较,希望本文可以帮助你解决WebRTC流媒体服务器的选择问题。</p>\n</div><div class=\"cl-preview-section\"><h2>参考</h2>\n</div><div class=\"cl-preview-section\"><ul><li><a href=\"https://coding.imooc.com/class/387.html?mc_marking=14d51eb28e859859dae4fe1063d81518&mc_channel=shouji\">《百万级高并发WebRTC流媒体服务器设计与开发》</a></li>\n<li><a href=\"https://time.geekbang.org/column/article/136000\">《从0打造音视频直播系统》</a>。</li>\n</ul></div>\n\t\t\t\t\t",
  306. "cover": [
  307. "//img1.sycdn.imooc.com/5dc698450001a9f712760906.png",
  308. "//img1.sycdn.imooc.com/5dc698e400014cb711420915.png",
  309. "//img1.sycdn.imooc.com/5dc6990d00013b0b16260978.png",
  310. "//img1.sycdn.imooc.com/5dc699260001242a12760906.png"
  311. ],
  312. "mode": "column"
  313. },
  314. {
  315. "id": "286567",
  316. "title": "iOS 端实现1对1音视频实时通话",
  317. "browse_count": 2030,
  318. "collection_count": 4,
  319. "comments_count": 1,
  320. "author": {
  321. "id": "4873493",
  322. "author_name": "李超",
  323. "avatar": "//img4.sycdn.imooc.com/5b9876c60001ffc914821482-160-160.jpg",
  324. "status": "normal"
  325. },
  326. "classify": "移动开发",
  327. "thumbs_up_count": 4,
  328. "create_time": "2019.05.11 22:48",
  329. "content": "\n\t\t\t\t\t\t<div class=\"cl-preview-section\"><h2>前言</h2>\n</div><div class=\"cl-preview-section\"><p>之前,我已经写过 <a href=\"http://www.imooc.com/article/285511\">Android 端实现1对1音视频实时通话</a> 的文章。在那篇文章中,我向大家介绍了在 Android 端是如何使用 WebRTC 进行音视频通话的。今天,我们再来看看 iOS 端1对1音视频实时通话的具体实现。</p>\n</div><div class=\"cl-preview-section\"><p>iOS 端的实现逻辑与 Android 端基本相同,最大的区别可能是语言方面的差异啦!所以,下面我基本上还是按照介绍 Android 端一样的过程来介绍 iOS 端的实现。具体步骤如下:</p>\n</div><div class=\"cl-preview-section\"><ul><li>权限申请</li>\n<li>引入 WebRTC 库</li>\n<li>采集并显示本地视频</li>\n<li>信令驱动</li>\n<li>创建音视频数据通道</li>\n<li>媒体协商</li>\n<li>渲染远端视频</li>\n</ul></div><div class=\"cl-preview-section\"><p>通过上面几个小节,全面介绍如何在iOS端实现1对1实时通话。</p>\n</div><div class=\"cl-preview-section\"><h2>申请权限</h2>\n</div><div class=\"cl-preview-section\"><p>首先,我们来看一下 iOS 端是如何获取访问音视频设备权限的。相比 Android 端而言,iOS端获取相关权限要容易很多。其步骤如下:</p>\n</div><div class=\"cl-preview-section\"><ul><li>打开项目,点击左侧目录中的项目。</li>\n<li>在左侧目录找到 <strong>info.plist</strong>,并将其打开。</li>\n<li>点击 <strong>右侧</strong> 看到 “+” 号的地方。</li>\n<li>添加 Camera 和 Microphone 访问权限。</li>\n</ul></div><div class=\"cl-preview-section\"><p>下面这张图更清晰的展现了申请权限的步骤:</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"https://upload-images.jianshu.io/upload_images/5956443-16123c09ff144469.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640\" data-original=\"https://upload-images.jianshu.io/upload_images/5956443-16123c09ff144469.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640\" alt=\"iOS申请权限\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>通过以上步骤,我们就将访问音视频设备的权限申请好了。申请完权限后,下面我们来看一下iOS端如何引入 WebRTC 库。</p>\n</div><div class=\"cl-preview-section\"><h2>引入WebRTC库</h2>\n</div><div class=\"cl-preview-section\"><p>在iOS端引入 WebRTC 库有两种方式:</p>\n</div><div class=\"cl-preview-section\"><ul><li>第一种,是通过 WebRTC 源码编译出 WebRTC 库,然后在项目中手动引入它;</li>\n<li>第二种方式,是 WebRTC 官方会定期发布编译好的 WebRTC 库,我们可以使用 Pod 方式进行安装。</li>\n</ul></div><div class=\"cl-preview-section\"><p>在本项目中,我们使用第二种方式。</p>\n</div><div class=\"cl-preview-section\"><p>使用第二种方式引入 WebRTC 库非常简单,我们只需要写个 <strong>Podfile</strong> 文件就可以了。在 <strong>Podfile</strong> 中可以指定下载 WebRTC 库的地址,以及我们要安装的库的名子。</p>\n</div><div class=\"cl-preview-section\"><p><strong>Podfile</strong> 文件的具体格式如下:</p>\n</div><div class=\"cl-preview-section\"><pre><code>source 'https://github.com/CocoaPods/Specs.git'\n \nplatform :ios,'11.0'\n\ntarget 'WebRTC4iOS2' do\n\npod 'GoogleWebRTC'\n\nend\n\n</code></pre>\n</div><div class=\"cl-preview-section\"><ul><li>source,指定了库文件从哪里下载</li>\n<li>platform,指定了使用的平台及平台版本</li>\n<li>target,指定项目的名子</li>\n<li>pod,指定要安装的库</li>\n</ul></div><div class=\"cl-preview-section\"><p>有了 Podfile 之后,在当前目录下执行 <code>pod install</code> 命令,这样 Pod 工具就可以将 WebRTC 库从源上来载下来。</p>\n</div><div class=\"cl-preview-section\"><p>在执行 <code>pod install</code> 之后,它除了下载库文件之外,会为我们产生一个新的工作空间文件,即**{project}.xcworkspace**。在该文件里,会同时加载项目文件及刚才安装好的 Pod 依赖库,并使两者建立好关联。</p>\n</div><div class=\"cl-preview-section\"><p>这样,WebRTC库就算引入成功了。下面就可以开始写我们自己的代码了。</p>\n</div><div class=\"cl-preview-section\"><h2>获取本地视频</h2>\n</div><div class=\"cl-preview-section\"><p>WebRTC 库引入成功之后,我们就可以开始真正的 WebRTC 之旅了。下面,我们来看一下如何获取本地视频并将其展示出来。</p>\n</div><div class=\"cl-preview-section\"><p>在获取视频之前,我们首先要选择使用哪个视频设备采集数据。在WebRTC中,我们可以通过<strong>RTCCameraVideoCapture</strong> 类获取所有的视频设备。如下所示:</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-objc\"><code class=\"prism language-objc\">NSArray<AVCaptureDevice*>* devices = [RTCCameraVideoCapture captureDevices];\nAVCaptureDevice* device = devices[0];\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>通过上面两行代码,我们就拿到了视频设备中的第一个设备。简单吧!</p>\n</div><div class=\"cl-preview-section\"><p>当然,光有设备还不行。我们还要清楚从设备中采集的数据放到哪里了,这样我们才能将其展示出来。</p>\n</div><div class=\"cl-preview-section\"><p>WebRTC 为我们提供了一个专门的类,即 <strong>RTCVideoSource</strong>。它有两层含义:</p>\n</div><div class=\"cl-preview-section\"><ul><li>一是表明它是一个视频<strong>源</strong>。当我们要展示视频的时候,就从这里获取数据;</li>\n<li>另一方面,它也是一个<strong>终点</strong>。即,当我们从视频设备采集到视频数据时,要交给它暂存起来。</li>\n</ul></div><div class=\"cl-preview-section\"><p>除此之外,为了能更方便的控制视频设备,WebRTC 提供了一个专门用于操作设备的类,即 <strong>RTCCameraVideoCapture</strong>。通过它,我们就可以自如的控制视频设备了。</p>\n</div><div class=\"cl-preview-section\"><p>通过上面介绍的两个类,以及前面介绍的 <strong>AVCaptureDevice</strong>,我们就可以轻松的将视频数据采集出来了。下面我们就来具体看一下代码吧!</p>\n</div><div class=\"cl-preview-section\"><p>在该代码中,首先将 <strong>RTCVideoSource</strong> 与 <strong>RTCCameraVideoCapture</strong> 进行绑定,然后再开启设备,这样视频数据就源源不断的被采集到 <strong>RTCVideoSource</strong> 中了。</p>\n</div><div class=\"cl-preview-section\"><pre class=\"language-objectc\"><code class=\"prism language-objectc\">...\n\nRTCVideoSource* videoSource = [factory videoSource];\ncapture = [[RTCCameraVideoCapturer alloc] initWithDelegate:videoSource];\n\n...\n\n[capture startCaptureWithDevice:device\n format:format\n fps:fps];\n...\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>通过上面的几行代码就可以从摄像头捕获视频数据了。</p>\n</div><div class=\"cl-preview-section\"><p>这里有一点需要特别强调一下,就是 <strong>factory</strong> 对象。在 WebRTC Native 层,<strong>factory</strong> 可以说是 <strong>“万物的根源”</strong>,像 RTCVideoSource、RTCVideoTrack、RTCPeerConnection 这些类型的对象,都需要通过 <strong>factory</strong> 来创建。 那么,<strong>factory</strong> 对象又是如何创建出来的呢?</p>\n</div><div class=\"cl-preview-section\"><p>通过下面的代码你就可以一知究竟了:</p>\n</div><div class=\"cl-preview-section\"><pre><code>...\n\n[RTCPeerConnectionFactory initialize];\n \n//如果点对点工厂为空\nif (!factory)\n{\n RTCDefaultVideoDecoderFactory* decoderFactory = [[RTCDefaultVideoDecoderFactory alloc] init];\n RTCDefaultVideoEncoderFactory* encoderFactory = [[RTCDefaultVideoEncoderFactory alloc] init];\n NSArray* codecs = [encoderFactory supportedCodecs];\n [encoderFactory setPreferredCodec:codecs[2]];\n \n factory = [[RTCPeerConnectionFactory alloc] initWithEncoderFactory: encoderFactory\n decoderFactory: decoderFactory];\n\n}\n...\n\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>在上面代码中,</p>\n</div><div class=\"cl-preview-section\"><ul><li>首先要调用 RTCPeerConnectionFactory 类的 initialize 方法进行初始化;</li>\n<li>然后创建 <strong>factory</strong> 对象。需要注意的是,在创建 <strong>factory</strong> 对象时,传入了两个参数:一个是默认的编码器;一个是默认的解码器。我们可以通过修改这两个参数来达到使用不同编解码器的目的。</li>\n</ul></div><div class=\"cl-preview-section\"><p>有了 <strong>factory</strong> 对象后,我们就可以开始创建其它对象了。那么,紧接下来的问题就是如何将采集到的视频展示出来了。</p>\n</div><div class=\"cl-preview-section\"><p>在iOS端展示本地视频与Android端还是有很大区别的,这主要是由于不同系统底层实现方式不一样。为了更高效的展示本地视频,它们采用了不同的方式。</p>\n</div><div class=\"cl-preview-section\"><p>在iOS端展示本地视频其实非常的简单,只需要在调用 capture 的 <strong>startCaptureWithDevice</strong> 方法之前执行下面的语句就好了:</p>\n</div><div class=\"cl-preview-section\"><pre><code>self.localVideoView.captureSession = capture.captureSession;\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>当然,在iOS页面初始化的时候,一定要记得定义 <strong>localVideoView</strong> 哟,其类型为 <strong>RTCCameraPreviewView</strong>!</p>\n</div><div class=\"cl-preview-section\"><p>通过上面的步骤,我们就可以看到视频设备采集到的视频图像了。</p>\n</div><div class=\"cl-preview-section\"><h2>信令驱动</h2>\n</div><div class=\"cl-preview-section\"><p>上面我们介绍了iOS端权限的申请,WebRTC库的引入,以及本地视频的采集与展示,这些功能实现起来都很简单。但接下来我们要介绍的信令就要复杂一些了。</p>\n</div><div class=\"cl-preview-section\"><p>在任何系统中,都可以说信令是系统的灵魂。例如,由谁来发起呼叫;媒体协商时,什么时间发哪种 <strong>SDP</strong> 都是由信令控制的。</p>\n</div><div class=\"cl-preview-section\"><p>对于本项目来说,它的信令相对还是比较简单,它包括下面几种信令:</p>\n</div><div class=\"cl-preview-section\"><p><strong>客户端命令</strong></p>\n</div><div class=\"cl-preview-section\"><ul><li>join,用户加入房间</li>\n<li>leave,用户离开房间</li>\n<li>message,端到端命令(offer、answer、candidate)</li>\n</ul></div><div class=\"cl-preview-section\"><p><strong>服务端命令</strong></p>\n</div><div class=\"cl-preview-section\"><ul><li>joined,用户已加入</li>\n<li>leaved,用户已离开</li>\n<li>other_joined,其它用户已加入</li>\n<li>bye,其它用户已离开</li>\n<li>full,房间已满</li>\n</ul></div><div class=\"cl-preview-section\"><p>这些信令之间是怎样一种关系?在什么情况下该发送怎样的信令呢?要回答这个问题我们就要看一下信令状态机了。</p>\n</div><div class=\"cl-preview-section\"><h3>信令状态机</h3>\n</div><div class=\"cl-preview-section\"><p>在 iOS 端的信令与我们之前介绍的 js端 和 Android 端一样,会通过一个信令状态机来管理。在不同的状态下,需要发不同的信令。同样的,当收到服务端,或对端的信令后,状态会随之发生改变。下面我们来看一下这个状态的变化图吧:</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"https://upload-images.jianshu.io/upload_images/5956443-2f71ce4133ec8484.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\" data-original=\"https://upload-images.jianshu.io/upload_images/5956443-2f71ce4133ec8484.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\" alt=\"信令状态机\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>在初始时,客户端处于 init/leaved 状态。</p>\n</div><div class=\"cl-preview-section\"><ul><li>在 init/leaved 状态下,用户只能发送 <strong>join</strong> 消息。服务端收到 <strong>join</strong> 消息后,会返回 <strong>joined</strong> 消息。此时,客户端会更新为 <strong>joined</strong> 状态。</li>\n<li>在 <strong>joined</strong> 状态下,客户端有多种选择,收到不同的消息会切到不同的状态:\n<ul><li>如果用户离开房间,那客户端又回到了初始状态,即 init/leaved 状态。</li>\n<li>如果客户端收到 <strong>second user join</strong> 消息,则切换到 <strong>join_conn</strong> 状态。在这种状态下,两个用户就可以进行通话了。</li>\n<li>如果客户端收到 <strong>second user leave</strong> 消息,则切换到 <strong>join_unbind</strong> 状态。其实 <strong>join_unbind</strong> 状态与 <strong>joined</strong> 状态基本是一致的。</li>\n</ul></li>\n<li>如果客户端处于 <strong>join_conn</strong> 状态,当它收到 <strong>second user leave</strong> 消息时,也会转成 <strong>joined_unbind</strong> 状态。</li>\n<li>如果客户端是 <strong>joined_unbind</strong> 状态,当它收到 <strong>second user join</strong> 消息时,会切到 <strong>join_conn</strong> 状态。</li>\n</ul></div><div class=\"cl-preview-section\"><p>通过上面的状态图,我们就非常清楚的知道了在什么状态下应该发什么信令;或者说,发什么样的信令,状态会发生怎样的变化了。</p>\n</div><div class=\"cl-preview-section\"><h3>引入 <a href=\"http://socket.io\">socket.io</a> 库</h3>\n</div><div class=\"cl-preview-section\"><p>看过我之前文章的同学应该都清楚,无论是在 js端,还是在 Android 端的实时通话中,我一直使用 socket.io库作为信令的基础库。之所以选择 <a href=\"http://socket.io\">socket.io</a> 是基于以下原因:</p>\n</div><div class=\"cl-preview-section\"><ul><li>一方面是由于它支持跨平台,这样在各个平台上我们都可以保持相同的逻辑;</li>\n<li>另一方面,<a href=\"http://socket.io\">socket.io</a> 使用简单,功能又非常强大;</li>\n</ul></div><div class=\"cl-preview-section\"><p>不过,在 iOS 端的 <a href=\"http://socket.io\">socket.io</a> 是用 <strong>swift</strong> 语言实现的,而我们的1对1系统则是用 Object-C 实现的。那么,就带来一个问题,在 OC (Object-C) 里是否可以直接使用 swift 编写的库呢?</p>\n</div><div class=\"cl-preview-section\"><p>答案是肯定的。我们只需要在 Podfile 中 增加 <code>use_frameworks!</code> 指令即可。 所以,我们的 Podfile 现在应该变成这个样子:</p>\n</div><div class=\"cl-preview-section\"><pre><code>source 'https://github.com/CocoaPods/Specs.git'\n \nplatform :ios,'11.0'\n\nuse_frameworks!\n\ntarget 'WebRTC4iOS2' do\n\npod 'Socket.IO-Client-Swift', '~> 13.3.0'\npod 'GoogleWebRTC'\n\nend\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>上面 Podfile 中,每行的含义大家应该都很清楚了,我这里就不做过多讲解了。</p>\n</div><div class=\"cl-preview-section\"><h2>信令的使用</h2>\n</div><div class=\"cl-preview-section\"><p><strong><a href=\"http://socket.io\">socket.io</a></strong> 库引入成功后,下面我们来看一下何使用 <a href=\"http://socket.io\">socket.io</a>。在 iOS 下,使用 <a href=\"http://socket.io\">socket.io</a> 分为三步:</p>\n</div><div class=\"cl-preview-section\"><ul><li>通过 url 获取 socket。有了 socket 之后我们就可建立与服务器的连接了。</li>\n<li>注册侦听的消息,并为每个侦听的消息绑定一个处理函数。当收到服务器的消息后,随之会触发绑定的函数。</li>\n<li>通过 socket 建立连接。</li>\n<li>发送消息。</li>\n</ul></div><div class=\"cl-preview-section\"><p>下我们我们就逐一的看它们是如何实现的吧!</p>\n</div><div class=\"cl-preview-section\"><p><strong>获取 socket</strong></p>\n</div><div class=\"cl-preview-section\"><p>在 iOS 中获取 socket 其实很简单,我们来看一下代码:</p>\n</div><div class=\"cl-preview-section\"><pre><code>NSURL* url = [[NSURL alloc] initWithString:addr];\nmanager = [[SocketManager alloc] initWithSocketURL:url\n config:@{\n \t@\"log\": @YES,\n \t\t\t\t\t\t\t\t\t\t\t@\"forcePolling\":@YES,\n @\"forceWebsockets\":@YES\n }];\nsocket = manager.defaultSocket;\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>没错,通过这三行代码就可以了。至于为什么这么写我就不解释了,大家记下来就好了。这是 socket.io的固定格式。</p>\n</div><div class=\"cl-preview-section\"><p><strong>注册侦听消息</strong></p>\n</div><div class=\"cl-preview-section\"><p>使用 <a href=\"http://socket.io\">socket.io</a> 注册一个侦听消息也非常容易,如下所示:</p>\n</div><div class=\"cl-preview-section\"><pre><code>[socket on:@\"joined\" callback:^(NSArray * data, SocketAckEmitter * ack) {\n NSString* room = [data objectAtIndex:0];\n \n NSLog(@\"joined room(%@)\", room);\n \n [self.delegate joined:room];\n \n}];\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>上面就是注册一个 <strong>joined</strong> 消息,并给它绑定一个匿名的处理函数。如果带来的消息还有参数的话,我们可以从 data 这个数组中获取到。</p>\n</div><div class=\"cl-preview-section\"><p>同样的道理,如果我们想注册一个新的侦听消息,可以按着上面的格式,只需将 <strong>joined</strong> 替换一下就可以了。</p>\n</div><div class=\"cl-preview-section\"><p><strong>建立连接</strong><br>\n这个就更简单了,下接上代码了:</p>\n</div><div class=\"cl-preview-section\"><pre><code>[socket connect];\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>没错,只这一句连接就建好了哈!</p>\n</div><div class=\"cl-preview-section\"><p><strong>发送消息</strong><br>\n接下来,让我们看一下如何使用 <a href=\"http://socket.io\">socket.io</a> 发送消息。</p>\n</div><div class=\"cl-preview-section\"><pre><code>...\nif(socket.status == SocketIOStatusConnected){\n [socket emit:@\"join\" with:@[room]];\n}\n...\n</code></pre>\n</div><div class=\"cl-preview-section\"><p><a href=\"http://socket.io\">socket.io</a> 使用 <strong>emit</strong> 方法发送消息。它可以带一些参数,这些参数都被放在一个数据里。在上面的代码中,首先要判断socket是否已经处理连接状态,只有处于连接状态时,消息才能被真正发送出去。</p>\n</div><div class=\"cl-preview-section\"><p>以上就是 <a href=\"http://socket.io\">socket.io</a> 的使用,是不是非常的简单?</p>\n</div><div class=\"cl-preview-section\"><h2>创建 RTCPeerConnection</h2>\n</div><div class=\"cl-preview-section\"><p>信令系统建立好后,后面的逻辑都是围绕着信令系统建立起来的。<strong>RTCPeerConnection</strong> 对象的建立也不例外。</p>\n</div><div class=\"cl-preview-section\"><p>在客户端,用户要想与远端通话,首先要发送 <strong>join</strong> 消息,也就是要先进入房间。此时,如果服务器判定用户是合法的,则会给客户端回 <strong>joined</strong> 消息。</p>\n</div><div class=\"cl-preview-section\"><p>客户端收到 <strong>joined</strong> 消息后,就要创建 RTCPeerConnection 了,也就是要建立一条与远端通话的音视频数据传输通道。</p>\n</div><div class=\"cl-preview-section\"><p>下面,我们就来看一下 RTCPeerConnection 是如何建立的:</p>\n</div><div class=\"cl-preview-section\"><pre><code>...\n\nif (!ICEServers) {\n ICEServers = [NSMutableArray array];\n [ICEServers addObject:[self defaultSTUNServer]];\n}\n\nRTCConfiguration* configuration = [[RTCConfiguration alloc] init];\n[configuration setIceServers:ICEServers];\nRTCPeerConnection* conn = [factory\n peerConnectionWithConfiguration:configuration\n constraints:[self defaultPeerConnContraints]\n delegate:self];\n\n...\n\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>对于 iOS 的 <strong>RTCPeerConnection</strong> 对象有三个参数:</p>\n</div><div class=\"cl-preview-section\"><ul><li>第一个,是 RTCConfiguration 类型的对象,该对象中最重要的一个字段是 <strong>iceservers</strong>。它里边存放了 stun/turn 服务器地址。其主要作用是用于NAT穿越。对于 NAT 穿越的知识大家可以看 <a href=\"https://coding.imooc.com/class/329.html\">《WebRTC实时互动直播技术入门与实战》</a> ,这门课里对其原理做了说细阐述。</li>\n<li>第二个参数,是 RTCMediaConstraints 类型对象,也就是对 RTCPeerConnection 的限制。如,是否接收视频数据?是否接收音频数据?如果要与浏览器互通还要开启 <strong>DtlsSrtpKeyAgreement</strong> 选项。</li>\n<li>第三个参数,是<strong>委拖</strong>类型。相当于给 RTCPeerConnection 设置一个<strong>观察者</strong>。这样RTCPeerConnection 可以将一个状态/信息通过它通知给<strong>观察者</strong>。但它并不属于<strong>观察者</strong>模式,这一点大家一定要清楚。</li>\n</ul></div><div class=\"cl-preview-section\"><p><strong>RTCPeerConnection</strong> 对象创建好后,接下来我们介绍的是整个实时通话过程中,最重要的一部分知识,那就是 <strong>媒体协商</strong>。</p>\n</div><div class=\"cl-preview-section\"><h3>媒体协商</h3>\n</div><div class=\"cl-preview-section\"><p>首先,我们要知道媒体协商内容使用是 SDP 协议,不了解这部分知识的同学可以看 <a href=\"https://coding.imooc.com/class/329.html\">《WebRTC实时互动直播技术入门与实战》</a> 这门课,在门课里对其做了详细讲解。</p>\n</div><div class=\"cl-preview-section\"><p>其次,我们要清楚整体媒体协商的过程。</p>\n</div><div class=\"cl-preview-section\"><p>iOS 端的媒体协商过程与 Android/JS 端是一模一样的。还是下面这个经典的图:</p>\n</div><div class=\"cl-preview-section\"><p><img class=\"lazyload\" src=\"https://upload-images.jianshu.io/upload_images/5956443-787035ecac6e8d3e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\" data-original=\"https://upload-images.jianshu.io/upload_images/5956443-787035ecac6e8d3e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\" alt=\"媒体协商\" style=\"width:100%;\"></p>\n</div><div class=\"cl-preview-section\"><p>A 与 B 进行通话,通话的发起方,首先要创建 <strong>Offer</strong> 类型的 SDP 内容。之后调用 RTCPeerConnection 对象的 <strong>setLocalDescription</strong> 方法,将 <strong>Offer</strong> 保存到本地。</p>\n</div><div class=\"cl-preview-section\"><p>紧接着,将 <strong>Offer</strong> 发送给服务器。然后,通过信令服务器中转到被呼叫方。被呼叫方收到 <strong>Offer</strong> 后,调用它的 RTCPeerConnection 对象的 <strong>setRemoteDescription</strong> 方法,将远端的 <strong>Offer</strong> 保存起来。</p>\n</div><div class=\"cl-preview-section\"><p>之后,被呼到方创建 <strong>Answer</strong> 类型的 SDP 内容,并调用 RTCPeerConnection 对象的 <strong>setLocalDescription</strong> 方法将它存储到本地。</p>\n</div><div class=\"cl-preview-section\"><p>同样的,它也要将 <strong>Answer</strong> 发送给服务器。服务器收到该消息后,不做任何处理,直接中转给呼叫方。呼叫方收到 <strong>Answer</strong> 后,调用 <strong>setRemoteDescription</strong> 将其保存起来。</p>\n</div><div class=\"cl-preview-section\"><p>通过上面的步骤,整个<strong>媒体协商</strong>部分就完成了。</p>\n</div><div class=\"cl-preview-section\"><p>下面我们就具体看看,在 iOS 端是如何实现这个逻辑的:</p>\n</div><div class=\"cl-preview-section\"><pre><code>...\n\n[peerConnection offerForConstraints:[self defaultPeerConnContraints]\n completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) {\n if(error){\n NSLog(@\"Failed to create offer SDP, err=%@\", error);\n } else {\n __weak RTCPeerConnection* weakPeerConnction = self->peerConnection;\n [self setLocalOffer: weakPeerConnction withSdp: sdp];\n }\n }];\n...\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>在iOS端使用 RTCPeerConnection 对象的 <strong>offerForConstraints</strong> 方法创建 <strong>Offer</strong> SDP。它有两个参数:</p>\n</div><div class=\"cl-preview-section\"><ul><li>一个是 RTCMediaConstraints 类型的参数,该参数我们在前面创建 RTCPeerConnection 对象时介绍过,这里不在赘述。</li>\n<li>另一个参数是一个匿名回调函数。可以通过对 <strong>error</strong> 是否为空来判定 <strong>offerForConstraints</strong> 方法有没有执行成功。如果执行成功了,参数 <strong>sdp</strong> 就是创建好的 SDP 内容。</li>\n</ul></div><div class=\"cl-preview-section\"><p>如果成功获得了 <strong>sdp</strong>,按照之前的处理流程描述,我们首先要将它只存到本地;然后再将它发送给他务器,服务器中转给另一端。</p>\n</div><div class=\"cl-preview-section\"><p>我们的代码也是严格按照这个过程来的。在上面代码中 <strong>setLocalOffer</strong> 方法就是做这件事儿。具体代码如下:</p>\n</div><div class=\"cl-preview-section\"><pre><code>...\n[pc setLocalDescription:sdp completionHandler:^(NSError * _Nullable error) {\n if (!error) {\n NSLog(@\"Successed to set local offer sdp!\");\n }else{\n NSLog(@\"Failed to set local offer sdp, err=%@\", error);\n }\n }];\n \n__weak NSString* weakMyRoom = myRoom;\ndispatch_async(dispatch_get_main_queue(), ^{\n \n NSDictionary* dict = [[NSDictionary alloc] initWithObjects:@[@\"offer\", sdp.sdp]\n forKeys: @[@\"type\", @\"sdp\"]];\n \n [[SignalClient getInstance] sendMessage: weakMyRoom\n withMsg: dict];\n});\n...\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>从上面的代码可以清楚的看出,它做了两件事儿。一是调用 <strong>setLocalDescription</strong> 方法将 sdp 保存到本地;另一件事儿就是发送消息;</p>\n</div><div class=\"cl-preview-section\"><p>所以,通过上面的描述大家也就知道后面的所有逻辑了。这里我们就不一一展开来讲了。</p>\n</div><div class=\"cl-preview-section\"><p>当整个协商完成之后,紧接着,在WebRTC底层就会进行音视频数据的传输。如果远端的视频数据到达本地后,我们就需要将它展示到界面上。这又是如何做到的呢?</p>\n</div><div class=\"cl-preview-section\"><h3>渲染远端视频</h3>\n</div><div class=\"cl-preview-section\"><p>大家是否还记得,在我们创建 RTCPeerConnection 对象时,同时给RTCPeerConnection设置了一个<strong>委拖</strong>,在我们的项目中就是 <strong>CallViewController</strong> 对象。在该对象中我们实现了所有 <strong>RTCPeerConnection</strong> 对象的代理方法。其中比较关键的有下面几个:</p>\n</div><div class=\"cl-preview-section\"><ul><li>\n<p>(void)peerConnection:(RTCPeerConnection *)peerConnection<br>\ndidGenerateIceCandidate:(RTCIceCandidate *)candidate;该方法用于收集可用的 Candidate。</p>\n</li>\n<li>\n<p>(void)peerConnection:(RTCPeerConnection *)peerConnection<br>\ndidChangeIceConnectionState:(RTCIceConnectionState)newState;当 ICE 连接状态发生变化时会触发该方法</p>\n</li>\n<li>\n<p>(void)peerConnection:(RTCPeerConnection *)peerConnection<br>\ndidAddReceiver:(RTCRtpReceiver *)rtpReceiver<br>\nstreams:(NSArray<RTCMediaStream *> *)mediaStreams;该方法在侦听到远端 track 时会触发。</p>\n</li>\n</ul></div><div class=\"cl-preview-section\"><p>那么,什么时候开始渲染远端视频呢?当有远端视频流过来的时候,就会触发 (void)peerConnection:(RTCPeerConnection *)peerConnection<br>\ndidAddReceiver:(RTCRtpReceiver *)rtpReceiver<br>\nstreams:(NSArray<RTCMediaStream *> *)mediaStreams 方法。所以我们只需要在该方法中写一些逻辑即可。</p>\n</div><div class=\"cl-preview-section\"><p>当上面的函数被调用后,我们可以通过 <strong>rtpReceiver</strong> 参数获取到 <strong>track</strong>。这个track有可能是音频trak,也有可能是视频trak。所以,我们首先要对 track 做个判断,看其是视频还是音频。</p>\n</div><div class=\"cl-preview-section\"><p>如果是视频的话,就将remoteVideoView加入到trak中,相当于给track添加了一个观察者,这样remoteVideoView就可以从track获取到视频数据了。在 <strong>remoteVideoView</strong> 实现了渲染方法,一量收到数据就会直接进行渲染。最终,我们就可以看到远端的视频了。</p>\n</div><div class=\"cl-preview-section\"><p>具体代码如下:</p>\n</div><div class=\"cl-preview-section\"><pre><code>...\nRTCMediaStreamTrack* track = rtpReceiver.track;\nif([track.kind isEqualToString:kRTCMediaStreamTrackKindVideo]){\n \n if(!self.remoteVideoView){\n NSLog(@\"error:remoteVideoView have not been created!\");\n return;\n }\n \n remoteVideoTrack = (RTCVideoTrack*)track;\n \t\n \t [remoteVideoTrack addRenderer: self.remoteVideoView];\n}\n \n...\n\n</code></pre>\n</div><div class=\"cl-preview-section\"><p>通过上面的代码,我们就可以将远端传来的视频展示出来了。</p>\n</div><div class=\"cl-preview-section\"><h2>小结</h2>\n</div><div class=\"cl-preview-section\"><p>以上我就将 iOS 端实现1对1实时通话的整体逻辑讲解完了。整体来看,其过程与 js/Android 端基本上是一模一样的。</p>\n</div><div class=\"cl-preview-section\"><p>在本文中,我通过对下面几个主题的介绍,向大家完整的讲解了 iOS 端该如何实现一个实时音视频通话程序:</p>\n</div><div class=\"cl-preview-section\"><ul><li>权限申请</li>\n<li>引入 WebRTC 库</li>\n<li>采集并显示本地视频</li>\n<li>信令驱动</li>\n<li>创建音视频数据通道</li>\n<li>媒体协商</li>\n<li>渲染远端视频</li>\n</ul></div><div class=\"cl-preview-section\"><p>对于一个熟悉 iOS 的开发者来说,通过本文的讲解,应该可以很快写出这样一个实时通话的程序。</p>\n</div><div class=\"cl-preview-section\"><p>谢谢!</p>\n</div><div class=\"cl-preview-section\"><p>##参考资料<br><a href=\"https://coding.imooc.com/class/329.html?mc_marking=59909dfb407cd07d4f3998dbc2d45c49&mc_channel=shouji\">《WebRTC实时互动直播技术入门与实战》</a></p>\n</div>\n\t\t\t\t\t",
  330. "cover": [
  331. "https://upload-images.jianshu.io/upload_images/5956443-16123c09ff144469.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640",
  332. "https://upload-images.jianshu.io/upload_images/5956443-2f71ce4133ec8484.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240",
  333. "https://upload-images.jianshu.io/upload_images/5956443-787035ecac6e8d3e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"
  334. ],
  335. "mode": "column"
  336. },
  337. {
  338. "id": "292698",
  339. "title": "安卓从开发到逆向(二)java转换为smali对比分析",
  340. "browse_count": 945,
  341. "collection_count": 0,
  342. "comments_count": 0,
  343. "author": {
  344. "id": "6685330",
  345. "author_name": "大壮老师",
  346. "avatar": "//img3.sycdn.imooc.com/54584f850001c0bc02200220-160-160.jpg",
  347. "status": "normal"
  348. },
  349. "classify": "后端开发",
  350. "thumbs_up_count": 0,
  351. "create_time": "2019.09.21 17:07",
  352. "content": "\n\t\t\t\t\t\t<p>首先我们需要知道一个<a href=\"https://coding.imooc.com/class/283.html?mc_marking=561de3fb1745767d54fb64fc6c1604db&mc_channel=shouji\" target=\"_self\">知识点</a>:</p><pre>java虚拟机运行的是Java字节码,Dalvik虚拟机运行的是Dalvik字节码。传统的java程序经过编译,生成java字节码并保存在class文件中,java虚拟机通过解码class文件的内容来运行程序,而dalvik虚拟机运行的是dalvik字节码,所有dalvik字节码由java字节码转换而来,并被打包到一个DEX可执行文件当中,dalvik虚拟机通过解释DEX文件来执行这些字节码。</pre><p>破解软件下载地址:</p><pre>链接:https://pan.baidu.com/s/1mHOl9y-LXnETUg3oDAKNvA \n提取码:3ygd</pre><p>准备工具:</p><pre>转换工具:J2S2J</pre><p>首先我们来看一段JAVA代码</p><p><img class=\"lazyload\" src=\"http://img4.sycdn.imooc.com/5d85e78c00016b9405000271.jpg\" data-original=\"http://img4.sycdn.imooc.com/5d85e78c00016b9405000271.jpg\" img_width=\"692\" img_height=\"374\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img1.sycdn.imooc.com/5d85e78c00016b9406920374.jpg\" title></p><p><br></p><p>很简单的一段JAVA代码,定义了一个foo方法,传入两个int类型的参数,返回(a+b)*(a-b),在main方法中进行了调用,传入5和3,并打印出来。</p><p>接下来,我们通过J2S2J转换为smali</p><p><img class=\"lazyload\" src=\"http://img1.sycdn.imooc.com/5d85e7960001b02605000237.jpg\" data-original=\"http://img1.sycdn.imooc.com/5d85e7960001b02605000237.jpg\" img_width=\"1833\" img_height=\"866\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img4.sycdn.imooc.com/5d85e7960001b02618330866.jpg\" title></p><p><br></p><p>我们对比着JAVA代码,来读一下smali代码,如果你对smali一点都不熟悉,可以看一下这篇文章,写的非常好。</p><pre>https://blog.csdn.net/rozol/article/details/88368358</pre><p>我们来分析一下这段smali代码</p><p>代码头部</p><pre>创建了一个Hello类\n.class public LHello;\n该类继承于object\n.super Ljava/lang/Object;\n原文件名称为Hello.java\n.source \"Hello.java\"</pre><p>构造方法,该段未在java代码中显示,java中默认有一个隐藏的无参数的构造方法</p><p><img class=\"lazyload\" src=\"http://img3.sycdn.imooc.com/5d85e7a000019e1805000216.jpg\" data-original=\"http://img3.sycdn.imooc.com/5d85e7a000019e1805000216.jpg\" img_width=\"721\" img_height=\"311\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img2.sycdn.imooc.com/5d85e7a000019e1807210311.jpg\" title></p><p><br></p><p>主方法</p><p><img class=\"lazyload\" src=\"http://img2.sycdn.imooc.com/5d85e7aa0001a48d05000450.jpg\" data-original=\"http://img2.sycdn.imooc.com/5d85e7aa0001a48d05000450.jpg\" img_width=\"887\" img_height=\"798\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img2.sycdn.imooc.com/5d85e7aa0001a48d08870798.jpg\" title></p><p><br></p><p>自定义的foo方法</p><p><img class=\"lazyload\" src=\"http://img1.sycdn.imooc.com/5d85e7b50001b00504300440.jpg\" data-original=\"http://img1.sycdn.imooc.com/5d85e7b50001b00504300440.jpg\" img_width=\"430\" img_height=\"440\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img4.sycdn.imooc.com/5d85e7b50001b00504300440.jpg\" title></p><p><br></p><p>如上所示,我们解析了一段简单的JAVA代码到smali。</p><p>接下来,我们可以加一点点难度,写一个for循环</p><p><img class=\"lazyload\" src=\"http://img2.sycdn.imooc.com/5d85e7bf0001c06d05000376.jpg\" data-original=\"http://img2.sycdn.imooc.com/5d85e7bf0001c06d05000376.jpg\" img_width=\"635\" img_height=\"477\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img1.sycdn.imooc.com/5d85e7bf0001c06d06350477.jpg\" title></p><p><br></p><p>还是使用J2S2J转换为smali,本次我们仅分析for_test_method方法</p><p><img class=\"lazyload\" src=\"http://img2.sycdn.imooc.com/5d85e7ca00015aa505000476.jpg\" data-original=\"http://img2.sycdn.imooc.com/5d85e7ca00015aa505000476.jpg\" img_width=\"928\" img_height=\"882\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img1.sycdn.imooc.com/5d85e7ca00015aa509280882.jpg\" title></p><p><br></p><p>代码参考:</p><p><a href=\"https://github.com/freedom-wy/reverse_android\" _class=\"lazyload\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC\" data-original=\"https://github.com/freedom-wy/reverse_android\">https://github.com/freedom-wy/reverse_android</a> 中smali笔记</p><p>欢迎交流,一起学习,一起进步。</p><p>另外,我在慕课网上主讲课程:<a href=\"https://coding.imooc.com/class/283.html?mc_marking=561de3fb1745767d54fb64fc6c1604db&mc_channel=shouji\" target=\"_self\" title=\"《python爬虫工程师必学--App数据抓取实战》\" textvalue=\"《python爬虫工程师必学--App数据抓取实战》\">《python爬虫工程师必学--App数据抓取实战》</a></p><p>还请各位大神多多支持。</p><p><br></p>\n\t\t\t\t\t",
  353. "cover": [
  354. "http://img4.sycdn.imooc.com/5d85e78c00016b9405000271.jpg",
  355. "http://img1.sycdn.imooc.com/5d85e7960001b02605000237.jpg",
  356. "http://img3.sycdn.imooc.com/5d85e7a000019e1805000216.jpg",
  357. "http://img2.sycdn.imooc.com/5d85e7aa0001a48d05000450.jpg",
  358. "http://img1.sycdn.imooc.com/5d85e7b50001b00504300440.jpg",
  359. "http://img2.sycdn.imooc.com/5d85e7bf0001c06d05000376.jpg",
  360. "http://img2.sycdn.imooc.com/5d85e7ca00015aa505000476.jpg"
  361. ],
  362. "mode": "column"
  363. },
  364. {
  365. "id": "292504",
  366. "title": "安卓从开发到逆向(一)登录demo逆向破解",
  367. "browse_count": 1877,
  368. "collection_count": 6,
  369. "comments_count": 1,
  370. "author": {
  371. "id": "6685330",
  372. "author_name": "大壮老师",
  373. "avatar": "//img3.sycdn.imooc.com/54584f850001c0bc02200220-160-160.jpg",
  374. "status": "normal"
  375. },
  376. "classify": "后端开发",
  377. "thumbs_up_count": 6,
  378. "create_time": "2019.09.16 23:39",
  379. "content": "\n\t\t\t\t\t\t<p><img class=\"lazyload\" src=\"http://img3.sycdn.imooc.com/5d7fab9e0001dfcb05000320.jpg\" data-original=\"http://img3.sycdn.imooc.com/5d7fab9e0001dfcb05000320.jpg\" alt=\"http://img2.sycdn.imooc.com/5d7fab9e0001dfcb05900377.jpg\" style=\"width:100%;\"></p><p>apk及破解软件下载地址:</p><pre>链接:https://pan.baidu.com/s/1mHOl9y-LXnETUg3oDAKNvA \n提取码:3ygd</pre><p><strong>破解要求:</strong></p><p>即使用户名密码输入错误,也显示登录成功。</p><p>======================================================</p><p><strong>准备工具:</strong></p><pre>开发工具android studio3.4.1,破解工具android killer1.3.1,夜神安卓模拟器</pre><p>开发步骤:</p><p>1、在android studio中创建Project,选择空白activity,填入项目名称,包名,项目保存地址,运行平台等,点击finish,开始创建项目</p><p><img class=\"lazyload\" src=\"http://img1.sycdn.imooc.com/5d7fabb60001bf3205000321.jpg\" data-original=\"http://img1.sycdn.imooc.com/5d7fabb60001bf3205000321.jpg\" img_width=\"1131\" img_height=\"724\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img2.sycdn.imooc.com/5d7fabb60001bf3211310724.jpg\" title></p><p><br></p><p>2、项目创建完成后,会在当前目录中创建好模块app,并展示MainActivity.java文件</p><p><img class=\"lazyload\" src=\"http://img3.sycdn.imooc.com/5d7fabc00001608c05000160.jpg\" data-original=\"http://img3.sycdn.imooc.com/5d7fabc00001608c05000160.jpg\" img_width=\"1474\" img_height=\"471\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img4.sycdn.imooc.com/5d7fabc00001608c14740471.jpg\" title></p><p><br></p><p>3、需要注意三个文件,分别为src\\mainAndroidManifest.xml(应用清单文件),java\\com\\dazhuang\\login\\MainActivity.java(安卓工程默认主文件),res\\layout\\activity_main.xml(界面布局文件)</p><p>4、修改界面布局文件,编辑出登录demo界面</p><p><img class=\"lazyload\" src=\"http://img3.sycdn.imooc.com/5d7fabcf0001978305000273.jpg\" data-original=\"http://img3.sycdn.imooc.com/5d7fabcf0001978305000273.jpg\" img_width=\"1492\" img_height=\"814\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img2.sycdn.imooc.com/5d7fabcf0001978314920814.jpg\" title></p><p><br></p><p>5、编辑安卓工程默认主文件MainActivity.java,编辑登录逻辑</p><p><img class=\"lazyload\" src=\"http://img4.sycdn.imooc.com/5d7fabde0001253105000294.jpg\" data-original=\"http://img4.sycdn.imooc.com/5d7fabde0001253105000294.jpg\" img_width=\"1439\" img_height=\"846\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img1.sycdn.imooc.com/5d7fabde0001253114390846.jpg\" title></p><p><br></p><p>6、连接夜神模拟器,并运行app,点击android studio的运行键,android studio会自动连接夜神安卓模拟器,并发送app到模拟器上,进行安装和运行</p><p><img class=\"lazyload\" src=\"http://img1.sycdn.imooc.com/5d7fabea0001a92305000081.jpg\" data-original=\"http://img1.sycdn.imooc.com/5d7fabea0001a92305000081.jpg\" img_width=\"902\" img_height=\"145\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img4.sycdn.imooc.com/5d7fabea0001a92309020145.jpg\" title></p><p><br></p><p><img class=\"lazyload\" src=\"http://img1.sycdn.imooc.com/5d7fabf90001dfcb05000320.jpg\" data-original=\"http://img1.sycdn.imooc.com/5d7fabf90001dfcb05000320.jpg\" img_width=\"590\" img_height=\"377\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img4.sycdn.imooc.com/5d7fabf90001dfcb05900377.jpg\" title></p><p><br></p><p>7、根据MainActivity.java文件,我们可以知道,用户名为admin,密码为admin888,当我们输入正确的用户名密码时,app会弹出消息登录成功,否则弹出消息,登录失败。</p><p><strong>破解方法:</strong></p><p>1、使用android killer反编译app</p><p><img class=\"lazyload\" src=\"http://img3.sycdn.imooc.com/5d7fac0600013f9405000223.jpg\" data-original=\"http://img3.sycdn.imooc.com/5d7fac0600013f9405000223.jpg\" img_width=\"1064\" img_height=\"473\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img3.sycdn.imooc.com/5d7fac0600013f9410640473.jpg\" title></p><p><br></p><p>2、找到MainActivity$login_button.smali</p><p><img class=\"lazyload\" src=\"http://img4.sycdn.imooc.com/5d7fac270001a41605000391.jpg\" data-original=\"http://img4.sycdn.imooc.com/5d7fac270001a41605000391.jpg\" img_width=\"992\" img_height=\"775\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img4.sycdn.imooc.com/5d7fac270001a41609920775.jpg\" title></p><p><br></p><p>3、修改代码,在.line 40下添加:cond_1</p><p>smali代码相关示意详见:</p><pre>https://github.com/freedom-wy/reverse_android/tree/master/project/app</pre><p><img class=\"lazyload\" src=\"http://img4.sycdn.imooc.com/5d7fac380001ca1005000197.jpg\" data-original=\"http://img4.sycdn.imooc.com/5d7fac380001ca1005000197.jpg\" img_width=\"1229\" img_height=\"482\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img3.sycdn.imooc.com/5d7fac380001ca1012290482.jpg\" title></p><p><br></p><p>4、修改判断语句后的执行结果,将cond_0改为cond_1</p><p><img class=\"lazyload\" src=\"http://img4.sycdn.imooc.com/5d7fac4600017c4305000266.jpg\" data-original=\"http://img4.sycdn.imooc.com/5d7fac4600017c4305000266.jpg\" img_width=\"955\" img_height=\"508\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img2.sycdn.imooc.com/5d7fac4600017c4309550508.jpg\" title></p><p><br></p><p>5、重新编译apk</p><p><img class=\"lazyload\" src=\"http://img3.sycdn.imooc.com/5d7fac5800017dc105000079.jpg\" data-original=\"http://img3.sycdn.imooc.com/5d7fac5800017dc105000079.jpg\" img_width=\"1087\" img_height=\"171\" data-story_id=\"0\" data-image_ids=\"[]\" style=\"width:100%;\" alt=\"http://img2.sycdn.imooc.com/5d7fac5800017dc110870171.jpg\" title></p><p><br></p><p>apk重新编译后,会生成在AndroidKiller_v1.3.1\\AndroidKiller_v1.3.1\\projects\\login-release\\Bin下,名称为login-release_killer.apk</p><p>6、再次运行,无论输入什么用户名密码,弹出的消息都是登录成功了。</p><p>代码参考:</p><pre>https://github.com/freedom-wy/reverse_android/tree/master/project/login</pre><p>欢迎交流,一起学习,一起进步。</p><p>另外,我在慕课网上主讲课程:</p><p><a href=\"https://coding.imooc.com/class/283.html?mc_marking=561de3fb1745767d54fb64fc6c1604db&mc_channel=shouji\" target=\"_self\" title=\"《python爬虫工程师必学--App数据抓取实战》\" textvalue=\"《python爬虫工程师必学--App数据抓取实战》\">《python爬虫工程师必学--App数据抓取实战》</a>还请各位大神多多支持。</p><p><br></p>\n\t\t\t\t\t",
  380. "cover": [
  381. "http://img3.sycdn.imooc.com/5d7fab9e0001dfcb05000320.jpg",
  382. "http://img1.sycdn.imooc.com/5d7fabb60001bf3205000321.jpg",
  383. "http://img3.sycdn.imooc.com/5d7fabc00001608c05000160.jpg",
  384. "http://img3.sycdn.imooc.com/5d7fabcf0001978305000273.jpg",
  385. "http://img4.sycdn.imooc.com/5d7fabde0001253105000294.jpg",
  386. "http://img1.sycdn.imooc.com/5d7fabea0001a92305000081.jpg",
  387. "http://img1.sycdn.imooc.com/5d7fabf90001dfcb05000320.jpg",
  388. "http://img3.sycdn.imooc.com/5d7fac0600013f9405000223.jpg",
  389. "http://img4.sycdn.imooc.com/5d7fac270001a41605000391.jpg",
  390. "http://img4.sycdn.imooc.com/5d7fac380001ca1005000197.jpg",
  391. "http://img4.sycdn.imooc.com/5d7fac4600017c4305000266.jpg",
  392. "http://img3.sycdn.imooc.com/5d7fac5800017dc105000079.jpg"
  393. ],
  394. "mode": "column"
  395. },
  396. {
  397. "id": "301818",
  398. "title": "常见adb命令",
  399. "browse_count": 87,
  400. "collection_count": 0,
  401. "comments_count": 0,
  402. "author": {
  403. "id": "6433657",
  404. "author_name": "风落几番",
  405. "avatar": "//img1.sycdn.imooc.com/5b2a0c4d0001029d09600960-160-160.jpg",
  406. "status": "normal"
  407. },
  408. "classify": "其它",
  409. "thumbs_up_count": 0,
  410. "create_time": "2020.03.11 16:27",
  411. "content": "\n\t\t\t\t\t\t<p>1. 显示系统中全部设备: </p><p>adb devices </p><p>2.开启ADB服务: </p><p>adb start-server </p><p>3.关闭ADB服务: </p><p>adb kill-server </p><p>停止 adb 服务</p><p>4.连接设备: </p><p>adb connect 192.168.1.66 </p><p>5.断开设备: </p><p>adb disconnect 192.168.1.66</p><p>6.安装一个apk <br></p><p>adb install -r (APK路径) </p><p>7.直接卸载: </p><p>adb uninstall (apk包名) </p><p>8.列出手机装的所有app的包名: </p><p>adb shell pm list packages</p><p>9.清除应用数据与缓存: </p><p>adb shell pm clear (apk包名)</p><p>10.启动应用 </p><p>adb shell am start -n com.helloshan.demo/.MianActivity </p><p>11.强制停止应用 </p><p>需要强制停止应用,则执行以下命令: </p><p>adb shell am force-stop (apk包名) </p><p>12.删除系统应用: </p><p>adb remount (重新挂载系统分区,使系统分区重新可写)。 </p><p>adb shell </p><p>cd system/app/ </p><p>ls </p><p>rm *.apk </p><p>12.杀死某个进程:(三个步骤) </p><p>adb shell </p><p>ps </p><p>kill pid </p><p>ps是查看进程命令,kill pid 你想结束的进程 </p><p>13.获取文件的读写权限: </p><p>adb remount </p><p>有些设备并不能直接adb remount,必须要先以root身份进入,先执行adb root,在执行adb remount </p><p>14.查看日志: </p><p>adb logcat</p><p>15.常用操作文件夹命令 </p><p>操作文件和文件夹有时会出现权限不够,Read-only file system。就需要adb remount 操作,获得权限。 </p><p>cd system/sd/data //进入系统内指定文件夹 </p><p>ls //列表显示当前文件夹内容 </p><p>mkdir xxx //创建xxx的文件夹 </p><p>rm -r xxx //删除名字为xxx的文件夹及其里面的所有文件 </p><p>rm xxx //删除文件xxx </p><p>rmdir xxx //删除xxx的文件夹</p><p><br></p>\n\t\t\t\t\t",
  412. "cover": [],
  413. "mode": "base"
  414. },
  415. {
  416. "id": "298932",
  417. "title": "面对职场的“裁员潮”,“抢功劳”,“瞎指挥”,“薪资倒挂”,程序员们何去何从",
  418. "browse_count": 5058,
  419. "collection_count": 21,
  420. "comments_count": 4,
  421. "author": {
  422. "id": "6433657",
  423. "author_name": "风落几番",
  424. "avatar": "//img1.sycdn.imooc.com/5b2a0c4d0001029d09600960-160-160.jpg",
  425. "status": "normal"
  426. },
  427. "classify": "职场生活",
  428. "thumbs_up_count": 21,
  429. "create_time": "2020.01.02 16:49",
  430. "content": "\n\t\t\t\t\t\t<h2><a href=\"http://www.imooc.com/read/62?mc_marking=9d1b0be85540901e20d3310338bea1a4&mc_channel=shouji\" target=\"_self\">写在前边的话</a></h2><p>每次提起程序员的“<a href=\"http://www.imooc.com/read/62?mc_marking=9d1b0be85540901e20d3310338bea1a4&mc_channel=shouji\" target=\"_self\">情商</a>”,大家脑海里想到的故事都是这样的:</p><p><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5dfc48470001d0c605200311.png\" data-original=\"https://img1.sycdn.imooc.com/5dfc48470001d0c605200311.png\" style=\"width:100%;\"></p><p>或者是这样的:</p><p><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5dfc488a00011b8b04960312.png\" data-original=\"https://img1.sycdn.imooc.com/5dfc488a00011b8b04960312.png\" style=\"width:100%;\"></p><p>看到这样的故事和这样的调侃,我都有一点奇怪:我们程序员啊,或许情商不太高,但是也并<strong>不是智障</strong>好不好。至少像这样故事中“退化”到这种程度的程序员,我在工作这么多年以来,从来没有见过,这样的“<strong>情商故事</strong>”更多是杜撰出来的玩笑而已,而且仔细想想吧,这玩笑的杜撰者或许大概还必须自己得是个程序员。</p><p>所以,我们来聊一聊在我身边比较典型的“<strong>情商不足</strong>”的人。</p><h2>浅谈情商</h2><p>先来说说我们生活中见过比较多的一种类型:<strong>过于自信,目空一切</strong>。</p><p><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5dfc48aa0001e5ee01900224.png\" data-original=\"https://img1.sycdn.imooc.com/5dfc48aa0001e5ee01900224.png\" style=\"width:100%;\"></p><p>很肯定的是,程序员本来就是非常非常有自信的一种职业,我也不例外。我会对我自己写的每一行代码都爱不释手,鄙视一切其他人的代码甚至是自己过去的代码。其实这并不要紧,但是把这份自信无限制的放大到职场中,就适得其反了。职场有规则与价值观,放飞自我的任性,要出现在适合的地方。</p><p><em>我有一个前同事,技术能力非常出色,只是脾气非常火爆,经常称自己性子直,无论是领导还是同事,一言不合便“喷”了起来。开始还是以居高临下的姿态面对别人,最喜欢对别的程序员说教,不允许别人说自己不对,后来呢变成老板交代的工作,表面答应很好,之后推脱给别人去做,出了问题再各种借口。于是朋友寥寥,也有好心的同事提醒过他,但他总认为自己的资历深能力强,老板离不开他,不会把他怎么样。</em></p><p>事实是,无论你再有才华,持才傲物就是职场大忌了。越是在职场,越是要遵守职场的规则。地球离开谁都照样转,公司缺了谁都不会倒闭,时至今日,我还从来没有发现有什么“缺你不可”的事儿。</p><p>所以,如果你认为你非常重要,可以享受一些特权,那么对不起,你一定是想多了。</p><p><br></p><p>也有那么一种人,是那种自认为幽默感非常足,<strong>喜欢开玩笑却没分寸</strong>的人。</p><p><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5dfc48cc0001bef502660250.png\" data-original=\"https://img1.sycdn.imooc.com/5dfc48cc0001bef502660250.png\" style=\"width:100%;\"></p><p>幽默这件事吧,我觉得我自己稍微有一点发言权。我是一个土生土长的天津人,所以从蹒跚学步开始就少不了受到相声的教育,大一点了呢,无论是茶馆还是澡堂,处处弥漫着马志明先生的教诲。上了大学加入过相声社,打过快板说过相声,乃至到现在有了十来年的讲师经历,想必幽默这件事我肯定比不了专家,但是在诸位面前应该还是有点话语权的。</p><p>幽默确实是个技术含量非常高的活儿,有的人会觉得自己很好笑,各种“妙语连珠”,其实往往是流于恶俗,没有分寸。还是讲个例子吧。</p><p><em>我曾经有过这样一个同事,说“曾经”,想必大家也猜到了现在的结果。这是个比较愿意去活跃团队气氛的人。某一次,我们部门与其他部门配合一个项目,我们部门有一个长相一般的小姑娘受到了对方项目经理的夸赞,这时候,我们的“幽默大师”出现了:</em></p><p><em><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5dfc48e90001113403840338.png\" data-original=\"https://img1.sycdn.imooc.com/5dfc48e90001113403840338.png\" style=\"width:100%;\"></em></p><p>不要觉得你是在“吐槽大会”上,职场不是如来吐槽的,黑色幽默拿来自黑是“幽默”,用来黑别人那就惹人反感了。所以,请不要滥用自己的“幽默”,给幽默增加一点深度,过犹不及。</p><p><br></p><p>还有一种,也是最多的一种吧,就是<strong>喜欢抱怨,光说不练</strong>。当然,抱怨本身没有问题,这是一种人生态度和性格,我们不能强加更改,更核心的在于“<strong>只抱怨</strong>”。</p><p><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5dfc49090001b9ca01700187.png\" data-original=\"https://img1.sycdn.imooc.com/5dfc49090001b9ca01700187.png\" style=\"width:100%;\"></p><p>我刚工作的时候,有一个同事,现在关系也还不错,平时呢,我们偶尔出来聚个小餐、喝口小酒,顺便“工作的烦恼和哥们说说,上司的事情跟朋友谈谈”。最常挂在嘴边的话是:<strong>我真是受够了!这都是哪来的XX领导</strong>(请原谅我用XX替换掉在电视里应该开始哔的音效,自行填空)!接下来就是各种抱怨。</p><p>我得承认,每个人都喜欢抱怨,我也一样。然而多年的职场磨砺告诉我,没有任何一份工作是完美的。<strong>轻松愉快的工资少,给钱多的得加班,加班少的要心眼,要是真有一份工作机会告诉你工资多、不加班、办公环境好,那估计八成这是要把你卖到东南亚</strong>。尴尬不?其实不管在江河湖海还是在池塘,不会游泳的你还是不会游泳。</p><p>抱怨归抱怨,如果你一直停留在“领导看我不顺眼”这种思维里边,你就永远是一个“受害者”。真正的情商高的人会:<strong>大声抱怨,大口吃肉,然后扶石探路,摸索着前进</strong>。</p><p>所以,同志们,不是告诉大家不要抱怨,而是不要停留在抱怨,积极面对困境,毕竟“人在江湖飘,谁能不挨刀”,但是,“既在江湖飘,哪能总挨刀”,只要人在江湖,难忍的问题还是会在不经意的瞬间神出鬼没,并不会以你的意志为转移。也更加不是换一份工作就可以躲避的,唯一能做的就是<strong>修炼</strong>。挨欺负要总结,被针对要思考,具体的呢,我们后边聊。</p><h2>论“老实人”</h2><p>这个时候,估计又有同学要说了,风落啊,你说的这些例子都太极端了,而且都是他们个人原因,我可跟他们不一样,我是个<strong>老实人</strong>。</p><p>说到老实人这个词,在我年少无知的时候,这还是一个褒义词,至于现在,小灰下边的表情告诉你答案:</p><p><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5dfc492b0001701701960267.png\" data-original=\"https://img1.sycdn.imooc.com/5dfc492b0001701701960267.png\" style=\"width:100%;\"></p><p>实际上职场上纯正的老实人怕是不多了,但是很多人会有“老实人”的特征,这在职场上也是吃不消的。我在写这篇专栏的时候,特意好好回顾了一下身边的老实人,大体上有这么几类:</p><p><strong>“讨好型”老实人</strong></p><p>很多人都有这种讨好型的人格,表现在职场里就是特别特别考虑别人的感受,既怕麻烦别人,更怕得罪别人。</p><p><em>比如,加班去吃饭,太累了忘记了叫小白一起去,回来就想:小白是不是会不高兴?也会在每次开会后考虑:我的发言、我说的话会不会不合适?</em></p><p><em>甚至工作中难免的摩擦,也要考虑:我说的话是不是不太合适?</em></p><p>FINE,就算“职场如战场”,但是我们是为自己而战。职场上不需要忽略自己,更不需要舍己为人。</p><p><strong>“好说话的”老实人</strong></p><p>太好说话、耳根子软的老实人在职场中比比皆是,无论别人说什么,他都觉得:可以,没问题。更有甚者,前一天刚被被人坑的死去活来,第二天别人随便恭维几句,立刻化干戈为玉帛,手牵手共同展望美好明天了。</p><p>这样的老实人是真的那么“好说话”,真的“那么好脾气”么?也不然。我之前招到过一个应届毕业的实习生,大体就是这样的“老实人”,别人让做什么做什么。后来实习期满结束,我请他吃饭送别,也问到了这个问题。</p><p><em>他说:其实我每天回去都不开心,都要骂自己,可是我觉得我才刚毕业,什么都不会,我再不听别人的话,人家更加不会教我东西了。人家不都说么,吃亏是福,我觉得吃亏也是好事吧?</em></p><p>虽然说来如此,但是别人就是会吃准了这套,不断的提要求,不断的削弱老实人的话语权,最后,你就什么都没有了。</p><p>所以我觉得,职场上的奥义在于:我们的目标可能不是想说就说,而是<strong>想说“不”就可以说“不”</strong>。当然,这很难,甚至我自己也不能做到(老板你还好吗o(<em> ̄︶ ̄</em>)o),但至少在适当的时候给自己说“不”的权利,给自己说话的自由。</p><p><img class=\"lazyload\" src=\"https://img1.sycdn.imooc.com/5dfc494a0001a03b01760207.png\" data-original=\"https://img1.sycdn.imooc.com/5dfc494a0001a03b01760207.png\" style=\"width:100%;\"></p><p><strong>“不会说话”的老实人</strong></p><p>这是我在职场中生活中最不想遇到的人,不知道该说他们直言爽快还是老实无脑,总之是既好笑又无奈。</p><p>有那么一个同事,别人呢,刚刚买了一套学区房,有人主动问起就非常高兴的跟大家聊起来,这个可爱的的同事呢,突然开始插嘴:<em>啊,其实吧,那个学校好多人都知道,不怎么样,攀比很严重还特别乱。</em>瞬间全场安静。</p><p>然而实际上,他既没有对象更没有孩子,当然,这样的“毒蛇”不知道啥时候才能有对象。</p><p>我不知道他在说这句话的时候是出于嫉妒还是只是表达自己知道的情况,毕竟我可舍不得买下帝都的学区房。这是不会说话的极端情况,但是想审视自己的同学,以后说话前可以多想一下:你的这句话会让别人有什么样的感受?</p><p>点名了寥寥几种老实人,实际上还有很多。比如好面子的老实人、不够积极主动的老实人,甚至是没有底线的老实人等等,老实人本身没问题,但是职场中这些所谓的“老实”反而成为了限制自身发展的“缺点”。</p><h2><a href=\"http://www.imooc.com/read/62?mc_marking=9d1b0be85540901e20d3310338bea1a4&mc_channel=shouji\" target=\"_self\">不一样的“情商课”</a></h2><p>其实这一趴我们应该叫“<strong>情商课之反躬自省篇</strong>”。大家尽情可以把自己跟上边的情商故事对比一下,先大体上看看自己到底有哪些不足,相信总可以描绘出一些自己的剪影。但是随着互联网时代的发展,软件行业的故事也在不断增多,面对不同的场景到底应该做什么、怎么做其实仍然是个大问题。</p><p>如果有熟悉我的朋友呢,可能知道,我是一个非常喜欢辩论的人,大学期间参加辩论赛后来又做领队的。喜欢到什么程度呢?这么说吧,我老婆一直不遗余力的想找渠道给我报名《奇葩说》!好吧,让大家失望的是,这么一大段夸奖自己的话并不是为了引入什么“将辩论带入职场”的思想,而是想起了《奇葩说》里黄执中说过的一段话:</p><blockquote><p><em>人生的困扰,说到底,十之八九,问题都出在人际关系。而人际关系的困扰,说到底,十之八九,都是因为沟通出了问题。</em></p><p><em>无论家庭、校园还是职场,无论亲情、爱情还是友情……生命中,我们绝大多数的纠结与困惑,愤怒与失落,根源,都来自我们怎么与别人对话、协商、争论、说服。</em></p></blockquote><p>所以,在我看来,真正的情商问题,是人际关系问题、是沟通问题,具体来说,是自我管理、与人沟通方式方法和心理态度方面的问题。</p><p>在这个专栏里,我并不想讲一些大道理,更不想用一些名人轶事来告诉大家:有情商应该怎么做事,我们应该学习黄渤、学习蔡康永的说话之道。混迹职场,失之一语,差之千里。我更愿意用一些“真实的”、“常见的”场景来细致剖析一下职场的情商。</p><p>曾经看过这么一句话,深以为然:</p><p><strong>职场上,一个人的智商决定了他是否能够走上成功的道路,而一个人的情商却决定他能在这条路上走多远。</strong></p><p>然而,没有谁是天生的情商高手,所以我们需要也应该付出时间去实践、提升,为自己开辟一条更宽阔的职场空间。</p><p>与君共勉。</p><p>我希望的,是我能够把我自己亲身经历的、有感而发的“职场那些事”分享给大家,希望能够对大家有所启迪和帮助。</p><p>最后,欢迎加入《<a href=\"http://www.imooc.com/read/62?mc_marking=9d1b0be85540901e20d3310338bea1a4&mc_channel=shouji\">程序员情商课</a>》,我是风落,让我们一起做”有情商“的程序高手。</p><p>————————————————————————————————————————————</p><p>小尾巴:</p><p>我是蚂蚁金服风落,一只撸过代码、做过测试、搞过运维、当过项目经理的程序猿,欢迎大家关注我:</p><p>传送门:<a href=\"https://www.imooc.com/t/6433657\" _class=\"lazyload\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC\" data-original=\"https://www.imooc.com/t/6433657\">https://www.imooc.com/t/6433657</a> </p><p><strong>我的慕课专栏:</strong></p><p><a href=\"http://www.imooc.com/read/62?mc_marking=9d1b0be85540901e20d3310338bea1a4&mc_channel=shouji\" target=\"_blank\" textvalue=\"程序员情商课\">程序员情商课</a></p><p><a href=\"http://www.imooc.com/read/48?mc_marking=ee37b7e3ebd0f1c9dbae15dbc1fee33f&mc_channel=shouji\" target=\"_blank\" title=\"优秀测试工程师的必备思维39讲\" textvalue=\"优秀测试工程师的必备思维39讲\">优秀测试工程师的必备思维39讲</a></p><p><strong>我的课程:</strong></p><p><a href=\"https://coding.imooc.com/class/398.html?mc_marking=388439060529cc3bf69d2e9960e99385&mc_channel=shouji\" target=\"_blank\" title=\"编程必备基础 大话HTTP协议\" textvalue=\"编程必备基础 大话HTTP协议\">编程必备基础 大话HTTP协议</a></p><p><a href=\"https://coding.imooc.com/class/277.html?mc_marking=4b17dbb2a9c67ea3131cef1e6c865c3b&mc_channel=shouji\" target=\"_blank\" title=\"小白福音 零基础入门软件测试 首选必备课程\" textvalue=\"小白福音 零基础入门软件测试 首选必备课程\">小白福音 零基础入门软件测试 首选必备课程</a></p><p><a href=\"https://coding.imooc.com/class/305.html?mc_marking=7d3933c6bc58bf477416d5e2871d77c2&mc_channel=shouji\" target=\"_blank\" title=\"LoadRunner性能测试实战训练营 收藏\" textvalue=\"LoadRunner性能测试实战训练营\">LoadRunner性能测试实战训练营</a></p><p><a href=\"https://coding.imooc.com/class/238.html?mc_marking=c3a94b7a8f472bf169082c7c0fdad789&mc_channel=shouji\" target=\"_blank\" title=\"Selenium3.0平台级自动化测试框架综合实战\" textvalue=\"Selenium3.0平台级自动化测试框架综合实战\">Selenium3.0平台级自动化测试框架综合实战</a></p>\n\t\t\t\t\t",
  431. "cover": [
  432. "https://img1.sycdn.imooc.com/5dfc48470001d0c605200311.png",
  433. "https://img1.sycdn.imooc.com/5dfc488a00011b8b04960312.png",
  434. "https://img1.sycdn.imooc.com/5dfc48aa0001e5ee01900224.png",
  435. "https://img1.sycdn.imooc.com/5dfc48cc0001bef502660250.png",
  436. "https://img1.sycdn.imooc.com/5dfc48e90001113403840338.png",
  437. "https://img1.sycdn.imooc.com/5dfc49090001b9ca01700187.png",
  438. "https://img1.sycdn.imooc.com/5dfc492b0001701701960267.png",
  439. "https://img1.sycdn.imooc.com/5dfc494a0001a03b01760207.png"
  440. ],
  441. "mode": "column"
  442. }
  443. ]
  444. },
  445. "user": { // 用户表
  446. "data": [{
  447. "id": "8010388",
  448. "author_name": "Java架构师讲师团",
  449. "avatar": "//img2.sycdn.imooc.com/5dafce1a00013fd501400140-160-160.jpg",
  450. "status": "normal",
  451. "fans_count": 0,
  452. "professional": "架构师",
  453. "explain": "架构师成长沟通群878622640,欢迎加入~",
  454. "gender": "男",
  455. "follow_count": 0,
  456. "integral_count": "",
  457. "article_likes_ids": [],
  458. "author_likes_ids": [],
  459. "thumbs_up_article_ids": [],
  460. "article_ids": [
  461. "302042",
  462. "301911"
  463. ]
  464. },
  465. {
  466. "id": "4294850",
  467. "author_name": "7七月",
  468. "avatar": "//img3.sycdn.imooc.com/54584e2c00010a2c02200220-160-160.jpg",
  469. "status": "normal",
  470. "fans_count": 0,
  471. "professional": "全栈工程师",
  472. "explain": "十年研发及团队管理经验,对程序员面临的各种问题深有体会;精通Python、Java、Node.js、JavaScript等语言,对Web的基础研发、高并发处理与分布式有非常深入的理解。课程讲解深入浅出,极为擅长培养学生的编程思维。",
  473. "gender": "男",
  474. "follow_count": 0,
  475. "integral_count": "",
  476. "article_likes_ids": [],
  477. "author_likes_ids": [],
  478. "thumbs_up_article_ids": [],
  479. "article_ids": [
  480. "298045",
  481. "286282"
  482. ]
  483. },
  484. {
  485. "id": "5027812",
  486. "author_name": "快乐动起来呀",
  487. "avatar": "//img3.sycdn.imooc.com/54584cb50001e5b302200220-160-160.jpg",
  488. "status": "normal",
  489. "fans_count": 0,
  490. "professional": "Web前端工程师",
  491. "explain": "7年工作经验的资深前端工程师,主要从事Web视频领域的底层开发。先后在360、去哪儿网、某知名视频公司工作。",
  492. "gender": "男",
  493. "follow_count": 0,
  494. "integral_count": "",
  495. "article_likes_ids": [],
  496. "author_likes_ids": [],
  497. "thumbs_up_article_ids": [],
  498. "article_ids": [
  499. "294509",
  500. "293012"
  501. ]
  502. },
  503. {
  504. "id": "4427201",
  505. "author_name": "双越",
  506. "avatar": "//img4.sycdn.imooc.com/5a9fc8070001a82402060220-160-160.jpg",
  507. "status": "normal",
  508. "fans_count": 0,
  509. "professional": "Web前端工程师",
  510. "explain": "资深前端工程师,PMP,项目技术负责人,现就职于BAT一线互联网公司。他的博客总流量过百万,如《深入理解JS原型和闭包》《深入理解JS异步》等。在慕课网已推出《前端javascript面试技巧》《nodejs 从零开发博客项目 web server》等教程。",
  511. "gender": "男",
  512. "follow_count": 0,
  513. "integral_count": "",
  514. "article_likes_ids": [],
  515. "author_likes_ids": [],
  516. "thumbs_up_article_ids": [],
  517. "article_ids": [
  518. "300475",
  519. "293310"
  520. ]
  521. },
  522. {
  523. "id": "108955",
  524. "author_name": "liuyubobobo",
  525. "avatar": "//img4.sycdn.imooc.com/5347593e00010cfb01400140-160-160.jpg",
  526. "status": "normal",
  527. "fans_count": 0,
  528. "professional": "全栈工程师",
  529. "explain": "创业者,全栈工程师,持续学习者。对技术开发,产品设计、前后端,ios,html5,智能算法等领域均有接触;拥有多款独立App作品;对一切可编程的东西有浓厚兴趣,对游戏编程格外感兴趣。相信编程改变一切。",
  530. "gender": "男",
  531. "follow_count": 0,
  532. "integral_count": "",
  533. "article_likes_ids": [],
  534. "author_likes_ids": [],
  535. "thumbs_up_article_ids": [],
  536. "article_ids": [
  537. "289159",
  538. "26624"
  539. ]
  540. },
  541. {
  542. "id": "2255006",
  543. "author_name": "bobby",
  544. "avatar": "//img1.sycdn.imooc.com/58d9c48f0001ad0304070270-160-160.jpg",
  545. "status": "normal",
  546. "fans_count": 0,
  547. "professional": "全栈工程师",
  548. "explain": "python全栈工程师,五年工作经验,喜欢钻研python技术,对爬虫、web开发以及机器学习有浓厚的兴趣,关注前沿技术以及发展趋势。",
  549. "gender": "男",
  550. "follow_count": 0,
  551. "integral_count": "",
  552. "article_likes_ids": [],
  553. "author_likes_ids": [],
  554. "thumbs_up_article_ids": [],
  555. "article_ids": [
  556. "291507",
  557. "283364"
  558. ]
  559. },
  560. {
  561. "id": "4873493",
  562. "author_name": "李超",
  563. "avatar": "//img1.sycdn.imooc.com/5b9876c60001ffc914821482-160-160.jpg",
  564. "status": "normal",
  565. "fans_count": 0,
  566. "professional": "全栈工程师",
  567. "explain": "10多年的软件开发经历,8年多的音视频直播行业相关经验,6年多的团队管理经验,丰富的linux开发经验,对Linux内核做过深入分析。参加并设计了多个高负载,大并发服务器架构。",
  568. "gender": "男",
  569. "follow_count": 0,
  570. "integral_count": "",
  571. "article_likes_ids": [],
  572. "author_likes_ids": [],
  573. "thumbs_up_article_ids": [],
  574. "article_ids": [
  575. "295301",
  576. "286567"
  577. ]
  578. },
  579. {
  580. "id": "6685330",
  581. "author_name": "大壮老师",
  582. "avatar": "//img1.sycdn.imooc.com/54584f850001c0bc02200220-160-160.jpg",
  583. "status": "normal",
  584. "fans_count": 0,
  585. "professional": "Python工程师",
  586. "explain": "目前任职于某大型互联网公司人工智能中心。Python开发工程师,主要负责汽车简历数据抓取、商业推广平台数据抓取及接口开发、竞品信息数据抓取等工作。 开发语言:python、autoit。项目中主要使用工具requests 多线程抓取网页系统数据,使用autoit抓取软件系统数据,使用appium抓取app系统数据等。使用scrapy进行大数据量信息抓取。",
  587. "gender": "男",
  588. "follow_count": 0,
  589. "integral_count": "",
  590. "article_likes_ids": [],
  591. "author_likes_ids": [],
  592. "thumbs_up_article_ids": [],
  593. "article_ids": [
  594. "292698",
  595. "292504"
  596. ]
  597. },
  598. {
  599. "id": "6433657",
  600. "author_name": "风落几番",
  601. "avatar": "//img3.sycdn.imooc.com/5b2a0c4d0001029d09600960-160-160.jpg",
  602. "status": "normal",
  603. "fans_count": 0,
  604. "professional": "全栈工程师",
  605. "explain": "蚂蚁金服测试专家,带领团队从零建立多个大型项目安全测试,针对Web安全测试搭建了基于开源工具的安全测试架构,独立开发基于WebDriver的自动化测试平台,著有《LoadRunner性能测试巧匠训练营》。",
  606. "gender": "男",
  607. "follow_count": 0,
  608. "integral_count": "",
  609. "article_likes_ids": [],
  610. "author_likes_ids": [],
  611. "thumbs_up_article_ids": [],
  612. "article_ids": [
  613. "301818",
  614. "298932"
  615. ]
  616. }
  617. ]
  618. },
  619. "label": { // 分类表
  620. "data": [{
  621. "name": "后端开发",
  622. "user":[]
  623. }, {
  624. "name": "职场生活",
  625. "user":[]
  626. },
  627. {
  628. "name": "前端开发",
  629. "user":[]
  630. },
  631. {
  632. "name": "人工智能",
  633. "user":[]
  634. },
  635. {
  636. "name": "移动开发",
  637. "user":[]
  638. },
  639. {
  640. "name": "其它",
  641. "user":[]
  642. }
  643. ]
  644. }
  645. }

结束