2020年中房价分析,预测

前言

19年底写过一篇房价分析的文章,如今这非比寻常的半年过去了,是时候写一篇新的文章再分析一波了。

大纲

  1. 外部风险

    1.1 全球性金融危机
    1.2 金融危机给国内经济带来的影响

  2. 国内经济的韧性

  3. 政策预测

    3.1 房产税时间点
    3.2 其他金融工具

  4. 总结

全球性金融危机

这个章节有两个问题

  1. 是否会发生全球性金融危机
  2. 危机会到什么程度,会对我们的经济造成多大影响

个人认为第一个问题可以略过,危机不是是否会发生,而是已经发生,正在发生。之所以大家现在还无感,美股又重新涨回去,这完全是一种回光返照的现象。美国开启无限量 QE 这个行为是绝对会有严重后果的。

现在的情况是,08 年之后,全球经济开启量化宽松把当时的损害降到最小,但造成的后果是全球经济长期处于滞胀状态。问题最严重的当属日本,长期大力印钞但是物价和经济就是上不去。原因是金融离不开基础,没有实体的支撑民众信心不足,消费不足,怎么刺激都没用。欧洲其次,和日本一样老龄化问题严重,不同于日本引入了一些移民,且资本更加老牌一些,面对中国崛起被蚕食得部分更少一些,两方面加起来总体面子上好看一些。但实际内里都很虚,欧盟内部问题积累严重,希腊那次还远没有真正爆发出来。08年金融危机是美国引发的,结果是通过印钞让全世界为其买单,欧洲、日本现在的虚,很大一部分原因也都是这一波造成的贻害。并且美国自己也虚,08年之后产业反而更加空心化,国内贫富分化等社会问题愈加严重。

总之就是08年危机之后,人类并未爆发新的产业革命、技术革命,经济成长除了中国以外基本都是帐面上的虚数。实际是危机造成的影响根本没有去化,甚至更加严重,只是靠新兴国家的发展通过剪羊毛的方式获得了不少输血,仅维持表面的繁荣。

金融危机给国内经济带来的影响

第二个问题,危机会到什么程度?这个问题无法定量分析,且听在下高谈阔论吹一波。首先此次的经济危机会和历史上以往的经济危机大不一样。以往的经济危机基本两种形式。工业时代之前只有通货膨胀形式的金融危机。工业时代之后基本上是产能过剩之后迫使政府超发货币进而通货膨胀,两样并发。

而这次的危机是由全球疫情引发,迫使美国超发货币引起通货膨胀之后导致经济危机。

而这次的危机会造成怎样的影响,主要要看我国 ZF 的应对方式,要不要跟着美国一起放水。如果跟着一起放,因为我们对美国是贸易顺差,那么本国国民就要相对美国国民成倍的承受通胀压力。如果不跟着放人民币将面对巨大的升值压力。虽然从理论上讲,因为金融的不开放外资很难自由兑换。总之升值的压力肯定会有,升值之后又会降低自己出口产品的竞争力,短时间内会对经济造成重创。

左右为难,我目前的观察,我国 ZF 是跟着放一点,但是幅度很小很谨慎。我觉得是央妈觉得形势远没有到最困难的时候,最后的武器要留着最后用。

按照现在情况推演下去,美国会因为疫情和国内其他矛盾集体爆发导致社会运转出现重大危机,进而导致美元信用大幅振动,全球贸易可能因为一般等价物的波动受到巨大影响。届时人民币必将面对巨大的升值压力,而此时正是央妈开闸大放水的最好时期。虽然对外贸易会受到巨大影响,但是跟夺取一般等价物这个蓝星至宝相比,简直不值一提。哪怕只是夺取了部分。舰到用时方恨少,多两条 003 形势简直不是小好,是大好。可惜,可惜。

上面是往好了想,往坏了想,全球其他国家会认为美元都不靠谱的情况下我没有理由认为人民币更靠谱。全球重回金本位,金价暴涨,这种暴涨可能是十倍百倍级别的。迫使各国银行合作建立起以金本位为准的外汇结算机制(毅种循环)。全球经济进入长期滞胀的大萧条时代。除了黄金和食物任何东西都不会涨价。粮食不能自给的国家必将爆发饥荒战争不可避免,人类文明摇摇欲坠。

至于这两种情况下房价会怎样,我想已无须多言。

国内经济的韧性

OK,让我们先蒙上双眼,假装全球性的经济危机不会发生。假装新冠的影响从下半年开始会慢慢减弱,假装美国新印的钞票不会产生多大影响,假装世界各国的经济都会渐渐恢复,全球贸易也会渐渐恢复。只保留一个条件即因为新冠的影响世界贸易不可能回到新冠前的水准。

首先看下出口贸易占我国经济的比重。商务部的数据是 19.6%,世界银行的数据是 18.4%。假设由新冠影响今年的出口贸易会减少 20% (根据前几个月数据预估)。

商务部的数据是外贸带动了 1.9 亿人就业,做个简单的数学题,少 20% 意味着 3800w 人失业。连带着第三产业的消费娱乐方面的减少,我们的失业人数是个绝对不能统计的量级。

这么多人失业房价必跌啊!其实吧,说真的,咋说呢,没啥影响。为啥,资本主义里面有消费者,有劳动力,对于房子来说这部分失业的人原来是劳动力,不是消费者,现在都不是了。仅此而已 over。顶多也就是对三四五线城市的房市有些影响。我举个不恰当的例子,一个餐馆倒闭了,总共一个老板15个员工,你觉得这个城市少了几个购房者?老板通过贷款又买了两套,喜+1,有没有。

现在的情况是因为处于特殊时期,正常的经济增长对应房价增长的逻辑已经不存在了。世界大放水的前提下,只要不发生经济危机,房价没有跌的理由。

所以本节讨论的问题点是,外贸下降 20%,以及国内自身经济受到新冠冲击的情况下,会不会发生经济危机。

个人认为完全不会。只要不发生上一节提到的最坏情况,其他问题在我国基本都不是问题。ZF 还有太多的手段没用上。到目前为止也就金融系统向实体让利 1.5 万亿罢了。既没有 08 年的 4w 亿,也没有 15 16 年的放松限购限贷降低利率。就目前的情况正如中央所说稳中向好。事实如此。

政策预测

如果经济不崩的情况下,国内的房价基本就取决于政策。我想这个观点应该没有几个人会反对。

那么预测房价这个命题大部分时候也就是在预测政策。我在2019房价分析,未来房价预测一文中通篇没提政策,但是实际上是已经默认了 ZF 是希望抑制房价的。个人认为在 19 年及之前贸易战的环境下,房价的横盘或阴跌是最有利于整个国民经济的选项。所以我在 19 年底做出了那样的预测。

那么现在环境下,房价怎样的走势是最利于整个国民经济的呢?

当然还是横盘或阴跌。横盘或阴跌,减轻居民负担,为增加消费提供空间。尤其是在外贸不振的情况下,内需更显重要。降低年轻人的负担,提高生育率其实也是当务之急。唯一的问题就是地方财政。这个问题,我似乎已经看到了解决方法,就是加大土地供应,地价卖不高就多卖几块。正好加紧时间出清,出完了正好推房产税。届时地方财政怎么从卖地转移到房产税收将是一个重大的历史关头。这个话题留待以后再聊。

现在我们已经有了两个基本条件一是将来大概率需要放水,二是要严防房地产暴涨。暴涨不仅会在短期内吹大泡沫造成完全不可收拾的局面,更会吸走放出来的水,严重滞后经济恢复的时间。

其实这个目标中央已经在新闻里面说的非常清楚了,六保六稳,总结下来就一条稳房价。房价稳通通稳,房价保全部保。

房产税时间点

个人认为大致时间应当是可以推断出来的。即二三四线城市的地方财政无法再以卖地维持之时。

上表是我国历年卖地总收入,须知土地是有限资源,总有卖完的一天,面对现在天量的卖地收入,将来应该拿什么来填呢?很明显答案只有一个。

这里以我比较了解的南京来举个例子说明。目前南京长住人口 850w+ 流动人口 400w+,我斗胆估计一下南京未来 50 年的总人口峰值会在 1600w+ 左右。也就是说距离峰值还有 350w 的增长空间。目前南京每年大约成交 7w 套新房。按三口之家算可以满足 21w 新增人口。排除其他所有因素,南京的住房最多还有 15 年时间就将达到完全饱和。

拿发达国家的经验来看,有 1/3 的家庭是租房的,因为房产税和租售同权的原因。我们未来肯定是要走相同的路的。这种情况下还会挤出相当一部分的空置住房,再加上人口的自然死亡消减。即使是南京这样的新一线粗略估计最多 10 年也必将饱和。

继续思考,分税制是全国统一的,假设房产税也是。南京还能撑 10 年,其他的二三四线城市呢?很多城市人口流入都成问题。再次可惜我仍然查不到太多可信的数据。大家凭自身感觉吧。就目前的情况来看,四线及以下的城市别说增长了可能维持都是问题。所以中央有意对农村进行改造,收拢原来分散居住的农民,集中建设住房。常看新闻联播的人应该听过相关新闻。这其中不乏为四线以下城市提供新增人口的想法。

我在之前的文章中就提到 5 年之内必收房产税,就是基于以上的思考。卖地收入无法负担地方财政之时,房产税必须上马,这是不以人的意志为转移的。

其他金融工具

中国股市长久以来无法崛起发挥其应有的金融工具作用,最主要的原因不是国家的监管不利,或是房地产的吸水。而是一个更加简单的原因,在坐的都是垃圾。

美国公司要想拉升股价都是靠分红,投资者获得收益才看好你,投钱给你(买你的股票)。中国各种骚操作就是没这一项,真是奇葩。

为何如此,就是因为入场要求卡的太严,那些个变态的入场要求,完全都是在扯淡。在中国要是有家公司真的能达到三年稳定业绩利润增长,它还用得着上市去筹钱!?估计天使和风投早就把门槛都踏烂了。

注册制能不能改变这个现状我不知道,但至少这是中央在往这个方向努力的表现。如果将来股市或债市或其他金融工具能够真正起到金融工具的作用,那么房子也将真正褪去它的金融属性变成纯粹的房子,纯粹的消费品。

总结

在 19 年底的时候我认为房价的拐点已经到来,在疫情发生的时候我确定房价的拐点已经到来。直到美国开启了无限量 QE 。未来五年在大水漫灌的前提下,只要不发生重大经济崩溃,房价已不可能再下跌。

而五年之后土地财政走到尽头,房产税时代开启之后。房价的泡沫必将被挤出。最后再分析一下一套房子的合理价值应该是多少。

从南京某小区采样举例 165w 的房子租金 3000,租售比 46。按照国际上合理的租售比 30 来计算,合理的价格应在 108w。5 年后假设房产税出台价值回归合理区间如果租金按照每年 5% 的比率上涨,该房的价值应在 137w。这是租售比为 46 的房子的情况,损失为 17%。而租售比为 60 的房子房产税出台后的损失在 38% 左右。

以上,在下之所以,长时间关注房地产,主要是因为自己有改善的需求。写相关文章也是为自己买房理清思路。这里与大家分享交流,谢谢各位的点赞转发,但若按我的思路投资失败,在下概不负责,谢谢!

共产主义革命的浪潮必将再次席卷世界

共产党人不屑于隐瞒自己的观点和意图。他们公开宣布:他们的目的只有用暴力推翻全部现存的社会制度才能达到。让统治阶级在共产主义革命面前发抖吧。无产者在这个革命中失去的只是锁链。他们获得的将是整个世界。全世界无产者,联合起来!

—— 共产党宣言

当今世界有的国家剥夺了人民各种自由,集会自由,言论自由,婚姻自由,甚至玩个游戏的自由都不放过。

当今世界有的国家给予人民各种自由,自由的病死,自由的枪战,自由的被警察杀害。

是制度的不同,主义的不同造成了这样的差别吗?不,恰恰是相同的制度,相同的主义造成了相同的灾难。

资本主义的原生缺陷:

  1. 人与人的关系被金钱(资本主导的生产关系)定义。
  2. 产能过剩必然造成周期性的金融危机。
  3. 向帝国主义不可逆的进发,导致的贫富分化阶级矛盾。

上述的问题都可以在这些原生缺陷中找到答案,问题的表象不同只是来自于,社会发展的阶段不同以及少量具体国情的差别。

两国精英忽悠无产阶级的口吻都出奇的一致,国家太大无法施行北欧模式。

我有个更靠谱的理由,人的劣根性。我就是想住在几千万的豪宅里面,请一票菲佣,饭来张口衣来伸手,并且我的子孙后代都要这样。

错了吗?到底谁错了?

我不知道,我可以预见的是。太阳底下没有新鲜事,历史必将重演。

当巨龙肆虐蹂躏世界,屠龙的勇士必将出现。共产主义革命的浪潮必将再次席卷世界!

之后勇士变作巨龙,继续今天的故事。

肖氏毒奶

在这里把过往和今后的预言整理下,也算存档留证。

14年预言

农民土地允许上市交易(验证部分)

15年预言

16年房市大涨(验证)

18年预言

19年房市横盘或下跌(自行体会)

19年预言

20年房市横盘或下跌(待验证)

2020年3月2日

美股小规模反弹之后持续下跌,2020年引发美国乃至世界金融危机。

第三次。。(这条我先吃了)

房产税五年内出台

中国房市拐点已到,具体要看房产税落实时间点。

N1小钢炮固件使用docker-ui安装openwrt

Docker-ui 安装及设置

进入N1小钢炮管理后台,点击 System 选项,之后再点击 Startup,在右侧往下拖,找到 Docker 的对应进程选项 /etc/init.d/S60dockerd,把 NO 点为 YES 启用 Docker,再点击左边的 start,之后点击下面的 SAVE 保存

点击Apps-Other-Docker Setting-install Docker UI (没安装时默认为黄色,下图为已经安装完成后的显示)

特别注意:这步操作没有在没有科学上网的情况下可能无法顺利完成

之后重启 N1

Openwrt 镜像容器安装步骤

  1. 通过 ssh 连接 N1
  2. 输入命令拉取OpenWrt镜像 docker pull kanshudj/n1-openwrtgateway:r9 此步同样建议使用科学上网环境
  3. 运行:ip link set eth0 promisc on
  4. 运行:docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 -o parent=eth0 macnet
    (将第三个单独的1改为你主路由的网关地址,即你登录你主路由后台的IP地址第三位数字)

    这步是在 docker 环境中创建一个名为 macnet 的网卡

  5. 运行:docker run --restart always -d --network macnet --privileged kanshudj/n1-openwrtgateway:r9 /sbin/init
  6. 进入 Docker 管理界面,点击 Local,再点击 Containers
  1. 找到自己刚拉取完成的 OpenWrt 镜像,选择第四个命令行工具
  1. 点击Connect
  1. 输入 vi /etc/config/network
  1. 找到3处包含192.168.X.X的地方,输入i进入编辑,同样将第三个数字的位置改为你主路由的网段,在此处我的主路由为123,所以我将三处都改为123。当编辑完成后,按一次键盘左上角Esc键,之后输入:wq并且回车。(英文冒号+wq)

  2. 设置 OpenWrt 的初始密码命令为 passwd 还有可能为 passwordmount_root。输入正确之后会提示你输入正确明码。

  3. 重启N1

之后就可以通过你设置的 Openwrt 地址进入管理界面了(管理地址为上如3红框中的第一个),到这里我们旁路由OpenWrt就算全部设置完成了

参考文章

斐讯N1小钢炮Docker安装OpenWrt/LEDE做旁路由稳定去广告+科学功能

PS

我自己进行相关操作时遇到了两个坑,故把前人的操作步骤再完善一下,希望能让后来者少走弯路。

罗老师语录

1.“这几天又买了十几台安卓手机试玩儿,包括三星摩托罗拉索尼HTC小米魅族的热门机型(大都是我们前期的安卓定制版将要支持的机型)……怎么说呢?只能说“越看越放心”吧。还是那句话,这个行业只有一个聪明人,可他已经死了,剩下的是一群选错了行业、即将再次被虐杀的倒霉蛋……”——罗永浩

2.“把车停到公司楼下, 我摸了摸,给同事买的几盒炒面依然滚烫。去时的大雪完全停了,我打开天窗,车顶残留的雪落了我一头,能看到很多星星,凉凉的空气好得不像话,喇叭里的音乐也从重金属变成了钢琴曲…突然我就伤感起来了:你只是勤奋工作,努力做好自己,结果很多你的同行就要倒闭了…生命真残酷啊。”——罗永浩

3.“自打我公布要做手机以来,已经有无数朋友给我“支招“了,大部分主意集中在如何“填补市场空白”……唉,我要做的是最大最热最帅,竞争最残酷激烈的那块核心地带啊,“填补市场空白“还是留给那些没有雄心壮志,只想赚点儿小钱的企业吧。”——罗永浩

4.“我们做两到三代产品之后,灭掉苹果是没有问题,只要我们做成功了两款产品,第三款产品一定是去北美卖的,我不满足在中国做一个企业,没什么大意思”——罗永浩

5.”托马斯·骆说的好,不过还是保守了。用户体验、审美、营销推广、恋物、完美主义倾向这五项我都不输乔布斯,只差了一个现实扭曲场,但我人格力量远胜,加上这个行业全是土鳖和笨蛋,不骄傲地说,胜算很大。”——罗永浩

6.”卖出去的产品要做好售后服务,这是做企业的基本原则,但卖东西的时候我们不会刻意讨好或迎合各种脑子不好的消费者,好东西牛逼哄哄的摆在那里,你爱买不买。”——罗永浩

7.”摩托罗拉南京研发中心有四百多人?!难怪这企业要完蛋。”——罗永浩

8.”风投的那帮孙子只要有钱赚就会扑上来,不用惯着他们,否则你去舔他们也是白舔。”——罗永浩

9.”当然,牛逼吹得非常保守,比如我就不觉得我们的第一代硬件产品从工业设计和工艺上能全面超过iPhone。” ——罗永浩

10.“是,所以我也没打算一下就灭了苹果,先是三星、LG、索尼、HTC、OPPO、魅族…”——罗永浩

11.”嗯,觉得自己可以接乔布斯班的笨蛋很多,我看好的只有这一个 ,苹果之所以成功,除商业模式创新之外,最重要的是乔布斯不仅是顶级艺术家,还拥有光芒万丈的商业智慧,于是苹果成为独一无二的苹果。国内的@罗永浩可爱多 有艺术家的气质,也有非凡的商业智慧,个人品牌传播力也非常强悍,或许会成为中国的乔布斯,也未尝不可能。”——罗永浩

12.”虽然只想了几星期,但我们在软硬件结合改善用户体验方面的创新方案足以使我们将来单靠卖专利(已在准备专利申请所需的材料了)都能维持…可惜没碰到有眼光、大手笔的风投资本家(其实也不需要很大手笔,一千多万美元而已[挤眼]),逼得我们只好先做rom后做硬件…好饭不怕晚,只是苦了望眼欲穿的人民。”——罗永浩

13.”放心,手机进展很顺利,胜利的结局完全没有悬念,如果不是热爱做这个,早就受不了这么不刺激的创业了。”——罗永浩

14.“昨晚看完胡德夫回来加班,本来四点能完的工作折腾到了八点,大部分进展顺利,但个别难题到下班为止还没解决,希望今天下午搞定…又要忍不住说实话了:我们的rom就算打50%折扣也是安卓系统的第一。谷歌看了保证当场抽搐,接下来要收购可就讨厌了,我肯定是不卖的,但他们一生气不开源了就有点小麻烦。”——罗永浩

15.“我们很有可能成为上千亿美元市值的企业,而我之前总是低调地用人民币去估它。”——罗永浩

16.“你就当这是哥的幻觉吧:我已经想清楚了,对这个世界来说,乔布斯的稀缺性在于他骨子里是一个文青,但奇迹般地有兴趣和能力去运营一个企业。这两种品质发生在同一个人身上的概率,应该比一个人被雷劈中三次的概率高不了多少。但刚好在他去世前后,在神秘古老的东方,有一个……你懂的”——罗永浩

17.“国内最好?我知道你没有恶意,但这确实小瞧我们了。”——罗永浩

18.“是,单从美观的角度,智能手机正面的工业设计能拼的已经不多了,接下来要拼的是操作系统的UI,这方面除了苹果,没有一家是及格的。单以我们简洁优美得一塌糊涂的UI,加上我的传播推广能力,就足以撑起一个畅销手机产品了(想想当年的刀锋和巧克力),何况我们在用户体验方面还有很多天才的创新…”——罗永浩

19.“等了一个多月[可怜],今天终于搞定了我们的CTO[呵呵],一个伟大的公司就要不可避免地诞生了[酷]。手机行业唯一的聪明人死后,不是我选择了这个行业,是命运选中了我。刚才在卫生间洗脸,发现额头上隐隐有一个“王”字…..好吧,我承认它的学名是抬头纹,但抬头纹里为什么有一个竖呢?[吃惊] 待续…..”——罗永浩

20.“今天大家没太多事儿,还不到半夜十二点,就早早下班了。我一个人看着平庸的空办公室(租下来后没顾上重新装修),随手拍了张照片……后代的人们看到这张照片可能很难相信,一个改变世界的传奇科技公司竟然不是诞生在一个屌屌的车库,而是在这么一个除了异常整洁,哪儿都平凡的不能再平凡的写字间里。”——罗永浩

21.“有百分之五十就该去创业…何况我这次是百分之百。”——罗永浩

22.【最开心的是,在这个妖怪国家,我做营销都不用使什么手段,只要保持本色,猛说实话,就足以让无数的二逼们围着破口大骂、暴跳如雷、疯狂转发还附送犀利评论:“我倒要好好看看罗永浩你这个傻逼能弄出什么样的产品来!”】——罗永浩

23.“即便我们一时不能全面超过iPhone,也会让玩腻了iPhone又受不了其他手机(几乎是一片烂货的汪洋大海)的用户高高兴兴买单。”——罗永浩

24.【我和闯爷坐进车里,我劝他系上安全带,“你能想象咱俩要是出了车祸,还没系安全带,人类的智能手机事业会被耽误成什么样吗?” 闯爷认真想了想,“至少几十年吧。” “不知道”,我说,“反正我是不太敢细想。”】——罗永浩

25.“安卓这个烂摊子,只能靠我们做出些品质了。”——罗永浩

26.“才一个多小时就转发一万多了,心里突然有点慌:不知道这些人看了真东西有多好之后会疯狂成什么样子…” ——罗永浩

27.“我们初期产能不足的话,索性预收全款排队发货,这样资金周转就不是问题,你们也不用网上抢,敢预付全款先买到的人都是信任我们的,所以初期口碑一定更好,交钱后反悔的及时退款就是了……初步设想是这样。”——罗永浩

28.“大家都是做手机的,有些天快亮了,有些太阳已经下山了,还有些奄奄一息反正也是没救了的,给它们一锤子,也算死得安乐了。”——罗永浩

29.“我们不卖低端机,盯也盯的是中产阶级的钱包。”——罗永浩

30.“我认为智能手机这个领域里全是笨蛋(乔去世后),我的胜算简直不能更大了。”——罗永浩

31.“发现国产手机厂商现在都喜欢打电梯广告,这半年在电梯里陆续看了小米、魅族、oppo 和 nubia 的平面广告,回想起来,好像全都一个样,没有任何特色…… 大家明明是分头做的,但最后看起来都差不多,这也挺不容易的。对了,小米广告的“屌爆了”还是有点特色的:傻。”——罗永浩

32.“锤子ROM的论坛弄好了,今天晚些时候开放,我看了看其中的一条版规:“欢迎批评一切手机厂商或ROM团队,包括锤子科技,但禁止诽谤中伤任何厂商或团队。另外,盲目支持我们并胡乱辱骂我们竞争对手的用户将被管理员警告,屡劝不改的用户将被永久禁言。” 想起鬼气森森、妖孽横行的魅族论坛,我伤感了…” ——罗永浩

33.“还有很长的路要走,但肯定已经是很耐看的了”,我回短信说,“我们一定会接过乔老的大旗,用伟大的产品改善人类的生活品质的!” 有那么一会儿,我的眼眶有点红了… 我…我就调整了一下呼吸”——罗永浩

34.“Ios7按现在的路数走下去,很快就成乡村机了。”——罗永浩

35.“因为我们在做工方面的苛刻要求,目前产线上的良品率较低,按照我们和富士康的工程师的估计,未来一两周内可以上升至健康合理的正式量产水平。其实,现在我们产线上不能通过质检的大多数产品,做工也要比绝大多数手机厂商的产品好很多。目前为止,我们送测的工程机在做工方面几乎得到了所有媒体的一致好评,但和工程机一样水准的做工,在我们的产线上仍然是不良品。下周我们会从这些未通过质检的产品中拿出30台在新浪微博上做抽奖活动(仅限抽奖活动开始前已下单的客户参加),让大家看看做到了什么样的精细程度后,在我们的质检标准上仍然是不合格的。我们只要放松一点点品控标准,就会带来产能上的明显提升,但相信这不是你们希望的结果…即便这真是你们希望的结果,我们也不会这么做。”——罗永浩

36.“我们的rom完成度还很低,还有段路要走……但走到的那天,你们会知道什么叫洗牌。”——罗永浩

37.“刚才看了一下安智团队给锤子rom适配小米2的屏幕效果,虽然驱动有问题导致画面动起来的时候会瞬间破碎,但……孙子骗你…小米2装了锤子rom,完全是屌丝瞬间化身国际高富帅(且已富了至少三代)的效果。”——罗永浩

38.“我们一开始就计划做好几款手机就杀出国门,现在小米又在前面探路国际化,我们简直不能更高兴了。探路需要付出大量时间精力,我们把它省下来用在产品上感觉很幸运。另外,我们的目标用户跟小米重合度不高,如无意外,将来手机产业格局多半是小米通杀低端,锤子通杀高端,其他厂商…唉呀,真的不敢想”——罗永浩

39.“尊重别人的劳动成果”这个二x理论最初是从哪里来的?尊重别人的劳动态度、精神、追求等等可以理解,劳动成果为什么一定要尊重?劳动成果好就是好,翔就是翔,不能因为你消化得异常努力,消化得格外孜孜,就要尊重你吃了牛肉干之后艰辛排出的干燥翔。”——罗永浩

40.“我死也不会说的,我现在是成熟的企业家,我们这个二逼圈子,成熟的标志是不说实话。”——罗永浩

41.“如果我们上市后我说话少了,一定是因为忙,而不是为了稳定股价。”——罗永浩

42.“今天听到业界大佬说,老罗他们做得再差,也能做到一到两个魅族那么大。我觉得这跟骂人差不多,一些同事却感到很欣慰……”——罗永浩

43.“所有的行业都是被楞头楞脑杀进来的“外行”颠覆的,“内行”满脑子都是维持现状。”——罗永浩

44.“锤子明明还在路上,为什么你们总是把我和这些已经功成名就的科技大佬们相提并论?你们这样会害了一个年轻的企业家的!”——罗永浩

45.“我埋头做产品的时候和埋头吹牛逼的时候全都是真诚无比的。”——罗永浩

46.“我会努力的,把锤子做好了将来收购不可避免地走向衰落的苹果并复兴它是我余生义不容辞的责任。而且我也意识到,只有这样,才能让提姆库克和强纳肾爱抚明白,谁才是从精神和方法论上都真正继承了乔布斯衣钵的唯一传人。”——罗永浩

47.“从工业设计的角度,锤子黑色版可能是史上最接近完美的黑色版智能手机。”——罗永浩

48.“在公司楼下坐着晒了会儿太阳,想象了一下退休后的生活,好像还挺让人向往的,公司做到上千亿市值,我们的产品成了街机,我就可能退休了。”——罗永浩

49.“还剩53天了,每天数着日子过,看着手里一代代更新的工程机日趋完善,百感交集,真希望一觉醒来就是发布会那一天了。大卖和赚钱,从一开始就是没什么悬念的事,不急,我只是希望尽早让那些顶着傻逼们的冷嘲热讽坚持信任我们的人知道…我不会让你们失望的。怀抱着2014年年度最佳手机…屌屌地睡了…晚安。”——罗永浩

50.“除非山穷水尽,否则我们不会考虑拿互联网大佬的投资,其中的一家我们嫌丢人,另外两家迟早血雨腥风地打起来,我们不想过早站队…何况如果我们发展的顺利,将来谁站谁的队还不好说”——罗永浩

51.“最重要的还是最终的产品,产品做得好,前面的争议都是营销传奇;产品砸了,前面我温良恭俭让也只是温良恭俭让的傻逼。”——罗永浩

52.“我们眼里的对手只有苹果,其他的就算有些小亮点,也不足以成为我们的……你懂的。”——罗永浩

53.【这么客观、靠谱、专业、好看的评测,在魅族的脑残粉看来,也是“王自如竟敢黑我大魅族!”】-——罗永浩

54.”我们不搞宏大叙事,只让用户高兴得眼泪鼻涕横流,大小便失禁。”——罗永浩

55.”我是一步一个脚印的那种踏实人,不会吹离谱的牛”——罗永浩

56.“从来没有一个如此美好的产品和品牌,遭遇到过如此大规模的误解、污蔑和诽谤。”——罗永浩

59.”下次不可以对友司用恶意引号、挤眼、做鬼脸,你毕竟是官方微博……虽然他们确实挺傻的。” ——罗永浩

60.”我渐渐想明白雷军作为新兴手机企业老大最近为什么这么失态了:别看国内这么多手机企业,全都是做不出差异化产品的,巨头们赚的都是苦逼钱,没多少利润。目前国内能做出差异化的只有小米和魅族,但魅族不懂营销,软件二流,硬件虽好,整个体验远不如小米。所以我们出道前雷军就没紧张过谁……你懂的。”——罗永浩

61.”谬赞了,做工要超过 iPhone 还需要一点点时间,但“目前拆过用料最良心的一部手机”基本上应该是靠谱的。”——罗永浩

62.”体面的企业不会用“x999”的方式拿消费者当傻x,3000就痛痛快快3000,2999太猥琐了。” ——罗永浩

63.”并且作为一个体面的企业家,克制地隐藏了脏字” ——罗永浩

64.”不会,我找来的三个国内UI设计师,都认为自己可以灭掉苹果,只是需要一点点时间,比如半年。”——罗永浩

65.”到现在为止,苹果、三星、LG、索尼、HTC、摩托罗拉、诺基亚以及“中华酷联海”中的中兴、华为、联想,还有小米,都已经来信索取门票要求参加锤子ROM(”Smartisan OS”)发布会了。看来酷派、海信、魅族这几个无知无畏的厂商还不知道等着他们的悲惨命运是什么”——罗永浩

66.”哦,是吗?魅族真的很烂!三星、索尼、诺基亚、LG、摩托罗拉、HTC有些硬件还不错,软件一律很烂。只有苹果值得我们学习,但超越它也只是迟早的事。”——罗永浩

67.”三星、诺基亚、摩托罗拉都不是我眼里的对手,何况其中还有一个已变卖,一个快倒闭了。如果乔布斯还活着,我不敢说很有把握,但乔布斯死了之后,赶超苹果也只是迟早的事。希望我们崛起前苹果不要走下坡路,免得赢了也没什么意思。”——罗永浩

68:“说实话,看着一群做电饭锅出身的家电厂商,不会做用户体验,只能笨笨地拼硬件……在这样的市场,我们稍一发力,就鹤立鸡群了。” ——罗永浩

69.“试玩儿的这些手机中,个别产品的工业设计本来已经做到大概80分了(如果把苹果的Iphone 4作为99分的标准),但总有一个非常二逼的细节把整个分数大幅度拉低,很可能是这些企业并不缺优秀的工业设计人员,但决策者的愚蠢导致了这个结果……”——罗永浩

70.“苹果和三星瓜分了全球智能手机市场份额的百分之九十多,剩下一群大傻

屄,号称电子巨头在那抢饼干渣,你抢的饼干渣脸嘴角都没抹,怎么好好意思说

别人是业外人士呢?在我眼里,他们甚至都不是我对手,就一臭傻屄,然后你得

意洋洋地说你是业内我是业外,业你妈屄啊业”

71.“MX采用了前黑后白的设计,看起来特别业余,想那些二逼产品允许用户“随心换彩壳”后的恶劣结果。”——罗永浩

72.”水粉色系就是臭土鳖喜欢的颜色,你什么时候见过有文化的人喜欢粉色?”——罗永浩

73.“香槟金”就是为了抚慰土豪缺乏安全感的心灵诞生的名词,喜欢穿粉衬衫的ross不得不给粉色起个名字叫做“褪色的三文鱼色”。

75.”不奇怪,穷学生一直都是小米的客户群嘛” —罗永浩

  1. 锤子科技在2015年夏季发布会上宣布的smartisan公益基金,至今未成立,现在依然是Coming soon状态 网址:Coming soon…坚果发布会筹集的100万元门票收入也神秘失踪。锤子科技经营范围并不包含营业性演出,请问这100万元去哪儿了?一个公众人物在公开场合做出的捐款承诺是不是可以随便食言?

RWBY才是真正的女权主义影片

突然想到 RWBY 可以给现在盛行的田园女权们好好上一课,什么才叫他妈的女权!

随便上几组人物对比

Salem vs Ozpin 被爆cèi了几万年的负心男,如今更是当起了鸵鸟连人都见不到了。

Yang妈 vs 叔叔 呵呵还用我多说吗?一个整日借酒度日,一个带领族人顽强生存。

Adam vs Blake 这个乍看还好,Adam虽然是反派也算有种有型。对比在于失败后的表现,Blake重新站起直面敌人。Adam恼羞成怒,彻底癫狂差了个层次。

最后

P姐 和 小强 尼玛,制作组,还我P姐!

通过上面几组的对比,请问他们之间的差距在哪?精神意志的强弱,无它。

同理在其他各种不同的领域中的竞争决定胜败的往往都是意志力的强弱。

放在现实人类社会中也有很多有价值的例子。一个顶级强大的女性和顶级强大的男性几乎没有区别。同样无视世俗的眼光成就自我。中等强大的女性是最受伤的一个群体他们往往和身边的男性一样强或更强,却受限于自身所在的群体弱而收到不公平的待遇。弱的女性和弱的男性区别同样不大。

可惜实际情况是中等强大的女性数量太少。但是自然界不管这些,群体的弱同样是弱。

要想地位就要强大,枪杆子里面出政权,革命不是请客吃饭。自由有价。说的都是一个道理。RWBY这部作品虽然无意,但是却向我们展示了一个女性更强一点的世界会是什么样子——比男人打架好看多了!

以上纯属瞎侃,请勿严肃和我讨论女权问题,谢谢!

2019房价分析,未来房价预测

前言

18年底写过一篇《朋友该卖房了》,没有主动发出来和大家分享。主要是写完之后觉得没有干货,没有调查研究,基本都是以自身感觉为基础进行的推理,说服力不强。虽然这个基础也是来源于我的阅读。

现在来看去年年底我的预测没有错,今年的房价走势基本是下降趋势。文中也有提到一线城市和二线黄金地段不在讨论范围,事实也基本如此。只是预期中的大降价没有到来,这也是我当时没有发出来的原因,感觉对19年发生大降价的可能性没有太大的把握。

既然一年又过去了,我觉得有必要来一波真正的干货真正的系统性分析了。不仅是写给朋友们看,当然也是因为自己也身处其中,可以说房价和我们每个人都息息相关。

大纲

  1. 全国性分析

    1.1 货币
    1.2 居民收入
    1.3 出生/适龄人口
    1.4 GDP总量比值
    1.5 全国性总结

  2. 地区性分析-南京

    2.1 居民收入、GDP增速
    2.2 流入人口
    2.3 城市潜力分析
    2.4 房价租售比

货币

首先针对这张图真实性表示怀疑。一般性的认知,RMB发行量和对外贸易盈余有很大关系。

请看这张图,最近五年的贸易盈余。18年19年有明显的下降趋势,但是广义货币(M2)的增速曲线却几乎是完全平缓的。如果说M2曲线也是完全真实的,那我不得不感叹原来猪肉也能当蓄水池。开玩笑,消费品不具备金融属性,这显然是不可能的。

我相信中央不可能在贸易盈余下降的情况下还搞量化宽松,完全没有可能。而且现实情况是银行已经对地产行业收紧了贷款。我有理由相信M2的增速不可能完全由上图所示,增速较往年应当要缓一些。接下来的分析以这个推论为基准。

PS:这里补充一个M2增速没有降低的可能性,就是RMB的国际化进程。在对外投资过程中产生了大量广义货币。但是这部分新产生的货币回流速度肯定很缓甚至没有回流,对国内的房价不会有直接影响,这里不考虑这部分因素。

居民收入

上图为最近几年城乡居民家庭可支配收入的中位数,简单算了一下增长率目前还可以跑赢通胀,但是较早几年有明显下降。

收入是支撑房价的基石,而且由于最近几年的反腐工作比较有成效。体制内及相关人员的灰色收入也是有很大降幅的,但是这个在表上肯定没有体现。

所以随着总体收入增速的下降,至少支撑房价高速增长的基本条件已经不存在了。

其他关于收入的数据:

1978-2015中国历年GDP与城镇居民人均可支配收入统计表

2018年居民收入和消费支出情况

2018年全国居民人均可支配收入28228元

人口

最好理解的一张图,从1990开始新生人口不说是断崖式下跌情况也基本差不多了。新生儿大潮的最后一波同学明年也将迈入30的门槛,今年不买房明年也该买了,剩下的必然就是掏空6个钱包也买不起的了。

至于90年之后的情况相信大家也都了解了,放开二胎的时候小抬头了一下,之后就是一路下降,估计下面就是国家开放三四五胎生育率也上不来了。

也就是说新生人口对房价的推动力,今年不是最后一年那就是明年了。

宏观数据

450万亿!中国房产市值超美欧日总和。但是我尝试查了下数据来源,不是来源自任何机构,而是两个名人有提到这个数字一位是潘石屹,一位是经济学家向松祚

姑且用这个数字列个表格看下GDP和房地产总值的比值

就算这个数字有水分,也能看出中国房地产的泡沫不是一点两点,同时我看到2016年的数据是GDP 10.68房地产 43 比值 4.02,也就是说经过2016年底的一波大涨比值是继续扩大的趋势。本来GDP的增长就已经进入减速区间,比值还在继续扩大充分说明2016年底的大涨完全是透支未来的投机行为。

下面还有两幅图,可以进一步说明我国经济结构目前的畸形情况。

全国性总结

我们已经从三个维度分析了房价,不论是收入、人口、还是经济总量,都绝对没有支持房价继续高速增长的理由。人口和经济总量甚至都不足以支持房价继续增长了。至于收入和房价的比值只有放在单个城市看才有意义,我们放到下一阶段讨论。

至于城市化,有两个先决条件,一是年轻人口的净流入,二是能在城市安家才叫城市化。人口已经分析过90年之后的人口增长大幅下降,可见的未来不会再有大批的年轻人进城的情况发生。你也不可能指望农村40-50岁这个年龄段的突然进城打拼,他们当中可能很大一部分已经拼过又回去了。再上当的可能性不大,身体也不支持了。

二是在城市安家,现在每年大专本科毕业生800w,其中有多少能留在城市,大家心中自有秤,我觉得不必多说。而且就算这800w都能在城市安家,中国目前地级市293个,你算算一个城市能分到多少。其中的绝大部分当然会被一二线瓜分,所以后面还得单独分析。

其他迹象,万达全面转型,万科高喊活下去,碧桂园大面积新房出现质量问题。造成这些的原因就是国家银根对房地产的缩紧。这说明中央已经承认泡沫的存在,收紧银根就是为了预防系统性风险(看新闻联播的应该经常听到这个词),防止房地产倒塌时带着银行一起走。

总结,全国范围看房产价格停止上涨是必然,大概率在未来很长时间内横盘或下降,中小概率全面崩盘。横盘、跌、崩盘这几个选项中到底会落在哪个取决于以下几点,新的技术革命,美国发生金融危机或政治危机,一带一路跨越式进展。如果你认为以上几个事件在未来五年有一个以上必然发生,那么我建议你有钱继续买房。

但是不管手上有房无房,对我们普通老百姓来说最好的结果就是横盘或小幅阴跌以时间换空间,等待经济发展挤掉泡沫。否则兴百姓苦,亡百姓苦,稳定压倒一切。

南京地区分析

居民收入

年份 收入 增长率
2018 59308 8.75%
2017 54538 9.08%
2016 49997 8.44%
2015 46104 8.31%
2014 42568

2018 南京的 GDP 增速是 9.43%,从过往几年来看 GDP 和收入的增速差距越来越小,不知道是好事坏事。

再看一下房价的走势

年份 均价 增长率
2018 26359 36.98%
2017 19243 23.49%
2016 15583 12.02%
2015 13911 8.81%
2014 12785 15.89%
2013 11032 -4.42%
2012 11542 4.76%
2011 11018 49.07%
2010 7391 16.38%
2009 6351 14.19%
2008 5562

计算最近10年的平均增长率17.7%远高于 GDP 和居民收入增长率。显然这种状况不可能一直持续下去。

数据来源-政府网站

流入人口

我个人观点在其他国家流入人口不应成为推高房价的因素,城市扩大之后增长的人口应该消费的也是增长部分的住房。顶多推高核心区域的房价,因为城市扩大之后核心会变得更核心。

但是我国有几个特殊情况,首先流入的人口携带的不仅是自身的购买力还可能是其家庭的6个钱包的购买力。其二,重点城市的房价不仅有居住属性,还同时具备教育、医疗等其他公共服务价值属性。同时这些附加值导致我国基本不会出现老年人离开其原城市居住地到其他地区养老的情况,进一步减少的人口流出情况。其三,我国没有房产税,城市公共设施建设及维护的费用全部来自新房的土地拍卖出让金。等于是新房购买者同时负担了整个城市旧房及其附属公共设施的维护费用。随着城市的扩大公共设施建设维护费用的增加必然转嫁给新房房价。(PS:只要明白这个第三点,就会知道房产税早晚都要收,深圳的土地出让到期之后的解决先例等于说明政府已经变相承认土地的永久使用权,等于解决了法理问题。如果不收房产税,使用权就真变成所有权了。这里有个逻辑矛盾点,但是是必要矛盾,以后有空再详细讨论。)

好下面来看看南京的人口流入数据。

一年4-5万人的增长最近两年由于政策优化户籍人口增长加快了,但是我从其他渠道有看到外地在宁人口有下降,等于是转化了部分原来的外籍人口取得了户籍人口的增长。

总体来说南京前十年都没有什么特别出彩的产业升级或爆发。二线的工资一线的房价导致对人才新引力的匮乏,最近两年因为一线城市的生存压力实在太大,对人才有了一定的吸引力,可以看到阿里到南京搞个分部也是看到了南京的人才红利。

但是随着一带一路中部大开发的进行远期来看人才吸引力完全不及中部,举个例子重庆工资标准和南京差不多,房价却要低很多。(PS:妹子都更漂亮!)

未来发展潜力

一个城市的未来发展潜力和诸多因素相关,人才方面上节已经分析由于大环境的原因我并不看好,和中部城市竞争第二梯队的人才能力不足。好在江苏安徽地区的本土人才数量较多,这部分中部城市应当抢不走。

地理位置,南京的地理位置是经过历史证明的,不管是古代还是近代都是极为重要的。反倒是现在两省省会的定位到没什么出彩的地方。学府还在人文气息也有但总感觉少了点什么,你懂的。总之占着宝地未来发展总不会太差。但是最终究竟是千万,一千五,还是两千就是个值得讨论的问题了。个人认为两省之地一千五百万是绝对的上线,实现这个目标没个二三十年不太可能,近十年能上一千万就是很不错的成绩了。

另外近些年合肥的发展也挺好,中科大是个奇迹般的学校。往长远来说如果合肥能发展好,一千二三百万可能就是南京的极限了。

国家政策。说实话除了名字里的京字,以及东部战区指挥部驻地,我就没感觉到南京在其他方面有什么政治优待。再加上几个不咋地的市长,就两字尴尬。现如今,拿的出手的大企业屈指可数,房地产被抹了个光头,互联网具备人才优势,却只能替他人做嫁衣,本土龙头苏氏除了996啥都没学会,什么雨润中央同样是有样学样可能具备中国企业之最强法务团队(误,现在应该是某为了),悲剧。最近好歹混了个国家级开发新区,但也仅仅就是十几个城市中的一个。

房价租售比

两张图说明一切问题

恭喜台湾同胞喜提11名,千万不要认为南京等二线城市不在榜就是好事,只是没数据而已,不然就是屠榜了。顺便一提第一名是刚刚崩溃的委内瑞拉。第二名现在什么情况不用我详细介绍了吧。

还有另一组数据叫做可负担比率,国内几大都市同样也是名列前茅。太刺激了我就不贴了。有兴趣的自己上下面的网站去看。

numbeo-此网站可查询全球各大城市的收入与房价比值

另外租售比这个数据其实很好算,各位家门口的房子总价多少,每年租金多少,一除就出来了。大家可以自己算算这个数字是多少,如果超过30,世界前十的水平的。南京这个数字是多少,各位心里自然有数。

之所以把这组数据放在最后谈,就是想告诉大家这个数据才是真正的价值指标。你买房投资又不看这个,就像炒股票不谈市盈率一样,门都没进。整天扯什么K线,大盘,换手率,奉劝你趁早洗洗睡吧。韭菜,鉴定完毕!

我想这时候肯定有聪明的同学要质疑了,我要看这个前面20年房子都别买了。这里我请各位算个帐,用16年初的房价和现在的租金再算一遍你家门口房子的租售比。哎,是不是低多了?道理很简单,几年后租金能涨到补足这中间的差值,那就叫软着陆。其实这个情况过去20年间已经发生好多次了。就看你认为后面还会不会发生。

预测之前不要忘了想想现在的 GDP 增长率。

以上,没有总结,总结在 18 年已做,现在我把预测时间再加长一点未来五年,不是跌就是横。跌不跌跌多少取决于我党无敌的控制力,其实我还是蛮有信心可以稳住的 50% 吧。如果,万一,我是说万一,涨了(疯涨)。奉劝能跑的赶紧跑,肉翻的意思。

欢迎拍砖,尽量不要带脏。

airpods-pro使用体验

优点

不说了,相信各位已经听够了。

缺点

  1. 长时间佩戴耳朵疼,适应了一个星期情况有所减轻。之前使用红米的 airdots 情况都要比 airpods
    好的多。感觉原因还是尺寸大了,内部元器件实在太多,不得不在尺寸上做出妥协。PS: 我一天估计要
    戴7个小时甚至更长。

  2. 与我的 XPS15 连接不稳,经常出现断链,杂音等情况。一加7pro 没有问题,但是之前 PC 连红米也
    没有问题,不知道这个问题出在谁身上。

使用经验

  1. 耳塞一定要选择合适的不要选择偏小的。耳塞小了耳机嵌在耳朵中的体积更大,长时间佩戴更加不适。

  2. 使用强度高建议购买 appleCare+ 购买耳机之后 60 天内可以购买,从购买之日起增加两年的服务。
    买它的最重要的原因就是电池寿命。如果你在苹果官方维修更换电池两个耳机收费1150+,呵呵呵。
    苹果大法好。买了appleCare+之后电池寿命低于80%可以免费更换299¥可以延长一倍的寿命还是值得的。
    我在网上看了一下别人使用airpods 一二代的情况,高强度使用一年电池寿命可能就只剩50%了。

原生面经从初级到高级

原文链接

  1. 函数
    1.1函数的3种定义方法
    1.1.1 函数声明
    //ES5
    function getSum(){}
    function (){}//匿名函数
    //ES6
    ()=>{}//如果{}内容只有一行{}和return关键字可省,
    1.1.2 函数表达式(函数字面量)
    //ES5
    var sum=function(){}
    //ES6
    let sum=()=>{}//如果{}内容只有一行{}和return关键字可省,
    
    1.1.3 构造函数
    const sum = new Function('a', 'b' , 'return a + b')
    
    1.1.4 三种方法的对比
    1.函数声明有预解析,而且函数声明的优先级高于变量;
    2.使用Function构造函数定义函数的方式是一个函数表达式,这种方式会导致解析两次代码,影响性能。第一次解析常规的JavaScript代码,第二次解析传入构造函数的字符串

1.2.ES5中函数的4种调用
在ES5中函数内容的this指向和调用方法有关

1.2.1 函数调用模式
包括函数名()和匿名函数调用,this指向window

 function getSum() {
    console.log(this) //window
 }
 getSum()

 (function() {
    console.log(this) //window
 })()

 var getSum=function() {
    console.log(this) //window
 }
 getSum()

1.2.2 方法调用
对象.方法名(),this指向对象

var objList = {
   name: 'methods',
   getSum: function() {
     console.log(this) //objList对象
   }
}
objList.getSum()

1.2.3 构造器调用
new 构造函数名(),this指向构造函数

function Person() {
  console.log(this); //指向实例
}
var personOne = new Person();

1.2.4 间接调用
利用call和apply来实现,this就是call和apply对应的第一个参数,如果不传值或者第一个值为null,undefined时this指向window

function foo() {
   console.log(this);
}
foo.apply('我是apply改变的this值');//我是apply改变的this值
foo.call('我是call改变的this值');//我是call改变的this值

1.3 ES6中函数的调用
箭头函数不可以当作构造函数使用,也就是不能用new命令实例化一个对象,否则会抛出一个错误
箭头函数的this是和定义时有关和调用无关
调用就是函数调用模式

(() => {
   console.log(this)//window
})()

let arrowFun = () => {
  console.log(this)//window
}
arrowFun()

let arrowObj = {
  arrFun: function() {
   (() => {
     console.log(this)//指向函数arrFun
   })()
   }
 }
 arrowObj.arrFun();

1.4.call,apply和bind
1.IE5之前不支持call和apply,bind是ES5出来的;
2.call和apply可以调用函数,改变this,实现继承和借用别的对象的方法;

1.4.1 call和apply定义
调用方法,用一个对象替换掉另一个对象(this)
对象.call(新this对象,实参1,实参2,实参3…..)
对象.apply(新this对象,[实参1,实参2,实参3…..])

1.4.2 call和apply用法
1.间接调用函数,改变作用域的this值
2.劫持其他对象的方法

var foo = {
  name:"张三",
  logName:function(){
    console.log(this.name);
  }
}
var bar={
  name:"李四"
};
foo.logName.call(bar);//李四

实质是call改变了foo的this指向为bar,并调用该函数
3.两个函数实现继承

function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
function Cat(name){
Animal.call(this, name);
}
var cat = new Cat(“Black Cat”);
cat.showName(); //Black Cat
4.为类数组(arguments和nodeList)添加数组方法push,pop

(function(){
Array.prototype.push.call(arguments,’王五’);
console.log(arguments);//[‘张三’,’李四’,’王五’]
})(‘张三’,’李四’)
5.合并数组

let arr1=[1,2,3];
let arr2=[4,5,6];
Array.prototype.push.apply(arr1,arr2); //将arr2合并到了arr1中
6.求数组最大值

Math.max.apply(null,arr)
7.判断字符类型

Object.prototype.toString.call({})
1.4.3 bind
bind是function的一个函数扩展方法,bind以后代码重新绑定了func内部的this指向,不会调用方法,不兼容IE8

var name = ‘李四’
var foo = {
name: “张三”,
logName: function(age) {
console.log(this.name, age);
}
}
var fooNew = foo.logName;
var fooNewBind = foo.logName.bind(foo);
fooNew(10)//李四,10
fooNewBind(11)//张三,11 因为bind改变了fooNewBind里面的this指向
1.4.4 call,apply和bind原生实现
call实现:

Function.prototype.newCall = function(context, …parameter) {
if(context.instanceof Object) context={}
context.fn = this;
context.fn(…parameter);
delete context.fn;
}
let person = {
name: ‘Abiel’
}
function sayHi(age,sex) {
console.log(this.name, age, sex);
}
sayHi.newCall (person, 25, ‘男’); // Abiel 25 男
apply实现:

Function.prototype.newApply = function(context, parameter) {
if (typeof context === ‘object’) {
context = context || window
} else {
context = Object.create(null)
}
let fn = Symbol()
context[fn] = this
contextfn;
delete context[fn]
}
bind实现:

Function.prototype.bind = function (context,…innerArgs) {
var me = this
return function (…finnalyArgs) {
return me.call(context,…innerArgs,…finnalyArgs)
}
}
let person = {
name: ‘Abiel’
}
function sayHi(age,sex) {
console.log(this.name, age, sex);
}
let personSayHi = sayHi.bind(person, 25)
personSayHi(‘男’)
1.4.5 三者异同
同:都是改变this指向,都可接收参数
异:bind和call是接收单个参数,apply是接收数组

1.5.函数的节流和防抖
类型 概念 应用
节流 某个时间段内,只执行一次 scroll,resize事件一段时间触发一次
防抖 处理函数截止后一段时间依次执行 scroll,resize事件触发完后一段时间触发
节流:

1.5.1 节流
let throttle = function(func, delay) {
let timer = null;
return function() {
if (!timer) {
timer = setTimeout(()=> {
func.apply(this, arguments);
timer = null;
}, delay);
}
};
};
function handle() {
console.log(Math.random());
}
window.addEventListener(“scroll”, throttle(handle, 1000)); //事件处理函数
1.5.2 防抖
function debounce(fn, wait) {
let timeout = null;
return function() {
if (timeout !== null) clearTimeout(timeout);//如果多次触发将上次记录延迟清除掉
timeout = setTimeout(()=> {
fn.apply(this, arguments);
timeout = null;
}, wait);
};
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener(“scroll”, debounce(handle, 1000));
1.6.原型链
1.6.1 定义
对象继承属性的一个链条

1.6.2构造函数,实例与原型对象的关系
图片描述

var Person = function (name) { this.name = name; }//person是构造函数
var o3personTwo = new Person(‘personTwo’)//personTwo是实例
图片描述

原型对象都有一个默认的constructor属性指向构造函数

1.6.3 创建实例的方法
1.字面量

let obj={‘name’:’张三’}
2.Object构造函数创建

let Obj=new Object()
Obj.name=’张三’
3.使用工厂模式创建对象

function createPerson(name){
var o = new Object();
o.name = name;
};
return o;
}
var person1 = createPerson(‘张三’);
4.使用构造函数创建对象

function Person(name){
this.name = name;
}
var person1 = new Person(‘张三’);
1.6.4 new运算符
1.创了一个新对象;
2.this指向构造函数;
3.构造函数有返回,会替换new出来的对象,如果没有就是new出来的对象
4.手动封装一个new运算符

var new2 = function (func) {
var o = Object.create(func.prototype);    //创建对象
var k = func.call(o);             //改变this指向,把结果付给k
if (typeof k === ‘object’) {         //判断k的类型是不是对象
return k;                  //是,返回k
} else {
return o;                  //不是返回返回构造函数的执行结果
}
}
1.6.5 对象的原型链
图片描述

1.7 继承的方式
JS是一门弱类型动态语言,封装和继承是他的两大特性

1.7.1 原型链继承
将父类的实例作为子类的原型
1.代码实现
定义父类:

// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || ‘Animal’;
// 实例方法
this.sleep = function(){
console.log(this.name + ‘正在睡觉!’);
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + ‘正在吃:’ + food);
};
子类:

function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = ‘cat’;

// Test Code
var cat = new Cat();
console.log(cat.name);//cat
console.log(cat.eat(‘fish’));//cat正在吃:fish undefined
console.log(cat.sleep());//cat正在睡觉! undefined
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
2.优缺点
简单易于实现,但是要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,无法实现多继承

1.7.2 构造继承
实质是利用call来改变Cat中的this指向
1.代码实现
子类:

function Cat(name){
Animal.call(this);
this.name = name || ‘Tom’;
}
2.优缺点
可以实现多继承,不能继承原型属性/方法

1.7.3 实例继承
为父类实例添加新特性,作为子类实例返回
1.代码实现
子类

function Cat(name){
var instance = new Animal();
instance.name = name || ‘Tom’;
return instance;
}
2.优缺点
不限制调用方式,但不能实现多继承

1.7.4 拷贝继承
将父类的属性和方法拷贝一份到子类中
1.子类:

function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || ‘Tom’;
}
2.优缺点
支持多继承,但是效率低占用内存

1.7.5 组合继承
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
1.子类:

function Cat(name){
Animal.call(this);
this.name = name || ‘Tom’;
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
1.7.6 寄生组合继承
function Cat(name){
Animal.call(this);
this.name = name || ‘Tom’;
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
1.7.7 ES6的extends继承
ES6 的继承机制是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this,链接描述

//父类
class Person {
//constructor是构造方法
constructor(skin, language) {
this.skin = skin;
this.language = language;
}
say() {
console.log(‘我是父类’)
}
}

//子类
class Chinese extends Person {
constructor(skin, language, positon) {
//console.log(this);//报错
super(skin, language);
//super();相当于父类的构造函数
//console.log(this);调用super后得到了this,不报错,this指向子类,相当于调用了父类.prototype.constructor.call(this)
this.positon = positon;
}
aboutMe() {
console.log(${this.skin} ${this.language} ${this.positon});
}
}

//调用只能通过new的方法得到实例,再调用里面的方法
let obj = new Chinese(‘红色’, ‘中文’, ‘香港’);
obj.aboutMe();
obj.say();
1.8.高阶函数
1.8.1定义
函数的参数是函数或返回函数

1.8.2 常见的高阶函数
map,reduce,filter,sort

1.8.3 柯里化
1.定义:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数

fn(a,b,c,d)=>fn(a)(b)(c)(d)
2.代码实现:

const currying = fn => {
const len = fn.length
return function curr (…args1) {
if (args1.length >= len) {
return fn(…args1)
}
return (…args2) => curr(…args1, …args2)
}
}

1.8.4 反柯里化
1.定义:

obj.func(arg1, arg2)=>func(obj, arg1, arg2)
2.代码实现:

Function.prototype.uncurrying = function() {
var that = this;
return function() {
return Function.prototype.call.apply(that, arguments);
}
};

function sayHi () {
return “Hello “ + this.value +” “+[].slice.call(arguments);
}
let sayHiuncurrying=sayHi.uncurrying();
console.log(sayHiuncurrying({value:’world’},”hahaha”));
1.8.5偏函数
1.定义:指定部分参数来返回一个新的定制函数的形式
2.例子:

function foo(a, b, c) {
return a + b + c;
}
function func(a, b) {
return foo(a,b,8);
}
2.对象
2.1.对象的声明方法
2.1.1 字面量
var test2 = {x:123,y:345};
console.log(test2);//{x:123,y:345};
console.log(test2.x);//123
console.log(test2.proto.x);//undefined
console.log(test2.proto.x === test2.x);//false
2.1.2 构造函数
var test1 = new Object({x:123,y:345});
console.log(test1);//{x:123,y:345}
console.log(test1.x);//123
console.log(test1.proto.x);//undefined
console.log(test1.proto.x === test1.x);//false
new的作用:
1.创了一个新对象;
2.this指向构造函数;
3.构造函数有返回,会替换new出来的对象,如果没有就是new出来的对象

2.1.3 内置方法
Obejct.create(obj,descriptor),obj是对象,describe描述符属性(可选)

let test = Object.create({x:123,y:345});
console.log(test);//{}
console.log(test.x);//123
console.log(test.proto.x);//3
console.log(test.proto.x === test.x);//true
2.1.4 三种方法的优缺点
1.功能:都能实现对象的声明,并能够赋值和取值
2.继承性:内置方法创建的对象继承到proto属性上
3.隐藏属性:三种声明方法会默认为内部的每个成员(属性或方法)生成一些隐藏属性,这些隐藏属性是可以读取和可配置的,属性分类见下面
4.属性读取:Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptor()
5.属性设置:Object.definePropertype或Object.defineProperties

2.2.对象的属性
2.2.1 属性分类
1.数据属性4个特性:
configurable(可配置),enumerable(可枚举),writable(可修改),value(属性值)

2.访问器属性2个特性:
get(获取),set(设置)

3.内部属性
由JavaScript引擎内部使用的属性;
不能直接访问,但是可以通过对象内置方法间接访问,如:[[Prototype]]可以通过 Object.getPrototypeOf()访问;
内部属性用[[]]包围表示,是一个抽象操作,没有对应字符串类型的属性名,如[[Prototype]].

2.2.2 属性描述符
1.定义:将一个属性的所有特性编码成一个对象返回
2.描述符的属性有:数据属性和访问器属性
3.使用范围:
作为方法Object.defineProperty, Object.getOwnPropertyDescriptor, Object.create的第二个参数,

2.2.3 属性描述符的默认值
1.访问对象存在的属性

特性名 默认值
value 对应属性值
get 对应属性值
set undefined
writable true
enumerable true
configurable true
所以通过上面三种声明方法已存在的属性都是有这些默认描述符
2.访问对象不存在的属性

特性名 默认值
value undefined
get undefined
set undefined
writable false
enumerable false
configurable false
2.2.3 描述符属性的使用规则
get,set与wriable,value是互斥的,如果有交集设置会报错

2.2.4 属性定义
1.定义属性的函数有两个:Object.defineProperty和Object.defineProperties.例如:
Object.defineProperty(obj, propName, desc)

2.在引擎内部,会转换成这样的方法调用:
obj.[DefineOwnProperty]

2.2.5 属性赋值
1.赋值运算符(=)就是在调用[[Put]].比如:
obj.prop = v;

2.在引擎内部,会转换成这样的方法调用:
obj.[Put]

2.2.6 判断对象的属性
名称 含义 用法
in 如果指定的属性在指定的对象或其原型链中,则in 运算符返回true ‘name’ in test //true
hasOwnProperty() 只判断自身属性 test.hasOwnProperty(‘name’) //true
.或[] 对象或原型链上不存在该属性,则会返回undefined test.name //“lei” test[“name”] //“lei”
2.3.Symbol
2.3.1概念
是一种数据类型;
不能new,因为Symbol是一个原始类型的值,不是对象。

2.3.2 定义方法
Symbol(),可以传参

var s1 = Symbol();
var s2 = Symbol();
s1 === s2 // false

// 有参数的情况
var s1 = Symbol(“foo”);
var s2 = Symbol(“foo”);
s1 === s2 // false
2.3.3 用法
1.不能与其他类型的值进行运算;
2.作为属性名

let mySymbol = Symbol();

// 第一种写法
var a = {};
a[mySymbol] = ‘Hello!’;

// 第二种写法
var a = {
[mySymbol]: ‘Hello!’
};

// 第三种写法
var a = {};
Object.defineProperty(a, mySymbol, { value: ‘Hello!’ });

// 以上写法都得到同样结果
a[mySymbol] // “Hello!”
3.作为对象属性名时,不能用点运算符,可以用[]

let a = {};
let name = Symbol();
a.name = ‘lili’;
a[name] = ‘lucy’;
console.log(a.name,a[name]);
4.遍历不会被for…in、for…of和Object.keys()、Object.getOwnPropertyNames()取到该属性

2.3.4 Symbol.for
1.定义:在全局中搜索有没有以该参数作为名称的Symbol值,如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值
2.举例:

var s1 = Symbol.for(‘foo’);
var s2 = Symbol.for(‘foo’);
s1 === s2 // true
2.3.5 Symbol.keyFor
1.定义:返回一个已登记的Symbol类型值的key
2.举例:

var s1 = Symbol.for(“foo”);
Symbol.keyFor(s1) // “foo”

var s2 = Symbol(“foo”);
Symbol.keyFor(s2) // undefined
2.4.遍历
2.4.1 一级对象遍历方法
方法 特性
for … in 遍历对象自身的和继承的可枚举属性(不含Symbol属性)
Object.keys(obj) 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)
Object.getOwnPropertyNames(obj) 返回一个数组,包括对象自身的所有可枚举属性(不含Symbol属性)
Object.getOwnPropertySymbols(obj) 返回一个数组,包含对象自身的所有Symbol属性
Reflect.ownKeys(obj) 返回一个数组,包含对象自身的所有(不枚举、可枚举和Symbol)属性
Reflect.enumerate(obj) 返回一个Iterator对象,遍历对象自身的和继承的所有可枚举属性(不含Symbol属性)
总结:1.只有Object.getOwnPropertySymbols(obj)和Reflect.ownKeys(obj)可以拿到Symbol属性
2.只有Reflect.ownKeys(obj)可以拿到不可枚举属性

2.4.2 多级对象遍历
数据模型:

var treeNodes = [
{
id: 1,
name: ‘1’,
children: [
{
id: 11,
name: ‘11’,
children: [
{
id: 111,
name: ‘111’,
children:[]
},
{
id: 112,
name: ‘112’
}
]
},
{
id: 12,
name: ‘12’,
children: []
}
],
users: []
},
];
递归:

var parseTreeJson = function(treeNodes){
if (!treeNodes || !treeNodes.length) return;

   for (var i = 0, len = treeNodes.length; i < len; i++) {

        var childs = treeNodes[i].children;

        console.log(treeNodes[i].id);

        if(childs && childs.length > 0){
             parseTreeJson(childs);
        }
   }
};

console.log('------------- 递归实现 ------------------');
parseTreeJson(treeNodes);

2.5.深度拷贝
2.5.1 Object.assign
1.定义:将源对象(source)的所有可枚举属性,复制到目标对象(target)
2.用法:

合并多个对象
var target = { a: 1, b: 1 };
var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
3.注意:
这个是伪深度拷贝,只能拷贝第一层

2.5.2 JSON.stringify
1.原理:是将对象转化为字符串,而字符串是简单数据类型

2.5.3 递归拷贝
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){ // 遍历目标
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === ‘object’){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}

2.6.数据拦截
定义:利用对象内置方法,设置属性,进而改变对象的属性值

2.6.1 Object.defineProterty
1.ES5出来的方法;
2.三个参数:对象(必填),属性值(必填),描述符(可选);
3.defineProterty的描述符属性

数据属性:value,writable,configurable,enumerable
访问器属性:get,set
注:不能同时设置value和writable,这两对属性是互斥的
4.拦截对象的两种情况:

let obj = {name:’’,age:’’,sex:’’ },
defaultName = [“这是姓名默认值1”,”这是年龄默认值1”,”这是性别默认值1”];
Object.keys(obj).forEach(key => {
Object.defineProperty(obj, key, {
get() {
return defaultName;
},
set(value) {
defaultName = value;
}
});
});

console.log(obj.name);
console.log(obj.age);
console.log(obj.sex);
obj.name = “这是改变值1”;
console.log(obj.name);
console.log(obj.age);
console.log(obj.sex);

let objOne={},defaultNameOne=”这是默认值2”;
Object.defineProperty(obj, ‘name’, {
get() {
return defaultNameOne;
},
set(value) {
defaultNameOne = value;
}
});
console.log(objOne.name);
objOne.name = “这是改变值2”;
console.log(objOne.name);
5.拦截数组变化的情况

let a={};
bValue=1;
Object.defineProperty(a,”b”,{
set:function(value){
bValue=value;
console.log(“setted”);
},
get:function(){
return bValue;
}
});
a.b;//1
a.b=[];//setted
a.b=[1,2,3];//setted
a.b[1]=10;//无输出
a.b.push(4);//无输出
a.b.length=5;//无输出
a.b;//[1,10,3,4,undefined];

结论:defineProperty无法检测数组索引赋值,改变数组长度的变化;
但是通过数组方法来操作可以检测到

6.存在的问题

不能监听数组索引赋值和改变长度的变化
必须深层遍历嵌套的对象,因为defineProterty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历,显然能劫持一个完整的对象是更好的选择

2.6.2 proxy
1.ES6出来的方法,实质是对对象做了一个拦截,并提供了13个处理方法
13个方法详情请戳,阮一峰的proxy介绍

2.两个参数:对象和行为函数

let handler = {
get(target, key, receiver) {
console.log(“get”, key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(“set”, key, value);
return Reflect.set(target, key, value, receiver);
}
};
let proxy = new Proxy(obj, handler);
proxy.name = “李四”;
proxy.age = 24;
3.问题和优点
reflect对象没有构造函数
可以监听数组索引赋值,改变数组长度的变化,
是直接监听对象的变化,不用深层遍历

2.6.3 defineProterty和proxy的对比
1.defineProterty是es5的标准,proxy是es6的标准;

2.proxy可以监听到数组索引赋值,改变数组长度的变化;

3.proxy是监听对象,不用深层遍历,defineProterty是监听属性;

3.利用defineProterty实现双向数据绑定(vue2.x采用的核心)
请戳,剖析Vue原理&实现双向绑定MVVM
4.利用proxy实现双向数据绑定(vue3.x会采用)

3.数组
数组基本上考察数组方法多一点,所以这里就单纯介绍常见的场景数组的方法,还有很多场景后续补充;
本文主要从应用来讲数组api的一些骚操作;
如一行代码扁平化n维数组、数组去重、求数组最大值、数组求和、排序、对象和数组的转化等;
上面这些应用场景你可以用一行代码实现?

3.1 扁平化n维数组
1.终极篇

[1,[2,3]].flat(2) //[1,2,3]
[1,[2,3,[4,5]].flat(3) //[1,2,3,4,5]
[1,[2,3,[4,5]]].toString() //‘1,2,3,4,5’
[1[2,3,[4,5[…]].flat(Infinity) //[1,2,3,4…n]
Array.flat(n)是ES10扁平数组的api,n表示维度,n值为Infinity时维度为无限大

2.开始篇

function flatten(arr) {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(…arr);
}
return arr;
}
flatten([1,[2,3]]) //[1,2,3]
flatten([1,[2,3,[4,5]]) //[1,2,3,4,5]
实质是利用递归和数组合并方法concat实现扁平

3.2 去重
1.终极篇

Array.from(new Set([1,2,3,3,4,4])) //[1,2,3,4]
[…new Set([1,2,3,3,4,4])] //[1,2,3,4]
set是ES6新出来的一种一种定义不重复数组的数据类型
Array.from是将类数组转化为数组
…是扩展运算符,将set里面的值转化为字符串
2.开始篇

Array.prototype.distinct = nums => {
const map = {}
const result = []
for (const n of nums) {
if (!(n in map)) {
map[n] = 1
result.push(n)
}
}
return result
}
[1,2,3,3,4,4].distinct(); //[1,2,3,4]
取新数组存值,循环两个数组值相比较

3.3排序
1.终极篇

[1,2,3,4].sort((a, b) => a - b); // [1, 2,3,4],默认是升序
[1,2,3,4].sort((a, b) => b - a); // [4,3,2,1] 降序
sort是js内置的排序方法,参数为一个函数
2.开始篇
冒泡排序:

Array.prototype.bubleSort=function () {
let arr=this,
len = arr.length;
for (let outer = len; outer >= 2; outer–) {
for (let inner = 0; inner <= outer - 1; inner++) {
if (arr[inner] > arr[inner + 1]) {
//升序
[arr[inner], arr[inner + 1]] = [arr[inner + 1], arr[inner]];
console.log([arr[inner], arr[inner + 1]]);
}
}
}
return arr;
}
[1,2,3,4].bubleSort() //[1,2,3,4]
选择排序

Array.prototype.selectSort=function () {
    let arr=this,
        len = arr.length;
    for (let i = 0, len = arr.length; i < len; i++) {
for (let j = i, len = arr.length; j < len; j++) {
  if (arr[i] > arr[j]) {
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
}

}
return arr;
}
[1,2,3,4].selectSort() //[1,2,3,4]
3.4最大值
1.终极篇

Math.max(…[1,2,3,4]) //4
Math.max.apply(this,[1,2,3,4]) //4
[1,2,3,4].reduce( (prev, cur,curIndex,arr)=> {
return Math.max(prev,cur);
},0) //4
Math.max()是Math对象内置的方法,参数是字符串;
reduce是ES5的数组api,参数有函数和默认初始值;
函数有四个参数,pre(上一次的返回值),cur(当前值),curIndex(当前值索引),arr(当前数组)

2.开始篇
先排序再取值

3.5求和
1.终极篇

[1,2,3,4].arr.reduce(function (prev, cur) {
return prev + cur;
},0) //10
2.开始篇

function sum(arr) {
var len = arr.length;
if(len == 0){
return 0;
} else if (len == 1){
return arr[0];
} else {
return arr[0] + sum(arr.slice(1));
}
}
sum([1,2,3,4]) //10
利用slice截取改变数组,再利用递归求和

3.6合并
1.终极篇

[1,2,3,4].concat([5,6]) //[1,2,3,4,5,6]
[…[1,2,3,4],…[4,5]] //[1,2,3,4,5,6]
let arrA = [1, 2], arrB = [3, 4]
Array.prototype.push.apply(arrA, arrB))//arrA值为[1,2,3,4]
2.开始篇

let arr=[1,2,3,4];
[5,6].map(item=>{
arr.push(item)
})
//arr值为[1,2,3,4,5,6],注意不能直接return出来,return后只会返回[5,6]
3.7判断是否包含值
1.终极篇

[1,2,3].includes(4) //false
[1,2,3].indexOf(4) //-1 如果存在换回索引
[1, 2, 3].find((item)=>item===3)) //3 如果数组中无值返回undefined
[1, 2, 3].findIndex((item)=>item===3)) //2 如果数组中无值返回-1
includes(),find(),findIndex()是ES6的api

2.开始篇

[1,2,3].some(item=>{
return item===3
}) //true 如果不包含返回false
3.8类数组转化
1.终极篇

Array.prototype.slice.call(arguments) //arguments是类数组(伪数组)
Array.prototype.slice.apply(arguments)
Array.from(arguments)
[…arguments]
类数组:表示有length属性,但是不具备数组的方法
call,apply:是改变slice里面的this指向arguments,所以arguments也可调用数组的方法
Array.from是将类似数组或可迭代对象创建为数组
…是将类数组扩展为字符串,再定义为数组

2.开始篇

Array.prototype.slice = function(start,end){
var result = new Array();
start = start || 0;
end = end || this.length; //this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键
for(var i = start; i < end; i++){
result.push(this[i]);
}
return result;
}
3.9每一项设置值
1.终极篇

[1,2,3].fill(false) //[false,false,false]
fill是ES6的方法
2.开始篇

[1,2,3].map(() => 0)
3.10每一项是否满足
[1,2,3].every(item=>{return item>2}) //false
every是ES5的api,每一项满足返回 true

3.11有一项满足
[1,2,3].some(item=>{return item>2}) //true
some是ES5的api,有一项满足返回 true

3.12.过滤数组
[1,2,3].filter(item=>{return item>2}) //[3]
filter是ES5的api,返回满足添加的项的数组

3.13对象和数组转化
Object.keys({name:’张三’,age:14}) //[‘name’,’age’]
Object.values({name:’张三’,age:14}) //[‘张三’,14]
Object.entries({name:’张三’,age:14}) //[[name,’张三’],[age,14]]
Object.fromEntries([name,’张三’],[age,14]) //ES10的api,Chrome不支持 , firebox输出{name:’张三’,age:14}
3.14 对象数组
[{count:1},{count:2},{count:3}].reduce((p, e)=>p+(e.count), 0)
4.数据结构篇
数据结构是计算机存储、组织数据的方式,算法是系统描述解决问题的策略。了解基本的数据结构和算法可以提高代码的性能和质量。
也是程序猿进阶的一个重要技能。
手撸代码实现栈,队列,链表,字典,二叉树,动态规划和贪心算法
4.1 栈
栈的特点:先进后出

class Stack {
constructor() {
this.items = [];
}

// 入栈
push(element) {
  this.items.push(element);
}

// 出栈
pop() {
  return this.items.pop();
}

// 末位
get peek() {
  return this.items[this.items.length - 1];
}

// 是否为空栈
get isEmpty() {
  return !this.items.length;
}

// 长度
get size() {
  return this.items.length;
}

// 清空栈
clear() {
  this.items = [];
}

}

// 实例化一个栈
const stack = new Stack();
console.log(stack.isEmpty); // true

// 添加元素
stack.push(5);
stack.push(8);

// 读取属性再添加
console.log(stack.peek); // 8
stack.push(11);
console.log(stack.size); // 3
console.log(stack.isEmpty); // false
4.2 队列
队列:先进先出

class Queue {
constructor(items) {
this.items = items || [];
}

enqueue(element) {
  this.items.push(element);
}

dequeue() {
  return this.items.shift();
}

front() {
  return this.items[0];
}

clear() {
  this.items = [];
}

get size() {
  return this.items.length;
}

get isEmpty() {
  return !this.items.length;
}

print() {
  console.log(this.items.toString());
}

}

const queue = new Queue();
console.log(queue.isEmpty); // true

queue.enqueue(“John”);
queue.enqueue(“Jack”);
queue.enqueue(“Camila”);
console.log(queue.size); // 3
console.log(queue.isEmpty); // false
queue.dequeue();
queue.dequeue();

4.3 链表
链表:存贮有序元素的集合,
但是不同于数组,每个元素是一个存贮元素本身的节点和指向下一个元素引用组成
要想访问链表中间的元素,需要从起点开始遍历找到所需元素

class Node {
constructor(element) {
this.element = element;
this.next = null;
}
}

// 链表
class LinkedList {
constructor() {
this.head = null;
this.length = 0;
}

// 追加元素
append(element) {
  const node = new Node(element);
  let current = null;
  if (this.head === null) {
    this.head = node;
  } else {
    current = this.head;
    while (current.next) {
      current = current.next;
    }
    current.next = node;
  }
  this.length++;
}

// 任意位置插入元素
insert(position, element) {
  if (position >= 0 && position <= this.length) {
    const node = new Node(element);
    let current = this.head;
    let previous = null;
    let index = 0;
    if (position === 0) {
      this.head = node;
    } else {
      while (index++ < position) {
        previous = current;
        current = current.next;
      }
      node.next = current;
      previous.next = node;
    }
    this.length++;
    return true;
  }
  return false;
}

// 移除指定位置元素
removeAt(position) {
  // 检查越界值
  if (position > -1 && position < length) {
    let current = this.head;
    let previous = null;
    let index = 0;
    if (position === 0) {
      this.head = current.next;
    } else {
      while (index++ < position) {
        previous = current;
        current = current.next;
      }
      previous.next = current.next;
    }
    this.length--;
    return current.element;
  }
  return null;
}

// 寻找元素下标
findIndex(element) {
  let current = this.head;
  let index = -1;
  while (current) {
    if (element === current.element) {
      return index + 1;
    }
    index++;
    current = current.next;
  }
  return -1;
}

// 删除指定文档
remove(element) {
  const index = this.findIndex(element);
  return this.removeAt(index);
}

isEmpty() {
  return !this.length;
}

size() {
  return this.length;
}

// 转为字符串
toString() {
  let current = this.head;
  let string = "";
  while (current) {
    string += ` ${current.element}`;
    current = current.next;
  }
  return string;
}

}
const linkedList = new LinkedList();

console.log(linkedList);
linkedList.append(2);
linkedList.append(6);
linkedList.append(24);
linkedList.append(152);

linkedList.insert(3, 18);
console.log(linkedList);
console.log(linkedList.findIndex(24));

4.4 字典
字典:类似对象,以key,value存贮值

class Dictionary {
constructor() {
this.items = {};
}

set(key, value) {
  this.items[key] = value;
}

get(key) {
  return this.items[key];
}

remove(key) {
  delete this.items[key];
}

get keys() {
  return Object.keys(this.items);
}

get values() {
  /*
也可以使用ES7中的values方法
return Object.values(this.items)
*/

  // 在这里我们通过循环生成一个数组并输出
  return Object.keys(this.items).reduce((r, c, i) => {
    r.push(this.items[c]);
    return r;
  }, []);
}

}
const dictionary = new Dictionary();
dictionary.set(“Gandalf”, “gandalf@email.com“);
dictionary.set(“John”, “johnsnow@email.com“);
dictionary.set(“Tyrion”, “tyrion@email.com“);

console.log(dictionary);
console.log(dictionary.keys);
console.log(dictionary.values);
console.log(dictionary.items);

4.5 二叉树
特点:每个节点最多有两个子树的树结构

class NodeTree {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}

class BinarySearchTree {
constructor() {
this.root = null;
}

insert(key) {
  const newNode = new NodeTree(key);
  const insertNode = (node, newNode) => {
    if (newNode.key < node.key) {
      if (node.left === null) {
        node.left = newNode;
      } else {
        insertNode(node.left, newNode);
      }
    } else {
      if (node.right === null) {
        node.right = newNode;
      } else {
        insertNode(node.right, newNode);
      }
    }
  };
  if (!this.root) {
    this.root = newNode;
  } else {
    insertNode(this.root, newNode);
  }
}

//访问树节点的三种方式:中序,先序,后序
inOrderTraverse(callback) {
  const inOrderTraverseNode = (node, callback) => {
    if (node !== null) {
      inOrderTraverseNode(node.left, callback);
      callback(node.key);
      inOrderTraverseNode(node.right, callback);
    }
  };
  inOrderTraverseNode(this.root, callback);
}

min(node) {
  const minNode = node => {
    return node ? (node.left ? minNode(node.left) : node) : null;
  };
  return minNode(node || this.root);
}

max(node) {
  const maxNode = node => {
    return node ? (node.right ? maxNode(node.right) : node) : null;
  };
  return maxNode(node || this.root);
}

}
const tree = new BinarySearchTree();
tree.insert(11);
tree.insert(7);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.inOrderTraverse(value => {
console.log(value);
});

console.log(tree.min());
console.log(tree.max());

5.算法篇
5.1 冒泡算法
冒泡排序,选择排序,插入排序,此处不做赘述,请戳 排序

5.2 斐波那契
特点:第三项等于前面两项之和

function fibonacci(num) {
if (num === 1 || num === 2) {
return 1
}
return fibonacci(num - 1) + fibonacci(num - 2)
}
5.3 动态规划
特点:通过全局规划,将大问题分割成小问题来取最优解
案例:最少硬币找零
美国有以下面额(硬币):d1=1, d2=5, d3=10, d4=25
如果要找36美分的零钱,我们可以用1个25美分、1个10美分和1个便士( 1美分)

class MinCoinChange {

constructor(coins) {
this.coins = coins
this.cache = {}
}

makeChange(amount) {
if (!amount) return []
if (this.cache[amount]) return this.cache[amount]
let min = [], newMin, newAmount
this.coins.forEach(coin => {
newAmount = amount - coin
if (newAmount >= 0) {
newMin = this.makeChange(newAmount)
}
if (newAmount >= 0 &&
(newMin.length < min.length - 1 || !min.length) &&
(newMin.length || !newAmount)) {
min = [coin].concat(newMin)
}
})
return (this.cache[amount] = min)
}
}

const rninCoinChange = new MinCoinChange([1, 5, 10, 25])
console.log(rninCoinChange.makeChange(36))
// [1, 10, 25]
const minCoinChange2 = new MinCoinChange([1, 3, 4])
console.log(minCoinChange2.makeChange(6))
// [3, 3]
5.4 贪心算法
特点:通过最优解来解决问题
用贪心算法来解决2.3中的案例

class MinCoinChange2 {

constructor(coins) {
this.coins = coins
}

makeChange(amount) {
const change = []
let total = 0
this.coins.sort((a, b) => a < b).forEach(coin => {
if ((total + coin) <= amount) {
change.push(coin)
total += coin
}
})
return change
}
}
const rninCoinChange2 = new MinCoinChange2 ( [ 1, 5, 10, 25])
console.log (rninCoinChange2. makeChange (36))
6 设计模式
设计模式如果应用到项目中,可以实现代码的复用和解耦,提高代码质量。 本文主要介绍14种设计模式
写UI组件,封装框架必备
6.1 简单工厂模式
1.定义:又叫静态工厂方法,就是创建对象,并赋予属性和方法
2.应用:抽取类相同的属性和方法封装到对象上
3.代码:

let UserFactory = function (role) {

function User(opt) {
this.name = opt.name;
this.viewPage = opt.viewPage;
}
switch (role) {
case ‘superAdmin’:
return new User(superAdmin);
break;
case ‘admin’:
return new User(admin);
break;
case ‘user’:
return new User(user);
break;
default:
throw new Error(‘参数错误, 可选参数:superAdmin、admin、user’)
}
}

//调用
let superAdmin = UserFactory(‘superAdmin’);
let admin = UserFactory(‘admin’)
let normalUser = UserFactory(‘user’)
//最后得到角色,可以调用
6.2工厂方法模式
1.定义:对产品类的抽象使其创建业务主要负责用于创建多类产品的实例
2.应用:创建实例
3.代码:

var Factory=function(type,content){
if(this instanceof Factory){
var s=new thistype;
return s;
}else{
return new Factory(type,content);
}
}

//工厂原型中设置创建类型数据对象的属性
Factory.prototype={
Java:function(content){
console.log(‘Java值为’,content);
},
PHP:function(content){
console.log(‘PHP值为’,content);
},
Python:function(content){
console.log(‘Python值为’,content);
},
}

//测试用例
Factory(‘Python’,’我是Python’);
6.3原型模式
1.定义:设置函数的原型属性
2.应用:实现继承
3.代码:

function Animal (name) {
// 属性
this.name = name || ‘Animal’;
// 实例方法
this.sleep = function(){
console.log(this.name + ‘正在睡觉!’);
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + ‘正在吃:’ + food);
};

function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = ‘cat’;

// Test Code
var cat = new Cat();
console.log(cat.name);//cat
console.log(cat.eat(‘fish’));//cat正在吃:fish undefined
console.log(cat.sleep());//cat正在睡觉! undefined
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
6.4单例模式
1.定义:只允许被实例化依次的类
2.应用:提供一个命名空间
3.代码:

let singleCase = function(name){
this.name = name;
};
singleCase.prototype.getName = function(){
return this.name;
}
// 获取实例对象
let getInstance = (function() {
var instance = null;
return function(name) {
if(!instance) {//相当于一个一次性阀门,只能实例化一次
instance = new singleCase(name);
}
return instance;
}
})();
// 测试单体模式的实例,所以one===two
let one = getInstance(“one”);
let two = getInstance(“two”);
6.5外观模式
1.定义:为子系统中的一组接口提供一个一致的界面
2.应用:简化复杂接口
3.代码:
外观模式

6.6适配器模式
1.定义:将一个接口转换成客户端需要的接口而不需要去修改客户端代码,使得不兼容的代码可以一起工作
2.应用:适配函数参数
3.代码:
适配器模式

6.7装饰者模式
1.定义:不改变原对象的基础上,给对象添加属性或方法
2.代码

let decorator=function(input,fn){
//获取事件源
let input=document.getElementById(input);
//若事件源已经绑定事件
if(typeof input.onclick==’function’){
//缓存事件源原有的回调函数
let oldClickFn=input.onclick;
//为事件源定义新事件
input.onclick=function(){
//事件源原有回调函数
oldClickFn();
//执行事件源新增回调函数
fn();
}
}else{
//未绑定绑定
input.onclick=fn;
}
}

//测试用例
decorator(‘textInp’,function(){
console.log(‘文本框执行啦’);
})
decorator(‘btn’,function(){
console.log(‘按钮执行啦’);
})
6.8桥接模式
1.定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化
2.代码
桥接模式

6.9模块方法模式
1.定义:定义一个模板,供以后传不同参数调用
2.代码:
模块方法模式

6.10.观察者模式
1.作用:解决类与对象,对象与对象之间的耦合
2.代码:

let Observer=
(function(){
let _message={};
return {
//注册接口,
//1.作用:将订阅者注册的消息推入到消息队列
//2.参数:所以要传两个参数,消息类型和处理动作,
//3.消息不存在重新创建,存在将消息推入到执行方法

  regist:function(type,fn){
    //如果消息不存在,创建
    if(typeof _message[type]==='undefined'){
      _message[type]=[fn];
    }else{
      //将消息推入到消息的执行动作
      _message[type].push(fn);
    }
  },

  //发布信息接口
    //1.作用:观察这发布消息将所有订阅的消息一次执行
    //2.参数:消息类型和动作执行传递参数
    //3.消息类型参数必须校验
  fire:function(type,args){
    //如果消息没有注册,则返回
    if(!_message[type]) return;
      //定义消息信息
      var events={
        type:type, //消息类型
        args:args||{} //消息携带数据
      },
      i=0,
      len=_message[type].length;
      //遍历消息
      for(;i<len;i++){
        //依次执行注册消息
        _message[type][i].call(this,events);
      }
  },

  //移除信息接口
    //1.作用:将订阅者注销消息从消息队列清除
    //2.参数:消息类型和执行的动作
    //3.消息参数校验
  remove:function(type,fn){
    //如果消息动作队列存在
    if(_message[type] instanceof Array){
      //从最后一个消息动作序遍历
      var i=_message[type].length-1;
      for(;i>=0;i--){
        //如果存在该动作在消息队列中移除
        _message[type][i]===fn&&_message[type].splice(i,1);
      }
    }
  }
}

})()

//测试用例
//1.订阅消息
Observer.regist(‘test’,function(e){
console.log(e.type,e.args.msg);
})

//2.发布消息
Observer.fire(‘test’,{msg:’传递参数1’});
Observer.fire(‘test’,{msg:’传递参数2’});
Observer.fire(‘test’,{msg:’传递参数3’});
6.11状态模式
1.定义:一个对象状态改变会导致行为变化
2.作用:解决复杂的if判断
3.代码
状态模式

6.12策略模式
1.定义:定义了一系列家族算法,并对每一种算法单独封装起来,让算法之间可以相互替换,独立于使用算法的客户
2.代码
策略模式

6.13.访问模式
1.定义:通过继承封装一些该数据类型不具备的属性,
2.作用:让对象具备数组的操作方法
3.代码:
访问者模式

6.14中介者模式
1.定义:设置一个中间层,处理对象之间的交互
2.代码:
中介者模式

  1. HTTP
    1.1 什么是 HTTP
    HTTP 是一个连接客户端,网关和服务器的一个协议。

7.2 特点
支持客户/服务器模式:可以连接客户端和服务端;
简单快速:请求只需传送请求方法,路径和请求主体;
灵活:传输数据类型灵活;
无连接:请求结束立即断开;
无状态:无法记住上一次请求。

7.3 怎么解决无状态和无连接
无状态:HTTP 协议本身无法解决这个状态,只有通过 cookie 和 session 将状态做贮存,常见的场景是登录状态保持;

无连接:可以通过自身属性 Keep-Alive。

7.4 请求过程
HTTP(S) 请求地址 → DNS 解析 → 三次握手 → 发送请求 → 四次挥手

三次握手过程(图片来源 CSDN)
3 次握手.jpg

在这里插入图片描述

四次挥手过程(图片来源 CSDN)
image

在这里插入图片描述

7.5 HTTP 0.9~3.0 对比
7.5.1 HTTP 0.9
只允许客户端发送 GET 这一种请求;
且不支持请求头,协议只支持纯文本;
无状态性,每个访问独立处理,完成断开;
无状态码。

7.5.2 HTTP 1.0
有身份认证,三次握手;
请求与响应支持头域;
请求头内容;

属性名 含义
Accept 可接受的 MIME 类型
Accept-Encoding 数据可解码的格式
Accept-Language 可接受语言
Connection 值 keep-alive 是长连接
Host 主机和端口
Pragma 是否缓存,指定 no-cache 返回刷新
Referer 页面路由
If-Modified-Since 值为时间
响应头内容;

属性名 含义
Connection 值 keep-alive 是长连接
Content-Type 返回文档类型,常见的值有 text/plain,text/html,text/json
Date 消息发送的时间
Server 服务器名字
Last-Modified 值为时间,s 返回的最后修改时间
Expires 缓存过期时间,b 和 s 时间做对比
注意

expires 是响应头内容,返回一个固定的时间,缺陷是时间到了服务器要重新设置。
请求头中如果有 If-Modified-Since,服务器会将时间与 last-modified 对比,相同返回 304。
响应对象以一个响应状态行开始
响应对象不只限于超文本
支持 GET、HEAD、POST 方法
有状态码
支持长连接(但默认还是使用短连接)、缓存机制以及身份认证。

7.5.3 HTTP 1.1
请求头增加 Cache-Control

属性名 含义
Cache-Control 在1.1 引入的方法,指定请求和响应遵循的缓存机制,值有:public(b 和 s 都缓存),private(b 缓存),no-cache(不缓存),no-store(不缓存),max-age(缓存时间,s 为单位),min-fresh(最小更新时间),max-age=3600
If-None-Match 上次请求响应头返回的 etag 值响应头增加 Cache-Control,表示所有的缓存机制是否可以缓存及哪种类型 etag 返回的哈希值,第二次请求头携带去和服务器值对比
注意

Cache-Control 的 max-age 返回是缓存的相对时间
Cache-Control 优先级比 expires 高
缺点:不能第一时间拿到最新修改文件

7.5.4 HTTP 2.0
采用二进制格式传输
多路复用,其实就是将请求数据分成帧乱序发送到 TCP 中。TCP 只能有一个 steam,所以还是会阻塞
报头压缩
服务器推送主动向 B 端发送静态资源,避免往返延迟。

7.5.5 HTTP 3.0
1.是基于 QUIC 协议,基于 UDP
2.特点:
自定义连接机制:TCP 以 IP/端口标识,变化重新连接握手,UDP 是一 64 位 ID 标识,是无连接;
自定义重传机制:TCP 使用序号和应答传输,QUIC 是使用递增序号传输; 无阻塞的多路复用:同一条 QUIC 可以创建多个 steam。

7.5.6 HTTPS
1.https 是在 http 协议的基础上加了个 SSL;
2.主要包括:握手(凭证交换和验证)和记录协议(数据进行加密)。

7.5.7 缓存
1.按协议分:协议层缓存和非 http 协议缓存:
1.1协议层缓存:利用 http 协议头属性值设置;
1.2非协议层缓存:利用 meta 标签的 http-equiv 属性值 Expires,set-cookie。

2.按缓存分:强缓存和协商缓存:
2.1强缓存:利用 cache-control 和 expires 设置,直接返回一个过期时间,所以在缓存期间不请求,If-modify-since;
2.2协商缓存:响应头返回 etag 或 last-modified 的哈希值,第二次请求头 If-none-match 或 IF-modify-since 携带上次哈希值,一致则返回 304。

3.协商缓存对比: etag 优先级高于 last-modified;
4.etag 精度高,last-modified 精度是 s,1s 内 etag 修改多少次都会被记录; last-modified 性能好,etag 要得到 hash 值。

5.浏览器读取缓存流程:
会先判断强缓存;再判断协商缓存 etag(last-modified)是否存在;
存在利用属性 If-None-match(If-Modified-since)携带值;
请求服务器,服务器对比 etag(last-modified),生效返回 304。

F5 刷新会忽略强缓存不会忽略协商缓存,ctrl+f5 都失效

7.5.8 状态码
序列 详情
1XX(通知)
2XX(成功) 200(成功)、201(服务器创建)、202(服务器接收未处理)、203(非授权信息)、204(未返回内容)、205(重置内容)、206(部分内容)
3XX(重定向) 301(永久移动)、302(临时移动)、303(查看其他位置)、304(未修改)、305(使用代理)、307(临时重定向)
4XX(客户端错误) 400(错误请求)、401(未授权)、403(禁止)、404(未找到)、405(方法禁用)、406(不接受)、407(需要代理授权)
5XX(服务器错误) 500(服务器异常)、501(尚未实施)、502(错误网关)、503(服务不可用)、504(网关超时)、505(HTTP 版本不受支持)
7.5.9 浏览器请求分析
在这里插入图片描述

7.5.10 总结
协议

版本 内容
http0.9 只允许客户端发送 GET 这一种请求;且不支持请求头,协议只支持纯文本;无状态性,每个访问独立处理,完成断开;无状态码
http1.0 解决 0.9 的缺点,增加 If-modify-since(last-modify)和 expires 缓存属性
http1.x 增加 cache-control 和 If-none-match(etag)缓存属性
http2.0 采用二进制格式传输;多路复用;报头压缩;服务器推送
http3.0 采用 QUIC 协议,自定义连接机制;自定义重传机制;无阻塞的多路复用
缓存

类型 特性
强缓存 通过 If-modify-since(last-modify)、expires 和 cache-control 设置,属性值是时间,所以在时间内不用请求
协商缓存 通过 If-none-match(etag)设置,etag 属性是哈希值,所以要请求和服务器值对比
8.总结
这只是 JS 原生梳理,后续会再出 react,node,小程序相关的梳理;
原创码字不易,欢迎 star!

自制ant.design表格组件教程

前言

在实际项目中我们时常有可能需要Ip地址输入框,键值对输入框这样的复杂输入组件。但是ant design
官方并没有给出这样的组件,文档中也没有提到应该如何写出这样的组件。

我在研究了一番实现了该功能,在这里与大家分享一下,希望可以方便到后来人。

源码分享

对于不需要教程的同学可以直接查看源码:

教程-编写一个IP输入组件

第一步:完全受控组件与ant design表格组件联动

首先我们需要建立一个完全受控的输入组件,并且该组件可以嵌套进ant design的表格组件当中并正常
运行。

&#123;getFieldDecorator('ip')(
  <IpInput />
)&#125;

IpInput组件代码:

import React, &#123;Component&#125; from 'react';
import &#123;Input&#125; from "antd";

export default class IpInput extends Component &#123;
  constructor(props) &#123;
    super(props);

    this.state = &#123;
      ip: '',
    &#125;
  &#125;


  hdlInputChange = (e) => &#123;
    this.setState(&#123;ip: e.target.value&#125;)
    this.props.onChange(&#123;target: &#123;value: e.target.value&#125;&#125;)
  &#125;

  render() &#123;
    return (
      <div>
        <Input value=&#123;this.state.ip&#125; onChange=&#123;this.hdlInputChange&#125; />
      </div>
    );
  &#125;
&#125;

在CodeSandbox中浏览

第二步:在IpInput组件中建立多个Input,并联动

重点是输出值的拆分与聚合

hdlInputChange = (e, block) => &#123;
    let ipAdressArr = [this.state.ablock, this.state.bblock, this.state.cblock, this.state.dblock]
    let value = e.target.value
    this.setState(&#123;[`$&#123;block&#125;block`]: value&#125;)
    ipAdressArr[blockDict[block]] = value
    this.props.onChange(&#123;target: &#123;value: ipAdressArr.join('.')&#125;&#125;)
  &#125;

详细代码请看:CodeSandbox

到这一步一个基本的ip Input输入组件已经成型,但是难点部分还未开始。

第三步:校验规则处理

如果现在我们就开始正常使用该组件,4个输入框的报错样式必定是同时出现的。一个是红框的话,其他
三个也必定是红框状态。这是ant design的程序设计,FormItem的报错样式由外层容器通过CSS控制。

上面这段话不容易理解的话自己试一下就明白了。

也就是说我们需要单独控制4个Input框的校验状态,已经有数字的应为正确状态,没有数字的校验时显示为
红色。且这个校验行为要能够被ant designForm组件触发。

首先我们建立一个类似ant design原生的Form.Item组件用来包裹每个Input并负责校验逻辑。

class FromItemValidatorWarpper extends Component &#123;
  constructor(props) &#123;
    super(props)

    this.state = &#123;
      validateStatus: ''
    &#125;
  &#125;

  componentDidMount () &#123;&#125;

  componentDidUpdate (prevProps, prevState) &#123;
    if(this.props.valiating && prevProps.validating !== this.props.valiating)&#123;
      // form 触发的 validating

      let validateStatus = ''
      if(!this.getChildProp("value"))&#123;
        validateStatus = 'error'
      &#125;
      if(this.state.validateStatus !== validateStatus)&#123;
        this.setState(&#123;validateStatus&#125;)
      &#125;

    &#125;
  &#125;

  getChildProp(prop) &#123;
    const child = this.props.children
    return child && child.props && child.props[prop];
  &#125;

  render() &#123;
    return (
      <Form.Item
        validateStatus=&#123;this.state.validateStatus&#125;
      >
        &#123;this.props.children&#125;
      </Form.Item>
    )
  &#125;
&#125;

然后用该组件包裹IpInput组件

<FromItemValidatorWarpper valiating=&#123;this.state.valiating&#125;>
  <Input value=&#123;this.state.dblock&#125; onChange=&#123;(e)=>&#123;this.hdlInputChange(e, 'd')&#125;&#125; />
</FromItemValidatorWarpper>

IpInput组件中建立valiating属性,并使其和外层ant design Form组件的validator联动。

  componentDidUpdate(prevProps) &#123;
    let dataField = this.props["data-__field"]

    if(dataField.errors && !prevProps["data-__field"].errors)&#123;
      // valitor 函数触发报错
      this.setState(&#123;valiating: true&#125;)
    &#125;
    if(!dataField.errors && prevProps["data-__field"].errors)&#123;
      // valitor 函数触发清除报错
      this.setState(&#123;valiating: false&#125;)
    &#125;
  &#125;

最后在使用IpInput的组件位置添加一个正则校验。

  &#123;getFieldDecorator('ip', &#123;
    rules: [
      &#123;required: true, message: 'Please input your ip!'&#125;,
      &#123;
        pattern: /((?:(?:25[0-5]|2[0-4]\d|((1\d&#123;2&#125;)|([1-9]?\d)))\.)&#123;3&#125;(?:25[0-5]|2[0-4]\d|((1\d&#123;2&#125;)|([1-9]?\d))))/,
        message: 'Please finish your ip!'
      &#125;
    ],
  &#125;)(
    <IpInput/>
  )&#125;

请在CodeSandbox中查看效果

OK,我们现在只差最后一步了,即在外层使用组件时使用setFieldsValue初始化组件值的功能。

第四步:setFieldsValue方法支持

componentDidUpdate方法中加入一段代码判断组件上级value是否和内部value相同,不同且没有进行
初始化时进行初始化。

if(dataField.value !== [this.state.ablock, this.state.bblock, this.state.cblock, this.state.dblock].join('.') && !this.initialized)&#123;
  this.initialized = true
  let ipAdressArr = dataField.value.split('.')
  this.setState(&#123;
    ablock: ipAdressArr[0],
    bblock: ipAdressArr[1],
    cblock: ipAdressArr[2],
    dblock: ipAdressArr[3],
  &#125;)
&#125;

请在CodeSandbox中查看效果

结尾

以上,一个IP输入组件基本完成了。细节包括样式还有很多可以优化的地方,我在这里就不继续了。maybe
以后再来更新。

我把最后的完整代码放在github上,有任何建议可
以给我提交issue或下面评论回复,谢谢。

联通G2-40盒子刷机教程

第一步 下载刷机包

链接: 百度云 提取码: um8d

第二步 准备刷机U盘

  1. U盘格式化为FAT32格式
  2. 解压下载的刷机包,将解压内容复制到U盘根目录

第三部 拆机

  1. 卸下底部的4颗螺丝,卸下顶部面板
  2. 短接J38跳线

    我使用的是一根回形针,见下图,任何导电的物体皆可。

  3. 插入U盘

第四部 通电开始刷机

  1. 连接HDMI线,电源线开始刷机
  2. 电视出现如下画面说明刷机开始,此时可以拔出跳线,如果到进度条走完还不拔,机器重启之后又会开始刷机。
  1. 等待进度条走完并重启自动进入新系统,至此刷机完成。

最后

整个过程还是比较简单的,刷机之后的新系统使用起来感觉也很不错。个人感觉要强于小米。
真是废物利用的好rom,感谢作者。

新西伯利亚市《开拓月球和火星》

偶然之间看到这条新闻,50年前的苏联朋友们对50年后的畅想。

新西伯利亚市《开拓月球和火星》亲爱的后代,今天是苏联建国100周年(苏联的国庆日是“十月革命”纪念日当天)的特别的日子。热烈祝贺这伟大而光荣的纪念日。我们的时代是很有趣的,想必你们的时代也很有趣吧。我们现在还在建设共产主义,你们应该已经生活在共产主义中了吧。我们相信,你们已经漂亮地开发了我们的美丽的蓝色行星,开拓月球,在火星着陆,不断地向着宇宙进发。太空船是不是已经冲出了银河系了呢?是不是已经和其他行星文明的代表们进行了对科学和文化合作的交涉了呢。”

摩尔曼斯克市《发现了很多自然的新的秘密》我们才刚刚向着宇宙踏出第一步,但是你们应该已经可以飞往其他行星了吧。很多现在还没发现的自然的新的秘密已经被发现了吧,核能可以控制了吧,元素的量可以根据人类意愿控制了吧,气候已经可以改变了吧,在北极可以开发花园了吧。希望1917年燃起的列宁永恒思想之火,能够永远在你们的心中燃烧。万岁!”

阿迪格共和国迈科普地区《21世纪是共产主义胜利的世纪!》“沙俄时代是一片荒芜、无法地带的阿迪格,现在已经变成了有着先进的社会主义经济、工业,高识字率、高文化程度的地方。在下一个即将到来的50周年,我相信,进步的人类已经面对所有民族的不共戴天之敌——帝国主义——取得了决定性的胜利。在马克思列宁主义的武装下,在科学预见力量的指引下,我们知道共产主义就会建立起来。愿21世纪是共产主义席卷全球、凯旋的世纪!”

短短的三段文字,看完之后心中却充满了唏嘘。为何50年后的我们没有达到他们的期待。为何曾经的超级大国,如今已经沦落到要靠卖资源度日。

但是我想,50年抑或500年其实都不是问题,只要人类还在向前,跨越星辰大海所需要的时间对个人来说也许很长,对宇宙来说都只是弹指一挥间。

为什么文明才5000年 而地球都46亿岁了。从自然正态分布规律来讲,我们这代人是不是运气太差了? - 周启楠的回答 - 知乎

苏联2017——时空胶囊中的未来展望

50年前的这五封信,来自苏联1967年封藏的时间胶囊