Fardwn

Fardwn 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

专注技术!

个人动态

Fardwn 发布了文章 · 9月5日

中国人有实力自信 2017.02.03

http://www.bilibili.com/video...

复旦大学教授张维为在2014年有个很意思的演讲,叫《中国人请自信一点》,我专门去看了完整的全集,张维为教授能在2014年就这样讲,真的非常不容易。

在大多数国人眼中,中国是一个落后国家,我们在媒体上,总是能看到“核心技术都在外国手里”“中国比发达国家落后50年甚至100年”。

然而事实与国人印象相反,虽然离发达国家还有距离,但和世界上大多数国家对比,中国已经是个先进国家,中国人如今的自信程度与中国实际拥有的实力完全不匹配,可以说,现实中的中国,比中国人内心中的中国要先进10年。

当前中国的实力已经非常强大,和美国实力的对比已经超过了当年的苏联,

现代国家的力量来自于制造业,我国制造业产值2010年就超过了美国,位居世界第一,而苏联在顶峰时期也不过只有美国的70%,德国和日本的制造业产值则从未达到过美国的70%,

当然我国高端制造业产值离美国还有差距,但是过去百年来,相对于德国,苏联和日本,我国已经是力量最接近美国的国家。总的来说,我国国民心态的变化和自信的增强要慢于实际实力的增强十年甚至更久,我这里想列举几点事实给大家提振点信心。

1:如果地球上只有5个人,中国是其中1个,那么只有1个人比中国人有钱。

2015年地球上有72亿人,人均GDP比中国高的只有欧洲7亿+美国3亿+日韩台马2.3亿+澳新3千万+阿根廷巴西墨西哥3.7亿+中东国家5000万, 共计大约16.8亿人,就算17亿好了,只占世界总人口的23.6%

下面这个表里的数据来自2015年10月IMF发布的2015年人均GDP的预测,我们可以看到,中国的人均GDP是8000美元级别,那么从8000-10000美元这个区间,人均GDP领先中国的国家有墨西哥,土耳其,巴西,俄罗斯,马来西亚。

这5个国家加起来有多少人口呢?墨西哥1.27亿,土耳其7500万,巴西2亿,俄罗斯1.44亿,马来西亚3000万,共计5.8亿人,占世界总人口的8%。

中国只要保持6.5%的增速,考虑到他们中有些还负增长如俄罗斯,超过他们轻而易举,那么5年后,中国GDP增长37%,届时全世界只有15%的人比中国人有钱。

这里说的是全中国平均的情况,如果你有幸住在中国的城市里面,又或者你受过大学教育,又或者你是年轻人,你将会高于平均值。

2:2015-2025年是美国的最后十年

我们这一代人,应该很荣幸生在这个时代,和国家民族一起走向复兴,这个过程我们本身很多并没有意识到,那就是中国超过美国只有最后的十年了。

在1990-2015年的25年时间里面,主要发达国家里面,美国是发展速度最快的,

1990年的GDP世界前三位,美国为5.98万亿美元,日本3.05万亿美元,德国1.71万亿美元,美国是日本的2倍,是德国的3.5倍

2015年美国GDP 17.87万亿美元,日本4.12万亿美元,德国3.36万亿美元,美国变成了日本的4倍,德国的5.5倍!如果看绝对差距,那么优势扩大的更多。

可以说,如果没有一个伟大国家的崛起,那么过去的25年世界格局毫无变化,一超将会更强,美国更进一步扩大了自己超级大国的优势。

1990年中国只有可怜的3570亿美元,2015年就增加到了10.42万亿美元。

中国人习惯了每年或者每两三年至少加薪一次,不然一定会跳槽,收入大概五到十年就要翻一番,这是我们习以为常的常态,但是在全世界90%的国家,过去的25年里面收入停滞或者龟速增长才是常态。

我们就以过去二十多年新经济欣欣向荣,人均GDP不断进步进入了世界前十,扩大了与主要发达国家优势的美国为例,从1990年到2015年的25年时间里面,美国人的收入增加了多少?

首先看看1991年的美国家庭收入中位数:4.087万美元

2015年9月,美国联邦人口普查局最新年度报告显示,美国家庭收入中位数家庭收入中位值为53657美元,在25年的时间里面,美国人的家庭收入仅仅增长了31%。

我们再把时间放近一点,从伟大的克林顿下台后开始算,

2000年美国家庭收入中位数为5.588万美元,也就是说,2015年的美国家庭收入中位数比起15年前还下降了4%

工资15年不涨反跌,各位同胞,如果这件事发生在中国,你会不会把政府骂翻?

从另外一个角度来看,作为人人向往的灯塔国, 2015年美国家庭收入中位数为5.36万美元,差不多35万人民币,中国的白领家庭,夫妻俩只要年入35万,就可以比一半的美国人收入高,这还不算,中国人收入的增长,房产的增值速度都比美国人快。

现在还有想移民美国的国人,固然能马上享受到更好的自然环境,社会保障,食品安全等

但请在心理上一定要做好两项心理建设准备:

1:移民后10年工资不涨,而国内同学的工资翻了一倍

2:移民后10年房价不涨,而国内同学的房子价格涨了一倍甚至两倍

再看看中国,

根据世界银行,IMF等的数据,2015年中国GDP占世界比重大约为15.5%,按照每年增长6.5%,那就是增长1个百分点,随着基数的增大,还不只1个百分点,因此只要中国保证6.5%的增速,2020年就可以占到世界GDP的20%,这是相当稳妥的。

名义值的增长是快于GDP实际增长值的。

我们把目光回看到2005年,彼时中国GDP才2.23万亿美元,到2015年达到10.42万亿美元,名义值10年增长了4.7倍,假设未来十年名义值增长速度是过去十年的一半,那么到2025年中国GDP也将达到23.46万亿美元

美国GDP 2005年是14.5万亿美元,2015年为17.87万亿美元,10年间名义值增长仅为23%,我们假设未来十年美国的发展速度比过去十年加快四分之一,达到30%, 那么到2025年将达到23.23万亿美元,低于中国。

实际上,中国应该能提前完成这一目标,因为中国目前的经济实力是被低估的,

中国目前是按照SNA 1993 GDP 核算体系,但是以下三个内容没有执行:

首先,根据SNA1993 GDP核算体系,美国是把住自己的房子也按照市场价算成虚拟租金计入GDP,2014年此项就高达1.6万亿美元,占到GDP的9%。

而中国住自己的房子则是以房屋折旧和物业费来算的,举个例子,城镇的房子2005年50万建造,乘以固定比率2%,一年折旧一万,计入GDP,实际上这个房子到2015年早就涨到300万了。

其次中国第二产业中的工业,只统计规模以上工业增加值,也就是主营业务收入2000万人民币以上的收入

然后中国不把非法和地下经济计入GDP,如赌博,色情等,实际上这些都是SNA1993 规定要统计的内容。

在美国,拉斯维加斯的赌博业,还有部分州的色情业都是计入GDP的

另外中国还有庞大的境外代购产业,也并不计入GDP

我国现行沿用的SNA1993核算体系,以上的统计项中国都没有执行,更新版本的SNA2008 GDP核算体系,中国原定2015年执行,现在也没有这样做。

而美国的GDP,早在2013年就执行SNA 2008 体系了,把研发支出也计入GDP,美国2012年的GDP一下就增加了5600亿美元,增加了3.6%

如果中国按照SNA2008 体系核算,GDP也会增加数千亿美元

这个世界90%国家的人民,他们社会的生活是静止的,缓慢的,工资是20年不涨的,是一个慢节奏的社会,也可以说是一个一潭死水的社会。

巴西是如此,俄罗斯是如此,美国是如此,我们隔壁的日本,香港,台湾,都是如此。

而在中国,你生活在一个急剧变化的向上的社会,你生活在一个积极向上的国家,在和世界霸主美国竞争,而且能在10年内毫无疑问的取得胜利。

3:中国和美国是世界科技第一阵营强国,比日本德国英国强

1)  自然指数

2014年11月,自然出版集团首次以全新的“加权分值计数法”(WFC,weighted fractional count)指数方式发布了全球“自然指数”。自然指数的分析是基于前一年各科研机构在Nature系列、Science、Cell等68种自然科学类期刊上发表的研究型论文数量进行计算和统计,它追踪了约6万篇优质科研论文的作者单位信息,涵盖全球2万多家科研机构。

Web of Science数据库中,这68种期刊在自然科学期刊总数中占比不到1%,但其产生的引用量约占自然科学期刊总引用量的30%。

2014年的“自然指数”统计结果,美国第一,18643。中国第二,5206。德国第三,4077。四到十位是日本、英国、法国、加拿大、西班牙、瑞士、韩国。相对于2013年,中国增长14.9%,美国下降0.8%,其它国家基本增长都不超过2%。

中国2015年1月至12月的年度数据变为6480,增长24.5%,呈加速爆发增长之势。

如果能保持这样的速度,意味着中国发表在世界顶尖杂志的的高质量科研产出在5年内就超过美国。

2)  1% 论文高被引顶尖科学家

汤森路透集团公布的2014年全球“高被引科学家”名单, 根据2002—2012年自然和社会科学领域论文进行分析评估,并将所属领域同一年度他引频次在前1%的论文进行排名统计, 我国(含港澳)共有134人入选2014年“高被引科学家”,仅次于美国(1702人)、英国(304人)和德国(163人)。

2015年度公布的全球高被引科学家,是根据2003年—2013年在全球各领域引用次数前1%的高质量论文科学家,全球共有2975名(3125人次)科学家入选,中国共有148位科学家(含港澳台地区)入选168人次。

可以看到,2002-2012,2003-2013,仅仅是把2002年换成2013年,人数就增加了15人,增加了11%以上。

3)前沿科学核心论文

中科院文献情报中心和美国汤森路透集团统计了2015年10个科学领域中100个热点前沿和49个新兴前沿的数据。美国在143个前沿中有核心论文,其中108个前沿的核心论文数排名第一。中国在82个前沿中有核心论文,核心论文数排第一的前沿有16个,超过英国(10个)、德国(8个)以及法国和日本(2个)。

世界第二毫无悬念

4)  1% 高被引论文数量

2004年至2013年的十年间,中国高被引论文数量突破1.2万篇,占到全世界高被引论文总量的10.4%,在美国(6.2万篇)、英国(1.6万篇)、德国(1.4万篇)之后,居世界第4位。

中国各学科论文在2005—2014年10年段的被引用次数处于世界前1%的高被引论文有15011篇,占世界份额的11.9%,排名世界第四位

请注意,上面两篇报道的高被引论文量是十年的总和,按照我国科技的爆发成长,数量肯定是前低后高。

两次10年数据的统计有9年是相同的,仅仅是把2004年换成了2014年,高被引论文数量就从1.2万变成了1.5万篇,增加了3000篇,考虑到中国的增速远远超过英国和德国,世界第二当之无愧。

4:中美是世界舞台的中心国家,而其他国家更看好中国

我们自己看好自己,那外国人是否看好我们?

下面是2015年皮尤研究中心公布的在全球40个主要国家的调查,

认为中国已经或者将要取代美国的比例为48%,而认为中国永远无法超过美国的比例是35%,作为场上在竞争的仅有的2个重量级选手,外国人对中国更有信心。

40个国家有27个认为中国将会取代美国,这个数字在2008年只有6个,在2013年是23个,

中国变强了,形象就更正面了,这个世界果然还是认实力,对35个国家的调查,2015年对中国有正面看法的人为55%,而2014年这一比例为49%,对中国表示负面看法的人在下降,2014为38%,2015下降为34%

在中美的竞争中,其他国家只是台下的观众,显然,台下的观众更看好我们。

你生活在一个强大而富裕的国家,而且还在以世界最快的速度走向更强大,作为中国人,有什么理由不自信呢?

我相信,在未来十年,中国人的国民心态会慢慢改变,自信会逐渐增强,目前中国社会普遍存在的一些现象,将会逐渐变淡甚至消失,举几个例子,看看你是否有以下症状:

1:中国男人们不敢追白人女孩,潜意识认为自己无论是从外表,还是文化,还是性能力,还是经济上,都对白人女孩没有足够的吸引力,不敢展开追求。这点在美国华人身上表现的非常明显。我国去欧美留学的留学生,极少敢于对当地女孩展开追求。

反之对能追到外国女孩的中国男性,中国男人们都送上牛逼,为国争光等赞美之词,而不是认为这是平等的交往。

而对和外国男人交往的中国女孩,中国男人们往往持有愤愤然的态度,却完全不检讨自己心里是否足够强大和自信。

2:对外国人想当然觉得其幽默/有钱/高端

来中国的外国男人们,是向来不愁找不到女朋友的,部分人更是一年换很多个,哪怕是学历不高,工作不好的屌丝们也不例外,这就好比,我国只有中专学历的杀马特,又或者是在县城台球馆游荡的初中无业青年,去了国外被外国妞环绕,仅仅因为其中国脸,想想这是什么画面。

3:全民极为优待外国人,做不到中外平等,生怕被人看不起,“丢中国人的脸”。例如帮外国人找自行车,帮外国人翻数吨垃圾找护照,新闻报道以做了某某事受到外国人夸奖为荣。

前不久广西一个派出所,行政拘留了一个签证到期非法滞留的黑人穆斯林,派出所专门动用财力和资源贴心的为他准备了清真餐,准备了礼拜室,最后得到了这个罪犯的夸奖,派出所将此事颇为得意的放在网站上宣传。这个派出所从所长到警员的心态,在中国社会不是孤例。

4:广告宣传,你如果多留意在路边,电视台,网站,超市售卖物品的包装上,火车站,临街商铺,还有各种淘宝店铺,都会看到大量的白人形象,你有没有想过,这些白人大部分其实根本不是什么明星,而是由各种外国来华留学生,无业人员组成,像你我一样的路人?

2015年底央视办过一期外国留学生到义乌实习做淘宝的,一个留学生到一家,这些淘宝店铺包括各种各样的商品,非常有意思,不管高矮美丑,作为免费实习的交换,这些淘宝商家无一例外让这些留学生拍照作为自己淘宝页面的形象广告

我曾经在一家县城的餐馆,因为曾经有在附近中学教书的外教来吃过饭,老板竟然把他们就餐的照片打印出来放大挂在店里面作为宣传。

与外国形象类似的,还有各种各样洋名字的楼盘,洋名字的国内品牌

为何中国的商家热衷于此,自己内心深处觉得外国脸比较洋气。

5:极为重视外国人的评价,做不到外国人的评价和中国人的评价一视同仁,

以入选了世界科技十大突破的我国大亚湾中微子实验为例,2016年我国测量出最精确中微子能谱的新闻,我们看看这个叫李大庆的记者是如何报道的

作为主要参与人的国内顶尖科学家对这项成果的评价就删掉,而作为非直接参与者的外国人的评论就保留,一项世界级成果,要得到外国人的表扬心里才觉得光荣,哪怕这个外国人根本不如国内的学者专业,这个记者内心的心态是值得玩味的。

自信的中国人,会对本国人和外国人一视同仁,是平视的姿态,而不是仰视的姿态。

我们的国家和民族将会重新登顶世界之巅,这是基于事实的判断,

对于知乎上绝大多数的80后和90后来说,当你们的孩子懂事的时候,你的国家就已经是世界第一了,你的孩子会理所当然的觉得中国很强。

那么你自己呢? 会如你孩子那样的自信吗?中国人,请相信你自己吧。

查看原文

赞 0 收藏 0 评论 0

Fardwn 赞了回答 · 2019-09-30

解决运行在真机上,用<span>会莫名其妙变成拨打电话

我记得好像apple有个私有meta标签,是关于将数字识别为tele号码的吧,建议翻下meta大全把这个加上试试~

关注 3 回答 2

Fardwn 收藏了文章 · 2019-06-17

mac下mongodb的安装与配置

本文相关源码

安装 mongodb

install 之前,iTerm2 下用 brew 查看已安装软件、搜索 mongodb:

brew list
brew search mongodb

安装 mongodb :

brew install mongodb

此处需要稍等一段时间,成功后会输出以下即说明安装成功:

$ brew install mongodb
Updating Homebrew...
==> Downloading https://homebrew.bintray.com/bottles/mongodb-3.4.0.sierra.bottle.1.tar.gz
######################################################################## 100.0%
==> Pouring mongodb-3.4.0.sierra.bottle.1.tar.gz
==> Caveats
To have launchd start mongodb now and restart at login:
  brew services start mongodb
Or, if you don't want/need a background service you can just run:
  mongod --config /usr/local/etc/mongod.conf
==> Summary
?  /usr/local/Cellar/mongodb/3.4.0: 17 files, 261.4M

启动 mongodb

新建一个 iTerm2 窗口,执行 mongod 尝试启动 mongodb 但会失败 exiting:

$ mongod
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] MongoDB starting : pid=1765 port=27017 dbpath=/data/db 64-bit host=MacBook-Pro-2.local
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] db version v3.4.0
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] git version: f4240c60f005be757399042dc12f6addbc3170c1
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.2j  26 Sep 2016
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] allocator: system
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] modules: none
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] build environment:
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten]     distarch: x86_64
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten]     target_arch: x86_64
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] options: {}
2017-06-12T15:51:49.811+0800 I STORAGE  [initandlisten] exception in initAndListen: 29 Data directory /data/db not found., terminating
2017-06-12T15:51:49.811+0800 I NETWORK  [initandlisten] shutdown: going to close listening sockets...
2017-06-12T15:51:49.811+0800 I NETWORK  [initandlisten] shutdown: going to flush diaglog...
2017-06-12T15:51:49.811+0800 I CONTROL  [initandlisten] now exiting
2017-06-12T15:51:49.811+0800 I CONTROL  [initandlisten] shutting down with code:100

启动 mongodb 之前,要先新建一个mongodb默认的数据写入目录:

$ mkdir -p /data/db
mkdir: /data/db: Permission denied (没有权限拒绝访问)

// sudo 并输入密码,重新新建目录
$ sudo mkdir -p /data/db
Password:

给刚才新建的数据库目录赋予权限:

$ sudo chown -R guojc /data

此时,执行 mongod 启动 mongodb 服务:

$ mongod
2017-06-12T16:00:48.036+0800 I CONTROL  [initandlisten] MongoDB starting : pid=1837 port=27017 dbpath=/data/db 64-bit host=MacBook-Pro-2.local
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] db version v3.4.0
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] git version: f4240c60f005be757399042dc12f6addbc3170c1
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.2j  26 Sep 2016
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] allocator: system
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] modules: none
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] build environment:
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten]     distarch: x86_64
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten]     target_arch: x86_64
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] options: {}
2017-06-12T16:00:48.037+0800 I STORAGE  [initandlisten] wiredtiger_open config: create,cache_size=3584M,session_max=20000,eviction=(threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),checkpoint=(wait=60,log_size=2GB),statistics_log=(wait=0),
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten]
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten]
2017-06-12T16:00:48.665+0800 I FTDC     [initandlisten] Initializing full-time diagnostic data capture with directory '/data/db/diagnostic.data'
2017-06-12T16:00:48.741+0800 I INDEX    [initandlisten] build index on: admin.system.version properties: { v: 2, key: { version: 1 }, name: "incompatible_with_version_32", ns: "admin.system.version" }
2017-06-12T16:00:48.741+0800 I INDEX    [initandlisten]    building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2017-06-12T16:00:48.755+0800 I INDEX    [initandlisten] build index done.  scanned 0 total records. 0 secs
2017-06-12T16:00:48.756+0800 I COMMAND  [initandlisten] setting featureCompatibilityVersion to 3.4
2017-06-12T16:00:48.756+0800 I NETWORK  [thread1] waiting for connections on port 27017

mongodb 启动成功,正等待着被连接。

新建 iTerm2 窗口,执行 mongo,进入 mongodb 命令行模式:

$ mongo
MongoDB shell version v3.4.0
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.0
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
  http://docs.mongodb.org/
Questions? Try the support group
  http://groups.google.com/group/mongodb-user
Server has startup warnings:
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten]
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten]
>

继续在上面的终端输入 show dbs,会列出系统自带的2个数据库:

> show dbs
admin  0.000GB
local  0.000GB
help

小结一下,往后要重新启动 mongodb 服务、进入 mongodb 命令行的操作:
在一个iTerm2窗口执行:mongod //MongoDB starting........waiting for connections
另一个iTerm2窗口执行:mongo //MongoDB shell

插入数据

首先,在电脑根目录下安装 node 的数据驱动库:

# guojc @ MacBook-Pro-2 in ~ [16:29:33] C:127
$ cnpm install mongodb

新建目录并编辑text.js文件: s3_mongodb/test.js:

var MongoClient = require('mongodb').MongoClient;
// 连接数据库
var url_test = 'mongodb://localhost:27017/test'; //数据库test本不存在,连接时会自动创建

var insertData = function(db){
  // 往test数据库里新建一个site集合,并插入一条数据
  db.collection('site').insertOne({name: 'guojc', age: 99, hobby: 'movie'}, function(err, result){
    console.log('inserted successly');
    console.log(result);
    db.close();
    console.log('close');
  });
}

MongoClient.connect(url_test, function(err, db) {
  console.log('Connected successly to server.');
  insertData(db);
});

新建 iTerm2 窗口,运行 test.js:

node test.js

成功后,来到之前执行过 mongo 的 MongoDB shell 窗口,执行:

> show dbs
admin  0.000GB
local  0.000GB
test  0.000GB
help

继续执行:选中数据库test、显示test中的表,查询该表的所有数据:

> use test
switched to db test

> show tables
site

> db.site.find()
{ "_id" : ObjectId("593e594ab4727b09349c33a6"), "name" : "guojc", "age" : 99, "hobby" : "movie" }

至此,mongodb的安装配置与基本启动就介绍完了。
下一篇继续学习使用 mongoose 来操作 mongodb 。

查看原文

Fardwn 赞了文章 · 2019-06-17

Node +MongoDB 搭建后台的全过程

Node + mongoDB 搭建后台的全过程

近期基于 vue-cil 搭建前端项目, express + mongoose 搭建后台,遇到了不少问题,总结博客如下,有什么不正确的地方,请大家批评指正^ _ ^!

mongodb 是 NoSQL 非关系型数据库。
mongoose是 mongodb 的一个对象模型工具,是基于 node-mongodb-native 开发的 mongodb 的 nodejs 驱动,可以在异步的环境下执行。同时它也是针对 mongodb 操作的一个对象模型库,封装了 mongodb 对文档的一些增删改查等常用方法,让nodejs操作 mongodb 数据库变得更加容易。

一、Mac下mongodb的安装与配置

1、 安装mongodb:

brew install mongodb

2、安装成功之后,启动 mongodb

mongod

3、执行 mongod ,启动 mongodb 失败,显示如下:

clipboard.png

原因 : 启动 mongodb 之前,要先新建一个mongodb默认的数据写入目录:

sudo mkdir -p /data/db

给刚才新建的数据库目录赋予权限

sudo chown -R 用户名 /data

如下图所示:

clipboard.png

4、再次启动

mongod

5、若启动成功,可以通过http访问该数据库,mongodb使用了27017端口,因此在浏览器中打开 http://localhost:27017
出现如下图所示提示,即说明连接成功。默认的情况下,关闭shell,mongodb就停止运行了 如果想在后台运行,启动时只需添加 --fork函数即可

clipboard.png

小结:

mongod 是用来连接到mongodb数据库服务器的,即服务器端。
mongo 是用来启动MongoDB shell的,是mongodb的命令行客户端。

二、安装 MongoDB可视化工具 RoboMongo

下载地址:https://robomongo.org/download

三、连接数据库

dbase.js

let mongoose = require('mongoose');
/**
 * 连接
 */
mongoose.connect('mongodb://127.0.0.1:27017/learning',{useNewUrlParser: true});

/**
  * 连接成功
  */
mongoose.connection.on('connected', function () {    
    console.log('Mongoose connection succuss' );  
});    

/**
 * 连接异常
 */
mongoose.connection.on('error',function (err) {    
    console.log('Mongoose connection error: ' + err);  
});    
 
/**
 * 连接断开
 */
mongoose.connection.on('disconnected', function () {    
    console.log('Mongoose connection disconnected');  
}); 


module.exports = mongoose;

四、启动 node 服务

server.js

const fs = require('fs')
const path = require('path')
const express = require('express')
const session = require('express-session')
const bodyParser = require('body-parser')
const app = express()

require('./dbase')
app.use(bodyParser.json()) //解析json类型的请求体

app.get('*', function (req, res) {
    res.sendFile(path.join(__dirname, '../Learning/index.html'));
})

app.listen(process.env.PORT || 8000, function () {
    console.log("Listen port:8000...")
});

小结:

问题: 启动node服务连接数据库时,报错如下图所示:

clipboard.png

解决办法:在 mongoose 5.x 版本中,连接数据库时,不再使用 {useMongoClient: true}),而是 { useNewUrlParser: true }

五、 vue-cil 搭建的项目跨域问题

Vue-cli 创建的项目,可以直接利用 Node.js 代理服务器,通过修改proxyTable接口实现跨域请求。在vue-cli项目中的config文件夹下的index.js配置文件中,修改 dev 里面的proxyTable: {}项,如下所示:

proxyTable: {  
    '/api': {  //代理地址  
        target: 'http://127.0.0.1:8000/',  //需要代理的地址  
        changeOrigin: true,  //是否跨域  
        secure: false,    
        pathRewrite: {  
            '^/api': '/'   //本身的接口地址没有 '/api' 这种通用前缀,所以要rewrite,如果本身有则去掉  
        }
    }
}

重启项目npm run dev ,接口前加上“/api”即可实现跨域请求

查看原文

赞 30 收藏 22 评论 5

Fardwn 赞了文章 · 2019-06-14

mac下mongodb的安装与配置

本文相关源码

安装 mongodb

install 之前,iTerm2 下用 brew 查看已安装软件、搜索 mongodb:

brew list
brew search mongodb

安装 mongodb :

brew install mongodb

此处需要稍等一段时间,成功后会输出以下即说明安装成功:

$ brew install mongodb
Updating Homebrew...
==> Downloading https://homebrew.bintray.com/bottles/mongodb-3.4.0.sierra.bottle.1.tar.gz
######################################################################## 100.0%
==> Pouring mongodb-3.4.0.sierra.bottle.1.tar.gz
==> Caveats
To have launchd start mongodb now and restart at login:
  brew services start mongodb
Or, if you don't want/need a background service you can just run:
  mongod --config /usr/local/etc/mongod.conf
==> Summary
?  /usr/local/Cellar/mongodb/3.4.0: 17 files, 261.4M

启动 mongodb

新建一个 iTerm2 窗口,执行 mongod 尝试启动 mongodb 但会失败 exiting:

$ mongod
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] MongoDB starting : pid=1765 port=27017 dbpath=/data/db 64-bit host=MacBook-Pro-2.local
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] db version v3.4.0
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] git version: f4240c60f005be757399042dc12f6addbc3170c1
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.2j  26 Sep 2016
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] allocator: system
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] modules: none
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] build environment:
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten]     distarch: x86_64
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten]     target_arch: x86_64
2017-06-12T15:51:49.810+0800 I CONTROL  [initandlisten] options: {}
2017-06-12T15:51:49.811+0800 I STORAGE  [initandlisten] exception in initAndListen: 29 Data directory /data/db not found., terminating
2017-06-12T15:51:49.811+0800 I NETWORK  [initandlisten] shutdown: going to close listening sockets...
2017-06-12T15:51:49.811+0800 I NETWORK  [initandlisten] shutdown: going to flush diaglog...
2017-06-12T15:51:49.811+0800 I CONTROL  [initandlisten] now exiting
2017-06-12T15:51:49.811+0800 I CONTROL  [initandlisten] shutting down with code:100

启动 mongodb 之前,要先新建一个mongodb默认的数据写入目录:

$ mkdir -p /data/db
mkdir: /data/db: Permission denied (没有权限拒绝访问)

// sudo 并输入密码,重新新建目录
$ sudo mkdir -p /data/db
Password:

给刚才新建的数据库目录赋予权限:

$ sudo chown -R guojc /data

此时,执行 mongod 启动 mongodb 服务:

$ mongod
2017-06-12T16:00:48.036+0800 I CONTROL  [initandlisten] MongoDB starting : pid=1837 port=27017 dbpath=/data/db 64-bit host=MacBook-Pro-2.local
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] db version v3.4.0
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] git version: f4240c60f005be757399042dc12f6addbc3170c1
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.2j  26 Sep 2016
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] allocator: system
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] modules: none
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] build environment:
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten]     distarch: x86_64
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten]     target_arch: x86_64
2017-06-12T16:00:48.037+0800 I CONTROL  [initandlisten] options: {}
2017-06-12T16:00:48.037+0800 I STORAGE  [initandlisten] wiredtiger_open config: create,cache_size=3584M,session_max=20000,eviction=(threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),checkpoint=(wait=60,log_size=2GB),statistics_log=(wait=0),
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten]
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten]
2017-06-12T16:00:48.665+0800 I FTDC     [initandlisten] Initializing full-time diagnostic data capture with directory '/data/db/diagnostic.data'
2017-06-12T16:00:48.741+0800 I INDEX    [initandlisten] build index on: admin.system.version properties: { v: 2, key: { version: 1 }, name: "incompatible_with_version_32", ns: "admin.system.version" }
2017-06-12T16:00:48.741+0800 I INDEX    [initandlisten]    building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2017-06-12T16:00:48.755+0800 I INDEX    [initandlisten] build index done.  scanned 0 total records. 0 secs
2017-06-12T16:00:48.756+0800 I COMMAND  [initandlisten] setting featureCompatibilityVersion to 3.4
2017-06-12T16:00:48.756+0800 I NETWORK  [thread1] waiting for connections on port 27017

mongodb 启动成功,正等待着被连接。

新建 iTerm2 窗口,执行 mongo,进入 mongodb 命令行模式:

$ mongo
MongoDB shell version v3.4.0
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.0
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
  http://docs.mongodb.org/
Questions? Try the support group
  http://groups.google.com/group/mongodb-user
Server has startup warnings:
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten]
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-06-12T16:00:48.616+0800 I CONTROL  [initandlisten]
>

继续在上面的终端输入 show dbs,会列出系统自带的2个数据库:

> show dbs
admin  0.000GB
local  0.000GB
help

小结一下,往后要重新启动 mongodb 服务、进入 mongodb 命令行的操作:
在一个iTerm2窗口执行:mongod //MongoDB starting........waiting for connections
另一个iTerm2窗口执行:mongo //MongoDB shell

插入数据

首先,在电脑根目录下安装 node 的数据驱动库:

# guojc @ MacBook-Pro-2 in ~ [16:29:33] C:127
$ cnpm install mongodb

新建目录并编辑text.js文件: s3_mongodb/test.js:

var MongoClient = require('mongodb').MongoClient;
// 连接数据库
var url_test = 'mongodb://localhost:27017/test'; //数据库test本不存在,连接时会自动创建

var insertData = function(db){
  // 往test数据库里新建一个site集合,并插入一条数据
  db.collection('site').insertOne({name: 'guojc', age: 99, hobby: 'movie'}, function(err, result){
    console.log('inserted successly');
    console.log(result);
    db.close();
    console.log('close');
  });
}

MongoClient.connect(url_test, function(err, db) {
  console.log('Connected successly to server.');
  insertData(db);
});

新建 iTerm2 窗口,运行 test.js:

node test.js

成功后,来到之前执行过 mongo 的 MongoDB shell 窗口,执行:

> show dbs
admin  0.000GB
local  0.000GB
test  0.000GB
help

继续执行:选中数据库test、显示test中的表,查询该表的所有数据:

> use test
switched to db test

> show tables
site

> db.site.find()
{ "_id" : ObjectId("593e594ab4727b09349c33a6"), "name" : "guojc", "age" : 99, "hobby" : "movie" }

至此,mongodb的安装配置与基本启动就介绍完了。
下一篇继续学习使用 mongoose 来操作 mongodb 。

查看原文

赞 11 收藏 18 评论 2

Fardwn 收藏了文章 · 2019-05-08

细解JavaScript ES7 ES8 ES9 新特性

导言:ECMAScript的演化不会停止,但是我们完全没必要害怕。除了ES6这个史无前例的版本带来了海量的信息和知识点以外,之后每年一发的版本都仅仅带有少量的增量更新,一年更新的东西花半个小时就能搞懂了,完全没必要畏惧。本文将带您花大约一个小时左右的时间,迅速过一遍ES7,ES8,ES9的新特性。

想追求更好的阅读体验,请移步原文地址

es_16_17_18

题记:本文提供了一个在线PPT版本,方便您浏览 细解JAVASCRIPT ES7 ES8 ES9 新特性 在线PPT ver

本文的大部分内容译自作者Axel Rauschmayer博士的网站,想了解更多关于作者的信息,可以浏览Exploring JS: JavaScript books for programmers

那些与ECMAScript有关的事情

谁在设计ECMAScript?

TC39 (Technical Committee 39)。

TC39 是推进 JavaScript 发展的委员会。其会员都是公司(其中主要是浏览器厂商)。TC39 定期召开会议,会议由会员公司的代表与特邀专家出席。会议纪录都可在网上查看,可以让你对 TC39 如何工作有一个清晰的概念。

很有意思的是,TC39 实行的是协商一致的原则:通过一项决议必须得到每一位会员(公司代表)的赞成。

ECMAScript的发布周期

在2015年发布的 ECMAScript(ES6)新增内容很多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化。两个发布版本之间时间跨度如此之大主要有两大原因:

  • 比新版率先完成的特性,必须等待新版的完成才能发布。
  • 那些需要花长时间完成的特性,也顶着很大的压力被纳入这一版本,因为如果推迟到下一版本发布意味着又要等很久,这种特性也会推迟新的发布版本。

因此,从 ECMAScript 2016(ES7)开始,版本发布变得更加频繁,每年发布一个新版本,这么一来新增内容也会更小。新版本将会包含每年截止时间之前完成的所有特性。

ECMAScript的发布流程

每个 ECMAScript 特性的建议将会从阶段 0 开始, 然后经过下列几个成熟阶段。其中从一个阶段到下一个阶段必须经过 TC39 的批准。

  1. stage-0 - Strawman: just an idea, possible Babel plugin.
    任何讨论、想法、改变或者还没加到提案的特性都在这个阶段。只有TC39成员可以提交。

    当前的stage 0列表可以查看这里 --> Stage 0 Proposals

  2. stage-1 - Proposal: this is worth working on.

    什么是 Proposal?一份新特性的正式建议文档。提案必须指明此建议的潜在问题,例如与其他特性之间的关联,实现难点等。

  3. stage-2 - Draft: initial spec.

    什么是 Draft?草案是规范的第一个版本。其与最终标准中包含的特性不会有太大差别。

    草案之后,原则上只接受增量修改。这个阶段开始实验如何实现,实现形式包括polyfill, 实现引擎(提供草案执行本地支持),或者编译转换(例如babel)

  4. stage-3 - Candidate: complete spec and initial browser implementations.

    候选阶段,获得具体实现和用户的反馈。此后,只有在实现和使用过程中出现了重大问题才会修改。至少要在一个浏览器中实现,提供polyfill或者babel插件。

  5. stage-4 - Finished: will be added to the next yearly release.

    已经准备就绪,该特性会出现在下个版本的ECMAScript规范之中。

    当前的stage 1-3列表可以查看这里 --> ECMAScript proposals

已经正式发布的特性索引

ProposalAuthorChampion(s)TC39 meeting notesExpected Publication Year
Array.prototype.includesDomenic DenicolaDomenic Denicola
Rick Waldron
November 20152016
Exponentiation operatorRick WaldronRick WaldronJanuary 20162016
Object.values/Object.entriesJordan HarbandJordan HarbandMarch 20162017
String paddingJordan HarbandJordan Harband
Rick Waldron
May 20162017
Object.getOwnPropertyDescriptorsJordan Harband
Andrea Giammarchi
Jordan Harband
Andrea Giammarchi
May 20162017
Trailing commas in function parameter lists and callsJeff MorrisonJeff MorrisonJuly 20162017
Async functionsBrian TerlsonBrian TerlsonJuly 20162017
Shared memory and atomicsLars T HansenLars T HansenJanuary 20172017
Lifting template literal restrictionTim DisneyTim DisneyMarch 20172018
s (dotAll) flag for regular expressionsMathias BynensBrian Terlson
Mathias Bynens
November 20172018
RegExp named capture groupsGorkem Yakin
Daniel Ehrenberg
Daniel Ehrenberg
Brian Terlson
Mathias Bynens
November 20172018
Rest/Spread PropertiesSebastian MarkbågeSebastian MarkbågeJanuary 20182018
RegExp Lookbehind AssertionsGorkem Yakin
Nozomu Katō
Daniel Ehrenberg
Daniel Ehrenberg
Mathias Bynens
January 20182018
RegExp Unicode Property EscapesMathias BynensBrian Terlson
Daniel Ehrenberg
Mathias Bynens
January 20182018
Promise.prototype.finallyJordan HarbandJordan HarbandJanuary 20182018
Asynchronous IterationDomenic DenicolaDomenic DenicolaJanuary 20182018
Optional catch bindingMichael FicarraMichael FicarraMay 20182019
JSON supersetRichard GibsonMark Miller
Mathias Bynens
May 20182019

ES7新特性(ECMAScript 2016)

ECMAScript 2016

ES7在ES6的基础上主要添加了两项内容:

  • Array.prototype.includes()方法
  • 求幂运算符(**)

Array.prototype.includes()方法

includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。

var array = [1, 2, 3];

console.log(array.includes(2));
// expected output: true

var pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false

Array.prototype.includes()方法接收两个参数:

  • 要搜索的值
  • 搜索的开始索引。

当第二个参数被传入时,该方法会从索引处开始往后搜索(默认索引值为0)。若搜索值在数组中存在则返回true,否则返回false。 且看下面示例:

['a', 'b', 'c', 'd'].includes('b')         // true
['a', 'b', 'c', 'd'].includes('b', 1)      // true
['a', 'b', 'c', 'd'].includes('b', 2)      // false

乍一看,includes的作用跟数组的indexOf重叠,为什么要特意增加这么一个api呢?主要区别有以下几点:

  • 返回值。看一个函数,先看他们的返回值。indexOf的返回数是值型的,includes的返回值是布尔型,所以在if条件判断的时候includes要简单得多,而indexOf 需要多写一个条件进行判断。
var ary = [1];
if (ary.indexOf(1) !== -1) {
    console.log("数组存在1")
}
if (ary.includes(1)) {
    console.log("数组存在1")
}
  • NaN的判断。如果数组中有NaN,你又正好需要判断数组是否有存在NaN,这时你使用indexOf是无法判断的,你必须使用includes这个方法。
var ary1 = [NaN];
console.log(ary1.indexOf(NaN))//-1
console.log(ary1.includes(NaN))//true
  • 当数组的有空的值的时候,includes会认为空的值是undefined,而indexOf不会。
var ary1 = new Array(3);
console.log(ary1.indexOf(undefined));//-1
console.log(ary1.includes(undefined))//true

求幂运算符(**)

加/减法我们通常都是用其中缀形式,直观易懂。在ECMAScript2016中,我们可以使用**来替代Math.pow。

4 ** 3           // 64

效果等同于

Math.pow(4,3)

值得一提的是,作为中缀运算符,**还支持以下操作

let n = 4;
n **= 3;
// 64

ES8新特性(ECMAScript 2017)

ECMAScript 2017

在2017年1月的TC39会议上,ECMAScript 2017的最后一个功能“Shared memory and atomics”推进到第4阶段。这意味着它的功能集现已完成。

ECMAScript 2017特性一览

主要新功能:

  • 异步函数 Async Functions(Brian Terlson)
  • 共享内存和Atomics(Lars T. Hansen)

次要新功能:

  • Object.values / Object.entries(Jordan Harband)
  • String padding(Jordan Harband,Rick Waldron)
  • Object.getOwnPropertyDescriptors() (Jordan Harband,Andrea Giammarchi)
  • 函数参数列表和调用中的尾逗号(Jeff Morrison)

Async Functions

Async Functions也就是我们常说的Async/Await,相信大家对于这个概念都已经不陌生了。Async/Await是一种用于处理JS异步操作的语法糖,可以帮助我们摆脱回调地狱,编写更加优雅的代码。

通俗的理解,async关键字的作用是告诉编译器对于标定的函数要区别对待。当编译器遇到标定的函数中的await关键字时,要暂时停止运行,带到await标定的函数处理完毕后,再进行相应操作。如果该函数fulfiled了,则返回值是fulfillment value,否则得到的就是reject value。

下面通过拿普通的promise写法来对比,就很好理解了:

async function asyncFunc() {
    const result = await otherAsyncFunc();
    console.log(result);
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc()
    .then(result => {
        console.log(result);
    });
}

按顺序处理多个异步函数的时候优势更为明显:

async function asyncFunc() {
    const result1 = await otherAsyncFunc1();
    console.log(result1);
    const result2 = await otherAsyncFunc2();
    console.log(result2);
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc1()
    .then(result1 => {
        console.log(result1);
        return otherAsyncFunc2();
    })
    .then(result2 => {
        console.log(result2);
    });
}

并行处理多个异步函数:

async function asyncFunc() {
    const [result1, result2] = await Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ]);
    console.log(result1, result2);
}

// Equivalent to:
function asyncFunc() {
    return Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ])
    .then([result1, result2] => {
        console.log(result1, result2);
    });
}

处理错误:

async function asyncFunc() {
    try {
        await otherAsyncFunc();
    } catch (err) {
        console.error(err);
    }
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc()
    .catch(err => {
        console.error(err);
    });
}

Async Functions若是要展开去讲,可以占用很大段的篇幅。鉴于本文是一篇介绍性文章,再次不再进行深入。

SharedArrayBuffer和Atomics

,如果之前您没有接触过ArrayBuffer相关知识的话,建议您从内存管理速成教程系列漫画解说入门,强推:
A crash course in memory management
[A cartoon intro to ArrayBuffers and SharedArrayBuffers
](https://hacks.mozilla.org/201...
[Avoiding race conditions in SharedArrayBuffers with Atomics
](https://hacks.mozilla.org/201...


ECMAScript 2017 特性 SharedArrayBuffer 和 atomics”,由Lars T. Hansen设计。它引入了一个新的构造函数 SharedArrayBuffer 和 具有辅助函数的命名空间对象 Atomics。

在我们开始之前,让我们澄清两个相似但截然不同的术语:并行(Parallelism) 和 并发(Concurrency) 。他们存在许多定义,我使用的定义如下

  • 并行(Parallelism) (parallel 并行 vs. serial 串行):同时执行多个任务;
  • 并发(Concurrency) (concurrent 并发 vs. sequential 连续):在重叠的时间段内(而不是一个接一个)执行几个任务。

JS并行的历史

  • JavaScript 在单线程中执行。某些任务可以异步执行:浏览器通常会在单线程中运行这些任务,然后通过回调将结果重新加入到单线程中。
  • Web workers 将任务并行引入了 JavaScript :这些是相对重量级的进程。每个 workers 都有自己的全局环境。默认情况下,不共享任何内容。 workers 之间的通信(或在 workers 和主线程之间的通信)发展:

    • 起初,你只能发送和接收字符串。
    • 然后,引入结构化克隆:可以发送和接收数据副本。结构化克隆适用于大多数数据(JSON 数据,TypedArray,正则表达式,Blob对象,ImageData对象等)。它甚至可以正确处理对象之间的循环引用。但是,不能克隆 error 对象,function 对象和 DOM 节点。
    • 可在 workers 之间的转移数据:当接收方获得数据时,发送方失去访问权限。
  • 通过 WebGL 使用 GPU 计算(它倾向于数据并行处理)

共享数组缓冲区(Shared Array Buffers)

共享阵列缓冲区是更高并发抽象的基本构建块。它们允许您在多个 workers 和主线程之间共享 SharedArrayBuffer 对象的字节(该缓冲区是共享的,用于访问字节,将其封装在一个 TypedArray 中)这种共享有两个好处:

你可以更快地在 workers 之间共享数据。
workers 之间的协调变得更简单和更快(与 postMessage() 相比)。

// main.js
const worker = new Worker('worker.js');

// 要分享的buffer
const sharedBuffer = new SharedArrayBuffer( // (A)
    10 * Int32Array.BYTES_PER_ELEMENT); // 10 elements

// 使用Worker共用sharedBuffer
worker.postMessage({sharedBuffer}); // clone

// 仅限本地使用
const sharedArray = new Int32Array(sharedBuffer); // (B)

创建一个共享数组缓冲区(Shared Array Buffers)的方法与创建普通的数组缓冲区(Array Buffer)类似:通过调用构造函数,并以字节的形式指定缓冲区的大小(行A)。你与 workers 共享的是 缓冲区(buffer) 。对于你自己的本地使用,你通常将共享数组缓冲区封装在 TypedArray 中(行B)。

workers的实现如下所列。

// worker.js
self.addEventListener('message', function (event) {
    const {sharedBuffer} = event.data;
    const sharedArray = new Int32Array(sharedBuffer); // (A)
    // ···
});

sharedArrayBuffer 的 API

构造函数:

  • new SharedArrayBuffer(length)

创建一个 length 字节的 buffer(缓冲区)。

静态属性:

  • get SharedArrayBuffer[Symbol.species]

默认情况下返回 this。 覆盖以控制 slice() 的返回。

实例属性:

  • get SharedArrayBuffer.prototype.byteLength()

返回 buffer(缓冲区) 的字节长度。

  • SharedArrayBuffer.prototype.slice(start, end)

创建一个新的 this.constructor[Symbol.species] 实例,并用字节填充从(包括)开始到(不包括)结束的索引。

Atomics: 安全访问共享数据

举一个例子

// main.js
sharedArray[1] = 11;
sharedArray[2] = 22;

在单线程中,您可以重新排列这些写入操作,因为在中间没有读到任何内容。 对于多线程,当你期望以特定顺序执行写入操作时,就会遇到麻烦:

// worker.js
while (sharedArray[2] !== 22) ;
console.log(sharedArray[1]); // 0 or 11

Atomics 方法可以用来与其他 workers 进行同步。例如,以下两个操作可以让你读取和写入数据,并且不会被编译器重新排列:

  • Atomics.load(ta : TypedArray, index)
  • Atomics.store(ta : TypedArray, index, value : T)

这个想法是使用常规操作读取和写入大多数数据,而 Atomics 操作(load ,store 和其他操作)可确保读取和写入安全。通常,您将使用自定义同步机制,例如锁,其实现基于Atomics。

这是一个非常简单的例子,它总是有效的:

// main.js
console.log('notifying...');
Atomics.store(sharedArray, 0, 123);

// worker.js
while (Atomics.load(sharedArray, 0) !== 123) ;
console.log('notified');

Atomics 的 API

Atomic 函数的主要操作数必须是 Int8Array ,Uint8Array ,Int16Array ,Uint16Array ,Int32Array 或 Uint32Array 的一个实例。它必须包裹一个 SharedArrayBuffer 。

所有函数都以 atomically 方式进行操作。存储操作的顺序是固定的并且不能由编译器或 CPU 重新排序。

加载和存储

  • Atomics.load(ta : TypedArray<T>, index) : T

读取和返回 ta[index] 上的元素,返回数组指定位置上的值。

  • Atomics.store(ta : TypedArray<T>, index, value : T) : T

在 ta[index] 上写入 value,并且返回 value。

  • Atomics.exchange(ta : TypedArray<T>, index, value : T) : T

将 ta[index] 上的元素设置为 value ,并且返回索引 index 原先的值。

  • Atomics.compareExchange(ta : TypedArray<T>, index, expectedValue, replacementValue) : T

如果 ta[index] 上的当前元素为 expectedValue , 那么使用 replacementValue 替换。并且返回索引 index 原先(或者未改变)的值。

简单修改 TypeArray 元素

以下每个函数都会在给定索引处更改 TypeArray 元素:它将一个操作符应用于元素和参数,并将结果写回元素。它返回元素的原始值。

  • Atomics.add(ta : TypedArray<T>, index, value) : T

执行 ta[index] += value 并返回 ta[index] 的原始值。

  • Atomics.sub(ta : TypedArray<T>, index, value) : T

执行 ta[index] -= value 并返回 ta[index] 的原始值。

  • Atomics.and(ta : TypedArray<T>, index, value) : T

执行 ta[index] &= value 并返回 ta[index] 的原始值。

  • Atomics.or(ta : TypedArray<T>, index, value) : T

执行 ta[index] |= value 并返回 ta[index] 的原始值。

  • Atomics.xor(ta : TypedArray<T>, index, value) : T

执行 ta[index] ^= value 并返回 ta[index] 的原始值。

等待和唤醒

  • Atomics.wait(ta: Int32Array, index, value, timeout=Number.POSITIVE_INFINITY) : ('not-equal' | 'ok' | 'timed-out')

如果 ta[index] 的当前值不是 value ,则返回 'not-equal'。否则继续等待,直到我们通过 Atomics.wake() 唤醒或直到等待超时。 在前一种情况下,返回 'ok'。在后一种情况下,返回'timed-out'。timeout 以毫秒为单位。记住此函数执行的操作:“如果 ta[index] 为 value,那么继续等待” 。

  • Atomics.wake(ta : Int32Array, index, count)

唤醒等待在 ta[index] 上的 count workers。

Object.values and Object.entries

Object.values() 方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于for-in循环枚举原型链中的属性 )。

obj参数是需要待操作的对象。可以是一个对象,或者一个数组(是一个带有数字下标的对象,[10,20,30] -> {0: 10,1: 20,2: 30})。

const obj = { x: 'xxx', y: 1 };
Object.values(obj); // ['xxx', 1]

const obj = ['e', 's', '8']; // 相当于 { 0: 'e', 1: 's', 2: '8' };
Object.values(obj); // ['e', 's', '8']

// 当我们使用数字键值时,返回的是数字排序
// 根据键值排序
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.values(obj); // ['yyy', 'zzz', 'xxx']

Object.values('es8'); // ['e', 's', '8']

Object.entries 方法返回一个给定对象自身可遍历属性 [key, value] 的数组, 排序规则和 Object.values 一样。这个方法的声明比较琐碎:

const obj = { x: 'xxx', y: 1 };
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]

const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]

const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]

Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]

String padding

为 String 对象增加了 2 个函数:padStart 和 padEnd。

像它们名字那样,这几个函数的主要目的就是填补字符串的首部和尾部,为了使得到的结果字符串的长度能达到给定的长度。你可以通过特定的字符,或者字符串,或者默认的空格填充它。下面是函数的声明:

str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])

这些函数的第一个参数是 targetLength(目标长度),这个是结果字符串的长度。第二个参数是可选的 padString(填充字符),一个用于填充到源字符串的字符串。默认值是空格。

'es8'.padStart(2);          // 'es8'
'es8'.padStart(5);          // '  es8'
'es8'.padStart(6, 'woof');  // 'wooes8'
'es8'.padStart(14, 'wow');  // 'wowwowwowwoes8'
'es8'.padStart(7, '0');     // '0000es8'

'es8'.padEnd(2);            // 'es8'
'es8'.padEnd(5);            // 'es8  '
'es8'.padEnd(6, 'woof');    // 'es8woo'
'es8'.padEnd(14, 'wow');    // 'es8wowwowwowwo'
'es8'.padEnd(7, '6');       // 'es86666'

Object.getOwnPropertyDescriptors

getOwnPropertyDescriptors 方法返回指定对象所有自身属性的描述对象。属性描述对象是直接在对象上定义的,而不是继承于对象的原型。ES2017加入这个函数的主要动机在于方便将一个对象深度拷贝给另一个对象,同时可以将getter/setter拷贝。声明如下:

Object.getOwnPropertyDescriptors(obj)

obj 是待操作对象。返回的描述对象键值有:configurable, enumerable, writable, get, set and value。

const obj = { 
  get es7() { return 777; },
  get es8() { return 888; }
};
Object.getOwnPropertyDescriptor(obj);
// {
//   es7: {
//     configurable: true,
//     enumerable: true,
//     get: function es7(){}, //the getter function
//     set: undefined
//   },
//   es8: {
//     configurable: true,
//     enumerable: true,
//     get: function es8(){}, //the getter function
//     set: undefined
//   }
// }

结尾逗号

结尾逗号用代码展示非常明了:

// 参数定义时
function foo(
    param1,
    param2,
) {}

// 函数调用时
foo(
    'abc',
    'def',
);

// 对象中
let obj = {
    first: 'Jane',
    last: 'Doe',
};

// 数组中
let arr = [
    'red',
    'green',
    'blue',
];

这个改动有什么好处呢?

  • 首先,重新排列项目更简单,因为如果最后一项更改其位置,则不必添加和删除逗号。
  • 其次,它可以帮助版本控制系统跟踪实际发生的变化。例如,从:
[
    'foo'
]

修改为

[
    'foo',
    'bar'
]

导致线条'foo'和线条'bar'被标记为已更改,即使唯一真正的变化是后一条线被添加。

ES9新特性(ECMAScript 2018)

ECMAScript 2018

ES9的新特性索引如下:

主要新功能:

  • 异步迭代(Domenic Denicola,Kevin Smith)
  • Rest/Spread 属性(SebastianMarkbåge)

新的正则表达式功能:

  • RegExp named capture groups(Gorkem Yakin,Daniel Ehrenberg)
  • RegExp Unicode Property Escapes(Mathias Bynens)
  • RegExp Lookbehind Assertions(Gorkem Yakin,NozomuKatō,Daniel Ehrenberg)
  • s (dotAll) flag for regular expressions(Mathias Bynens)

其他新功能:

  • Promise.prototype.finally() (Jordan Harband)
  • 模板字符串修改(Tim Disney)

异步迭代

首先来回顾一下同步迭代器:

ES6引入了同步迭代器,其工作原理如下:

  • Iterable:一个对象,表示可以通过Symbol.iterator方法进行迭代。
  • Iterator:通过调用iterable [Symbol.iterator] ()返回的对象。它将每个迭代元素包装在一个对象中,并通过其next()方法一次返回一个。
  • IteratorResult:返回的对象next()。属性value包含一个迭代的元素,属性done是true 后最后一个元素。

示例:

const iterable = ['a', 'b'];
const iterator = iterable[Symbol.iterator]();
iterator.next()
// { value: 'a', done: false }
iterator.next()
// { value: 'b', done: false }
iterator.next()
// { value: undefined, done: true }

异步迭代器

先前的迭代方式是同步的,并不适用于异步数据源。例如,在以下代码中,readLinesFromFile()无法通过同步迭代传递其异步数据:

for (const line of readLinesFromFile(fileName)) {
    console.log(line);
}

异步迭代器和常规迭代器的工作方式非常相似,但是异步迭代器涉及promise:

async function example() {
  // 普通迭代器:
  const iterator = createNumberIterator();
  iterator.next(); // Object {value: 1, done: false}
  iterator.next(); // Object {value: 2, done: false}
  iterator.next(); // Object {value: 3, done: false}
  iterator.next(); // Object {value: undefined, done: true}

  // 异步迭代器:
  const asyncIterator = createAsyncNumberIterator();
  const p = asyncIterator.next(); // Promise
  await p;// Object {value: 1, done: false}
  await asyncIterator.next(); // Object {value: 2, done: false}
  await asyncIterator.next(); // Object {value: 3, done: false}
  await asyncIterator.next(); // Object {value: undefined, done: true}
}

异步迭代器对象的next()方法返回了一个Promise,解析后的值跟普通的迭代器类似。
用法:iterator.next().then(({ value, done })=> {//{value: ‘some val’, done: false}}

const promises = [
    new Promise(resolve => resolve(1)),
    new Promise(resolve => resolve(2)),
    new Promise(resolve => resolve(3)),
];

async function test() {
    for await (const p of promises) {
        console.log(p);
    }
}
test(); //1 ,2 3

Rest/Spread 属性

这个就是我们通常所说的rest参数和扩展运算符,这项特性在ES6中已经引入,但是ES6中的作用对象仅限于数组:

restParam(1, 2, 3, 4, 5);

function restParam(p1, p2, ...p3) {
  // p1 = 1
  // p2 = 2
  // p3 = [3, 4, 5]
}

const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100

在ES9中,为对象提供了像数组一样的rest参数和扩展运算符:

const obj = {
  a: 1,
  b: 2,
  c: 3
}
const { a, ...param } = obj;
  console.log(a)     //1
  console.log(param) //{b: 2, c: 3}

function foo({a, ...param}) {
  console.log(a);    //1
  console.log(param) //{b: 2, c: 3}
}

正则表达式命名捕获组

编号的捕获组

//正则表达式命名捕获组
const RE_DATE = /([0-9]{4})-([0-9]{2})-([0-9]{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31

通过数字引用捕获组有几个缺点:

  • 找到捕获组的数量是一件麻烦事:必须使用括号。
  • 如果要了解组的用途,则需要查看正则表达式。
  • 如果更改捕获组的顺序,则还必须更改匹配代码。

命名的捕获组

ES9中可以通过名称来识别捕获组:(?<year>[0-9]{4})

在这里,我们用名称标记了前一个捕获组year。该名称必须是合法的JavaScript标识符(认为变量名称或属性名称)。匹配后,您可以通过访问捕获的字符串matchObj.groups.year来访问。

让我们重写前面的代码:

const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

// 使用解构语法更为简便
const {groups: {day, year}} = RE_DATE.exec('1999-12-31');
console.log(year); // 1999
console.log(day); // 31

可以发现,命名捕获组有以下优点:

  • 找到捕获组的“ID”更容易。
  • 匹配代码变为自描述性的,因为捕获组的ID描述了正在捕获的内容。
  • 如果更改捕获组的顺序,则无需更改匹配代码。
  • 捕获组的名称也使正则表达式更容易理解,因为您可以直接看到每个组的用途。

正则表达式 Unicode 转义

该特性允许您使用\p{}通过提及大括号内的Unicode字符属性来匹配字符,在正则表达式中使用标记 u (unicode) 设置。

/^\p{White_Space}+$/u.test('\t \n\r')
// true
/^\p{Script=Greek}+$/u.test('μετά')
// true

新方法匹配中文字符

由于在Unicode里面,中文字符对应的Unicode Script是Han,于是我们就可以用这个reg来匹配中文:

/\p{Script=Han}/u

这样我们就可以不用记忆繁琐又不好记的/[\u4e00-\u9fa5]/了,况且这个表达式已经有些年头了,说实话,后来又新增的属性为Han的字符并不在这个范围内,因此这个有年头reg并不一定好使。

我随便从网上找了一个Unicode8.0添加的中文字符“𬬭”,我测了一下两种reg的兼容性:

oldReg=/[\u4e00-\u9fa5]/
newReg=/\p{Script=Han}/u

oldReg.test('abc')
// false
newReg.test('abc')
// false

oldReg.test('地平线')
// true
newReg.test('地平线')
// true

oldReg.test('𬬭')
// false
newReg.test('𬬭')
// true

http://www.unicode.org/charts...

可以参考一下这个PDF,是Unicode的汉字全集,从524页9FA6至526页(最后一页)用旧匹配方式都无法生效。

Unicode

一些对于Unicode的科普

  • Name:唯一名称,由大写字母,数字,连字符和空格组成。例如:

    • A: Name = LATIN CAPITAL LETTER A
    • 😀: Name = GRINNING FACE
  • General_Category:对字符进行分类。例如:

    • X: General_Category = Lowercase_Letter
    • $: General_Category = Currency_Symbol
  • White_Space:用于标记不可见的间距字符,例如空格,制表符和换行符。例如:

    • T: White_Space = True
    • π: White_Space = False
  • Age:引入字符的Unicode标准版本。例如:欧元符号€在Unicode标准的2.1版中添加。

    • €: Age = 2.1
  • Script:是一个或多个书写系统使用的字符集合。

    • 有些脚本支持多种写入系统。例如,拉丁文脚本支持英语,法语,德语,拉丁语等书写系统。
    • 某些语言可以用多个脚本支持的多个备用写入系统编写。例如,土耳其语在20世纪初转换为拉丁文字之前使用了阿拉伯文字。
    • 例子:

      • α: Script = Greek
      • Д: Script = Cyrillic

正则表达式的Unicode属性转义

  • 匹配其属性prop具有值的所有字符value:

    \p{prop=value}

  • 匹配所有没有属性prop值的字符value:

    \P{prop=value}

  • 匹配二进制属性bin_prop为True的所有字符:

    \p{bin_prop}

  • 匹配二进制属性bin_prop为False的所有字符:

    \P{bin_prop}

  • 匹配空格:
/^\p{White_Space}+$/u.test('\t \n\r')
//true

匹配字母:

/^\p{Letter}+$/u.test('πüé')
//true

匹配希腊字母:

/^\p{Script=Greek}+$/u.test('μετά')
//true

匹配拉丁字母:

/^\p{Script=Latin}+$/u.test('Grüße')
//true

正则表达式反向断言

先来看下正则表达式先行断言是什么:

如获取货币的符号

const noReLookahead = /\D(\d+)/,
      reLookahead = /\D(?=\d+)/,
      match1 = noReLookahead.exec('$123.45'),
      match2 = reLookahead.exec('$123.45');
console.log(match1[0]); // $123   
console.log(match2[0]); // $

在ES9中可以允许反向断言:

const reLookahead = /(?<=\D)[\d\.]+/;
      match = reLookahead.exec('$123.45');
console.log(match[0]); // 123.45

使用?<=进行反向断言,可以使用反向断言获取货币的价格,而忽略货币符号。

正则表达式dotAll模式

正则表达式中点.匹配除回车外的任何单字符,标记s改变这种行为,允许行终止符的出现,例如:

/hello.world/.test('hello\nworld');  // false
/hello.world/s.test('hello\nworld'); // true

Promise.prototype.finally()

这个基本没什么好讲的,看名字就能看懂了。其用法如下:

promise
  .then(result => {···})
  .catch(error => {···})
  .finally(() => {···});

finally的回调总会被执行。

模板字符串修改

ES2018 移除对 ECMAScript 在带标签的模版字符串中转义序列的语法限制。
之前,u开始一个 unicode 转义,x开始一个十六进制转义,后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:uuuxxx111。

要取消转义序列的语法限制,可在模板字符串之前使用标记函数String.raw:

`\u{54}`
// "T"
String.raw`\u{54}`
// "\u{54}"

尾声

ECMAScript的演化不会停止,但是我们完全没必要害怕。除了ES6这个史无前例的版本带来了海量的信息和知识点以外,之后每年一发的版本都仅仅带有少量的增量更新,一年更新的东西花半个小时就能搞懂了,完全没必要畏惧。

Stay hungry. Stay foolish.

查看原文

Fardwn 赞了文章 · 2019-05-08

细解JavaScript ES7 ES8 ES9 新特性

导言:ECMAScript的演化不会停止,但是我们完全没必要害怕。除了ES6这个史无前例的版本带来了海量的信息和知识点以外,之后每年一发的版本都仅仅带有少量的增量更新,一年更新的东西花半个小时就能搞懂了,完全没必要畏惧。本文将带您花大约一个小时左右的时间,迅速过一遍ES7,ES8,ES9的新特性。

想追求更好的阅读体验,请移步原文地址

es_16_17_18

题记:本文提供了一个在线PPT版本,方便您浏览 细解JAVASCRIPT ES7 ES8 ES9 新特性 在线PPT ver

本文的大部分内容译自作者Axel Rauschmayer博士的网站,想了解更多关于作者的信息,可以浏览Exploring JS: JavaScript books for programmers

那些与ECMAScript有关的事情

谁在设计ECMAScript?

TC39 (Technical Committee 39)。

TC39 是推进 JavaScript 发展的委员会。其会员都是公司(其中主要是浏览器厂商)。TC39 定期召开会议,会议由会员公司的代表与特邀专家出席。会议纪录都可在网上查看,可以让你对 TC39 如何工作有一个清晰的概念。

很有意思的是,TC39 实行的是协商一致的原则:通过一项决议必须得到每一位会员(公司代表)的赞成。

ECMAScript的发布周期

在2015年发布的 ECMAScript(ES6)新增内容很多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化。两个发布版本之间时间跨度如此之大主要有两大原因:

  • 比新版率先完成的特性,必须等待新版的完成才能发布。
  • 那些需要花长时间完成的特性,也顶着很大的压力被纳入这一版本,因为如果推迟到下一版本发布意味着又要等很久,这种特性也会推迟新的发布版本。

因此,从 ECMAScript 2016(ES7)开始,版本发布变得更加频繁,每年发布一个新版本,这么一来新增内容也会更小。新版本将会包含每年截止时间之前完成的所有特性。

ECMAScript的发布流程

每个 ECMAScript 特性的建议将会从阶段 0 开始, 然后经过下列几个成熟阶段。其中从一个阶段到下一个阶段必须经过 TC39 的批准。

  1. stage-0 - Strawman: just an idea, possible Babel plugin.
    任何讨论、想法、改变或者还没加到提案的特性都在这个阶段。只有TC39成员可以提交。

    当前的stage 0列表可以查看这里 --> Stage 0 Proposals

  2. stage-1 - Proposal: this is worth working on.

    什么是 Proposal?一份新特性的正式建议文档。提案必须指明此建议的潜在问题,例如与其他特性之间的关联,实现难点等。

  3. stage-2 - Draft: initial spec.

    什么是 Draft?草案是规范的第一个版本。其与最终标准中包含的特性不会有太大差别。

    草案之后,原则上只接受增量修改。这个阶段开始实验如何实现,实现形式包括polyfill, 实现引擎(提供草案执行本地支持),或者编译转换(例如babel)

  4. stage-3 - Candidate: complete spec and initial browser implementations.

    候选阶段,获得具体实现和用户的反馈。此后,只有在实现和使用过程中出现了重大问题才会修改。至少要在一个浏览器中实现,提供polyfill或者babel插件。

  5. stage-4 - Finished: will be added to the next yearly release.

    已经准备就绪,该特性会出现在下个版本的ECMAScript规范之中。

    当前的stage 1-3列表可以查看这里 --> ECMAScript proposals

已经正式发布的特性索引

ProposalAuthorChampion(s)TC39 meeting notesExpected Publication Year
Array.prototype.includesDomenic DenicolaDomenic Denicola
Rick Waldron
November 20152016
Exponentiation operatorRick WaldronRick WaldronJanuary 20162016
Object.values/Object.entriesJordan HarbandJordan HarbandMarch 20162017
String paddingJordan HarbandJordan Harband
Rick Waldron
May 20162017
Object.getOwnPropertyDescriptorsJordan Harband
Andrea Giammarchi
Jordan Harband
Andrea Giammarchi
May 20162017
Trailing commas in function parameter lists and callsJeff MorrisonJeff MorrisonJuly 20162017
Async functionsBrian TerlsonBrian TerlsonJuly 20162017
Shared memory and atomicsLars T HansenLars T HansenJanuary 20172017
Lifting template literal restrictionTim DisneyTim DisneyMarch 20172018
s (dotAll) flag for regular expressionsMathias BynensBrian Terlson
Mathias Bynens
November 20172018
RegExp named capture groupsGorkem Yakin
Daniel Ehrenberg
Daniel Ehrenberg
Brian Terlson
Mathias Bynens
November 20172018
Rest/Spread PropertiesSebastian MarkbågeSebastian MarkbågeJanuary 20182018
RegExp Lookbehind AssertionsGorkem Yakin
Nozomu Katō
Daniel Ehrenberg
Daniel Ehrenberg
Mathias Bynens
January 20182018
RegExp Unicode Property EscapesMathias BynensBrian Terlson
Daniel Ehrenberg
Mathias Bynens
January 20182018
Promise.prototype.finallyJordan HarbandJordan HarbandJanuary 20182018
Asynchronous IterationDomenic DenicolaDomenic DenicolaJanuary 20182018
Optional catch bindingMichael FicarraMichael FicarraMay 20182019
JSON supersetRichard GibsonMark Miller
Mathias Bynens
May 20182019

ES7新特性(ECMAScript 2016)

ECMAScript 2016

ES7在ES6的基础上主要添加了两项内容:

  • Array.prototype.includes()方法
  • 求幂运算符(**)

Array.prototype.includes()方法

includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。

var array = [1, 2, 3];

console.log(array.includes(2));
// expected output: true

var pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false

Array.prototype.includes()方法接收两个参数:

  • 要搜索的值
  • 搜索的开始索引。

当第二个参数被传入时,该方法会从索引处开始往后搜索(默认索引值为0)。若搜索值在数组中存在则返回true,否则返回false。 且看下面示例:

['a', 'b', 'c', 'd'].includes('b')         // true
['a', 'b', 'c', 'd'].includes('b', 1)      // true
['a', 'b', 'c', 'd'].includes('b', 2)      // false

乍一看,includes的作用跟数组的indexOf重叠,为什么要特意增加这么一个api呢?主要区别有以下几点:

  • 返回值。看一个函数,先看他们的返回值。indexOf的返回数是值型的,includes的返回值是布尔型,所以在if条件判断的时候includes要简单得多,而indexOf 需要多写一个条件进行判断。
var ary = [1];
if (ary.indexOf(1) !== -1) {
    console.log("数组存在1")
}
if (ary.includes(1)) {
    console.log("数组存在1")
}
  • NaN的判断。如果数组中有NaN,你又正好需要判断数组是否有存在NaN,这时你使用indexOf是无法判断的,你必须使用includes这个方法。
var ary1 = [NaN];
console.log(ary1.indexOf(NaN))//-1
console.log(ary1.includes(NaN))//true
  • 当数组的有空的值的时候,includes会认为空的值是undefined,而indexOf不会。
var ary1 = new Array(3);
console.log(ary1.indexOf(undefined));//-1
console.log(ary1.includes(undefined))//true

求幂运算符(**)

加/减法我们通常都是用其中缀形式,直观易懂。在ECMAScript2016中,我们可以使用**来替代Math.pow。

4 ** 3           // 64

效果等同于

Math.pow(4,3)

值得一提的是,作为中缀运算符,**还支持以下操作

let n = 4;
n **= 3;
// 64

ES8新特性(ECMAScript 2017)

ECMAScript 2017

在2017年1月的TC39会议上,ECMAScript 2017的最后一个功能“Shared memory and atomics”推进到第4阶段。这意味着它的功能集现已完成。

ECMAScript 2017特性一览

主要新功能:

  • 异步函数 Async Functions(Brian Terlson)
  • 共享内存和Atomics(Lars T. Hansen)

次要新功能:

  • Object.values / Object.entries(Jordan Harband)
  • String padding(Jordan Harband,Rick Waldron)
  • Object.getOwnPropertyDescriptors() (Jordan Harband,Andrea Giammarchi)
  • 函数参数列表和调用中的尾逗号(Jeff Morrison)

Async Functions

Async Functions也就是我们常说的Async/Await,相信大家对于这个概念都已经不陌生了。Async/Await是一种用于处理JS异步操作的语法糖,可以帮助我们摆脱回调地狱,编写更加优雅的代码。

通俗的理解,async关键字的作用是告诉编译器对于标定的函数要区别对待。当编译器遇到标定的函数中的await关键字时,要暂时停止运行,带到await标定的函数处理完毕后,再进行相应操作。如果该函数fulfiled了,则返回值是fulfillment value,否则得到的就是reject value。

下面通过拿普通的promise写法来对比,就很好理解了:

async function asyncFunc() {
    const result = await otherAsyncFunc();
    console.log(result);
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc()
    .then(result => {
        console.log(result);
    });
}

按顺序处理多个异步函数的时候优势更为明显:

async function asyncFunc() {
    const result1 = await otherAsyncFunc1();
    console.log(result1);
    const result2 = await otherAsyncFunc2();
    console.log(result2);
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc1()
    .then(result1 => {
        console.log(result1);
        return otherAsyncFunc2();
    })
    .then(result2 => {
        console.log(result2);
    });
}

并行处理多个异步函数:

async function asyncFunc() {
    const [result1, result2] = await Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ]);
    console.log(result1, result2);
}

// Equivalent to:
function asyncFunc() {
    return Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ])
    .then([result1, result2] => {
        console.log(result1, result2);
    });
}

处理错误:

async function asyncFunc() {
    try {
        await otherAsyncFunc();
    } catch (err) {
        console.error(err);
    }
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc()
    .catch(err => {
        console.error(err);
    });
}

Async Functions若是要展开去讲,可以占用很大段的篇幅。鉴于本文是一篇介绍性文章,再次不再进行深入。

SharedArrayBuffer和Atomics

,如果之前您没有接触过ArrayBuffer相关知识的话,建议您从内存管理速成教程系列漫画解说入门,强推:
A crash course in memory management
[A cartoon intro to ArrayBuffers and SharedArrayBuffers
](https://hacks.mozilla.org/201...
[Avoiding race conditions in SharedArrayBuffers with Atomics
](https://hacks.mozilla.org/201...


ECMAScript 2017 特性 SharedArrayBuffer 和 atomics”,由Lars T. Hansen设计。它引入了一个新的构造函数 SharedArrayBuffer 和 具有辅助函数的命名空间对象 Atomics。

在我们开始之前,让我们澄清两个相似但截然不同的术语:并行(Parallelism) 和 并发(Concurrency) 。他们存在许多定义,我使用的定义如下

  • 并行(Parallelism) (parallel 并行 vs. serial 串行):同时执行多个任务;
  • 并发(Concurrency) (concurrent 并发 vs. sequential 连续):在重叠的时间段内(而不是一个接一个)执行几个任务。

JS并行的历史

  • JavaScript 在单线程中执行。某些任务可以异步执行:浏览器通常会在单线程中运行这些任务,然后通过回调将结果重新加入到单线程中。
  • Web workers 将任务并行引入了 JavaScript :这些是相对重量级的进程。每个 workers 都有自己的全局环境。默认情况下,不共享任何内容。 workers 之间的通信(或在 workers 和主线程之间的通信)发展:

    • 起初,你只能发送和接收字符串。
    • 然后,引入结构化克隆:可以发送和接收数据副本。结构化克隆适用于大多数数据(JSON 数据,TypedArray,正则表达式,Blob对象,ImageData对象等)。它甚至可以正确处理对象之间的循环引用。但是,不能克隆 error 对象,function 对象和 DOM 节点。
    • 可在 workers 之间的转移数据:当接收方获得数据时,发送方失去访问权限。
  • 通过 WebGL 使用 GPU 计算(它倾向于数据并行处理)

共享数组缓冲区(Shared Array Buffers)

共享阵列缓冲区是更高并发抽象的基本构建块。它们允许您在多个 workers 和主线程之间共享 SharedArrayBuffer 对象的字节(该缓冲区是共享的,用于访问字节,将其封装在一个 TypedArray 中)这种共享有两个好处:

你可以更快地在 workers 之间共享数据。
workers 之间的协调变得更简单和更快(与 postMessage() 相比)。

// main.js
const worker = new Worker('worker.js');

// 要分享的buffer
const sharedBuffer = new SharedArrayBuffer( // (A)
    10 * Int32Array.BYTES_PER_ELEMENT); // 10 elements

// 使用Worker共用sharedBuffer
worker.postMessage({sharedBuffer}); // clone

// 仅限本地使用
const sharedArray = new Int32Array(sharedBuffer); // (B)

创建一个共享数组缓冲区(Shared Array Buffers)的方法与创建普通的数组缓冲区(Array Buffer)类似:通过调用构造函数,并以字节的形式指定缓冲区的大小(行A)。你与 workers 共享的是 缓冲区(buffer) 。对于你自己的本地使用,你通常将共享数组缓冲区封装在 TypedArray 中(行B)。

workers的实现如下所列。

// worker.js
self.addEventListener('message', function (event) {
    const {sharedBuffer} = event.data;
    const sharedArray = new Int32Array(sharedBuffer); // (A)
    // ···
});

sharedArrayBuffer 的 API

构造函数:

  • new SharedArrayBuffer(length)

创建一个 length 字节的 buffer(缓冲区)。

静态属性:

  • get SharedArrayBuffer[Symbol.species]

默认情况下返回 this。 覆盖以控制 slice() 的返回。

实例属性:

  • get SharedArrayBuffer.prototype.byteLength()

返回 buffer(缓冲区) 的字节长度。

  • SharedArrayBuffer.prototype.slice(start, end)

创建一个新的 this.constructor[Symbol.species] 实例,并用字节填充从(包括)开始到(不包括)结束的索引。

Atomics: 安全访问共享数据

举一个例子

// main.js
sharedArray[1] = 11;
sharedArray[2] = 22;

在单线程中,您可以重新排列这些写入操作,因为在中间没有读到任何内容。 对于多线程,当你期望以特定顺序执行写入操作时,就会遇到麻烦:

// worker.js
while (sharedArray[2] !== 22) ;
console.log(sharedArray[1]); // 0 or 11

Atomics 方法可以用来与其他 workers 进行同步。例如,以下两个操作可以让你读取和写入数据,并且不会被编译器重新排列:

  • Atomics.load(ta : TypedArray, index)
  • Atomics.store(ta : TypedArray, index, value : T)

这个想法是使用常规操作读取和写入大多数数据,而 Atomics 操作(load ,store 和其他操作)可确保读取和写入安全。通常,您将使用自定义同步机制,例如锁,其实现基于Atomics。

这是一个非常简单的例子,它总是有效的:

// main.js
console.log('notifying...');
Atomics.store(sharedArray, 0, 123);

// worker.js
while (Atomics.load(sharedArray, 0) !== 123) ;
console.log('notified');

Atomics 的 API

Atomic 函数的主要操作数必须是 Int8Array ,Uint8Array ,Int16Array ,Uint16Array ,Int32Array 或 Uint32Array 的一个实例。它必须包裹一个 SharedArrayBuffer 。

所有函数都以 atomically 方式进行操作。存储操作的顺序是固定的并且不能由编译器或 CPU 重新排序。

加载和存储

  • Atomics.load(ta : TypedArray<T>, index) : T

读取和返回 ta[index] 上的元素,返回数组指定位置上的值。

  • Atomics.store(ta : TypedArray<T>, index, value : T) : T

在 ta[index] 上写入 value,并且返回 value。

  • Atomics.exchange(ta : TypedArray<T>, index, value : T) : T

将 ta[index] 上的元素设置为 value ,并且返回索引 index 原先的值。

  • Atomics.compareExchange(ta : TypedArray<T>, index, expectedValue, replacementValue) : T

如果 ta[index] 上的当前元素为 expectedValue , 那么使用 replacementValue 替换。并且返回索引 index 原先(或者未改变)的值。

简单修改 TypeArray 元素

以下每个函数都会在给定索引处更改 TypeArray 元素:它将一个操作符应用于元素和参数,并将结果写回元素。它返回元素的原始值。

  • Atomics.add(ta : TypedArray<T>, index, value) : T

执行 ta[index] += value 并返回 ta[index] 的原始值。

  • Atomics.sub(ta : TypedArray<T>, index, value) : T

执行 ta[index] -= value 并返回 ta[index] 的原始值。

  • Atomics.and(ta : TypedArray<T>, index, value) : T

执行 ta[index] &= value 并返回 ta[index] 的原始值。

  • Atomics.or(ta : TypedArray<T>, index, value) : T

执行 ta[index] |= value 并返回 ta[index] 的原始值。

  • Atomics.xor(ta : TypedArray<T>, index, value) : T

执行 ta[index] ^= value 并返回 ta[index] 的原始值。

等待和唤醒

  • Atomics.wait(ta: Int32Array, index, value, timeout=Number.POSITIVE_INFINITY) : ('not-equal' | 'ok' | 'timed-out')

如果 ta[index] 的当前值不是 value ,则返回 'not-equal'。否则继续等待,直到我们通过 Atomics.wake() 唤醒或直到等待超时。 在前一种情况下,返回 'ok'。在后一种情况下,返回'timed-out'。timeout 以毫秒为单位。记住此函数执行的操作:“如果 ta[index] 为 value,那么继续等待” 。

  • Atomics.wake(ta : Int32Array, index, count)

唤醒等待在 ta[index] 上的 count workers。

Object.values and Object.entries

Object.values() 方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于for-in循环枚举原型链中的属性 )。

obj参数是需要待操作的对象。可以是一个对象,或者一个数组(是一个带有数字下标的对象,[10,20,30] -> {0: 10,1: 20,2: 30})。

const obj = { x: 'xxx', y: 1 };
Object.values(obj); // ['xxx', 1]

const obj = ['e', 's', '8']; // 相当于 { 0: 'e', 1: 's', 2: '8' };
Object.values(obj); // ['e', 's', '8']

// 当我们使用数字键值时,返回的是数字排序
// 根据键值排序
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.values(obj); // ['yyy', 'zzz', 'xxx']

Object.values('es8'); // ['e', 's', '8']

Object.entries 方法返回一个给定对象自身可遍历属性 [key, value] 的数组, 排序规则和 Object.values 一样。这个方法的声明比较琐碎:

const obj = { x: 'xxx', y: 1 };
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]

const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]

const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]

Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]

String padding

为 String 对象增加了 2 个函数:padStart 和 padEnd。

像它们名字那样,这几个函数的主要目的就是填补字符串的首部和尾部,为了使得到的结果字符串的长度能达到给定的长度。你可以通过特定的字符,或者字符串,或者默认的空格填充它。下面是函数的声明:

str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])

这些函数的第一个参数是 targetLength(目标长度),这个是结果字符串的长度。第二个参数是可选的 padString(填充字符),一个用于填充到源字符串的字符串。默认值是空格。

'es8'.padStart(2);          // 'es8'
'es8'.padStart(5);          // '  es8'
'es8'.padStart(6, 'woof');  // 'wooes8'
'es8'.padStart(14, 'wow');  // 'wowwowwowwoes8'
'es8'.padStart(7, '0');     // '0000es8'

'es8'.padEnd(2);            // 'es8'
'es8'.padEnd(5);            // 'es8  '
'es8'.padEnd(6, 'woof');    // 'es8woo'
'es8'.padEnd(14, 'wow');    // 'es8wowwowwowwo'
'es8'.padEnd(7, '6');       // 'es86666'

Object.getOwnPropertyDescriptors

getOwnPropertyDescriptors 方法返回指定对象所有自身属性的描述对象。属性描述对象是直接在对象上定义的,而不是继承于对象的原型。ES2017加入这个函数的主要动机在于方便将一个对象深度拷贝给另一个对象,同时可以将getter/setter拷贝。声明如下:

Object.getOwnPropertyDescriptors(obj)

obj 是待操作对象。返回的描述对象键值有:configurable, enumerable, writable, get, set and value。

const obj = { 
  get es7() { return 777; },
  get es8() { return 888; }
};
Object.getOwnPropertyDescriptor(obj);
// {
//   es7: {
//     configurable: true,
//     enumerable: true,
//     get: function es7(){}, //the getter function
//     set: undefined
//   },
//   es8: {
//     configurable: true,
//     enumerable: true,
//     get: function es8(){}, //the getter function
//     set: undefined
//   }
// }

结尾逗号

结尾逗号用代码展示非常明了:

// 参数定义时
function foo(
    param1,
    param2,
) {}

// 函数调用时
foo(
    'abc',
    'def',
);

// 对象中
let obj = {
    first: 'Jane',
    last: 'Doe',
};

// 数组中
let arr = [
    'red',
    'green',
    'blue',
];

这个改动有什么好处呢?

  • 首先,重新排列项目更简单,因为如果最后一项更改其位置,则不必添加和删除逗号。
  • 其次,它可以帮助版本控制系统跟踪实际发生的变化。例如,从:
[
    'foo'
]

修改为

[
    'foo',
    'bar'
]

导致线条'foo'和线条'bar'被标记为已更改,即使唯一真正的变化是后一条线被添加。

ES9新特性(ECMAScript 2018)

ECMAScript 2018

ES9的新特性索引如下:

主要新功能:

  • 异步迭代(Domenic Denicola,Kevin Smith)
  • Rest/Spread 属性(SebastianMarkbåge)

新的正则表达式功能:

  • RegExp named capture groups(Gorkem Yakin,Daniel Ehrenberg)
  • RegExp Unicode Property Escapes(Mathias Bynens)
  • RegExp Lookbehind Assertions(Gorkem Yakin,NozomuKatō,Daniel Ehrenberg)
  • s (dotAll) flag for regular expressions(Mathias Bynens)

其他新功能:

  • Promise.prototype.finally() (Jordan Harband)
  • 模板字符串修改(Tim Disney)

异步迭代

首先来回顾一下同步迭代器:

ES6引入了同步迭代器,其工作原理如下:

  • Iterable:一个对象,表示可以通过Symbol.iterator方法进行迭代。
  • Iterator:通过调用iterable [Symbol.iterator] ()返回的对象。它将每个迭代元素包装在一个对象中,并通过其next()方法一次返回一个。
  • IteratorResult:返回的对象next()。属性value包含一个迭代的元素,属性done是true 后最后一个元素。

示例:

const iterable = ['a', 'b'];
const iterator = iterable[Symbol.iterator]();
iterator.next()
// { value: 'a', done: false }
iterator.next()
// { value: 'b', done: false }
iterator.next()
// { value: undefined, done: true }

异步迭代器

先前的迭代方式是同步的,并不适用于异步数据源。例如,在以下代码中,readLinesFromFile()无法通过同步迭代传递其异步数据:

for (const line of readLinesFromFile(fileName)) {
    console.log(line);
}

异步迭代器和常规迭代器的工作方式非常相似,但是异步迭代器涉及promise:

async function example() {
  // 普通迭代器:
  const iterator = createNumberIterator();
  iterator.next(); // Object {value: 1, done: false}
  iterator.next(); // Object {value: 2, done: false}
  iterator.next(); // Object {value: 3, done: false}
  iterator.next(); // Object {value: undefined, done: true}

  // 异步迭代器:
  const asyncIterator = createAsyncNumberIterator();
  const p = asyncIterator.next(); // Promise
  await p;// Object {value: 1, done: false}
  await asyncIterator.next(); // Object {value: 2, done: false}
  await asyncIterator.next(); // Object {value: 3, done: false}
  await asyncIterator.next(); // Object {value: undefined, done: true}
}

异步迭代器对象的next()方法返回了一个Promise,解析后的值跟普通的迭代器类似。
用法:iterator.next().then(({ value, done })=> {//{value: ‘some val’, done: false}}

const promises = [
    new Promise(resolve => resolve(1)),
    new Promise(resolve => resolve(2)),
    new Promise(resolve => resolve(3)),
];

async function test() {
    for await (const p of promises) {
        console.log(p);
    }
}
test(); //1 ,2 3

Rest/Spread 属性

这个就是我们通常所说的rest参数和扩展运算符,这项特性在ES6中已经引入,但是ES6中的作用对象仅限于数组:

restParam(1, 2, 3, 4, 5);

function restParam(p1, p2, ...p3) {
  // p1 = 1
  // p2 = 2
  // p3 = [3, 4, 5]
}

const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100

在ES9中,为对象提供了像数组一样的rest参数和扩展运算符:

const obj = {
  a: 1,
  b: 2,
  c: 3
}
const { a, ...param } = obj;
  console.log(a)     //1
  console.log(param) //{b: 2, c: 3}

function foo({a, ...param}) {
  console.log(a);    //1
  console.log(param) //{b: 2, c: 3}
}

正则表达式命名捕获组

编号的捕获组

//正则表达式命名捕获组
const RE_DATE = /([0-9]{4})-([0-9]{2})-([0-9]{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31

通过数字引用捕获组有几个缺点:

  • 找到捕获组的数量是一件麻烦事:必须使用括号。
  • 如果要了解组的用途,则需要查看正则表达式。
  • 如果更改捕获组的顺序,则还必须更改匹配代码。

命名的捕获组

ES9中可以通过名称来识别捕获组:(?<year>[0-9]{4})

在这里,我们用名称标记了前一个捕获组year。该名称必须是合法的JavaScript标识符(认为变量名称或属性名称)。匹配后,您可以通过访问捕获的字符串matchObj.groups.year来访问。

让我们重写前面的代码:

const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

// 使用解构语法更为简便
const {groups: {day, year}} = RE_DATE.exec('1999-12-31');
console.log(year); // 1999
console.log(day); // 31

可以发现,命名捕获组有以下优点:

  • 找到捕获组的“ID”更容易。
  • 匹配代码变为自描述性的,因为捕获组的ID描述了正在捕获的内容。
  • 如果更改捕获组的顺序,则无需更改匹配代码。
  • 捕获组的名称也使正则表达式更容易理解,因为您可以直接看到每个组的用途。

正则表达式 Unicode 转义

该特性允许您使用\p{}通过提及大括号内的Unicode字符属性来匹配字符,在正则表达式中使用标记 u (unicode) 设置。

/^\p{White_Space}+$/u.test('\t \n\r')
// true
/^\p{Script=Greek}+$/u.test('μετά')
// true

新方法匹配中文字符

由于在Unicode里面,中文字符对应的Unicode Script是Han,于是我们就可以用这个reg来匹配中文:

/\p{Script=Han}/u

这样我们就可以不用记忆繁琐又不好记的/[\u4e00-\u9fa5]/了,况且这个表达式已经有些年头了,说实话,后来又新增的属性为Han的字符并不在这个范围内,因此这个有年头reg并不一定好使。

我随便从网上找了一个Unicode8.0添加的中文字符“𬬭”,我测了一下两种reg的兼容性:

oldReg=/[\u4e00-\u9fa5]/
newReg=/\p{Script=Han}/u

oldReg.test('abc')
// false
newReg.test('abc')
// false

oldReg.test('地平线')
// true
newReg.test('地平线')
// true

oldReg.test('𬬭')
// false
newReg.test('𬬭')
// true

http://www.unicode.org/charts...

可以参考一下这个PDF,是Unicode的汉字全集,从524页9FA6至526页(最后一页)用旧匹配方式都无法生效。

Unicode

一些对于Unicode的科普

  • Name:唯一名称,由大写字母,数字,连字符和空格组成。例如:

    • A: Name = LATIN CAPITAL LETTER A
    • 😀: Name = GRINNING FACE
  • General_Category:对字符进行分类。例如:

    • X: General_Category = Lowercase_Letter
    • $: General_Category = Currency_Symbol
  • White_Space:用于标记不可见的间距字符,例如空格,制表符和换行符。例如:

    • T: White_Space = True
    • π: White_Space = False
  • Age:引入字符的Unicode标准版本。例如:欧元符号€在Unicode标准的2.1版中添加。

    • €: Age = 2.1
  • Script:是一个或多个书写系统使用的字符集合。

    • 有些脚本支持多种写入系统。例如,拉丁文脚本支持英语,法语,德语,拉丁语等书写系统。
    • 某些语言可以用多个脚本支持的多个备用写入系统编写。例如,土耳其语在20世纪初转换为拉丁文字之前使用了阿拉伯文字。
    • 例子:

      • α: Script = Greek
      • Д: Script = Cyrillic

正则表达式的Unicode属性转义

  • 匹配其属性prop具有值的所有字符value:

    \p{prop=value}

  • 匹配所有没有属性prop值的字符value:

    \P{prop=value}

  • 匹配二进制属性bin_prop为True的所有字符:

    \p{bin_prop}

  • 匹配二进制属性bin_prop为False的所有字符:

    \P{bin_prop}

  • 匹配空格:
/^\p{White_Space}+$/u.test('\t \n\r')
//true

匹配字母:

/^\p{Letter}+$/u.test('πüé')
//true

匹配希腊字母:

/^\p{Script=Greek}+$/u.test('μετά')
//true

匹配拉丁字母:

/^\p{Script=Latin}+$/u.test('Grüße')
//true

正则表达式反向断言

先来看下正则表达式先行断言是什么:

如获取货币的符号

const noReLookahead = /\D(\d+)/,
      reLookahead = /\D(?=\d+)/,
      match1 = noReLookahead.exec('$123.45'),
      match2 = reLookahead.exec('$123.45');
console.log(match1[0]); // $123   
console.log(match2[0]); // $

在ES9中可以允许反向断言:

const reLookahead = /(?<=\D)[\d\.]+/;
      match = reLookahead.exec('$123.45');
console.log(match[0]); // 123.45

使用?<=进行反向断言,可以使用反向断言获取货币的价格,而忽略货币符号。

正则表达式dotAll模式

正则表达式中点.匹配除回车外的任何单字符,标记s改变这种行为,允许行终止符的出现,例如:

/hello.world/.test('hello\nworld');  // false
/hello.world/s.test('hello\nworld'); // true

Promise.prototype.finally()

这个基本没什么好讲的,看名字就能看懂了。其用法如下:

promise
  .then(result => {···})
  .catch(error => {···})
  .finally(() => {···});

finally的回调总会被执行。

模板字符串修改

ES2018 移除对 ECMAScript 在带标签的模版字符串中转义序列的语法限制。
之前,u开始一个 unicode 转义,x开始一个十六进制转义,后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:uuuxxx111。

要取消转义序列的语法限制,可在模板字符串之前使用标记函数String.raw:

`\u{54}`
// "T"
String.raw`\u{54}`
// "\u{54}"

尾声

ECMAScript的演化不会停止,但是我们完全没必要害怕。除了ES6这个史无前例的版本带来了海量的信息和知识点以外,之后每年一发的版本都仅仅带有少量的增量更新,一年更新的东西花半个小时就能搞懂了,完全没必要畏惧。

Stay hungry. Stay foolish.

查看原文

赞 55 收藏 45 评论 1

Fardwn 发布了文章 · 2019-04-19

前端的模块管理(RequireJS、AMD、CMD、CommonJS、ES6)

随着应用复杂度的攀升,常规的JavaScript引入方式(script标签)已经不能满足需求,模块化管理成为团队协作的高效方法,现在的模块化管理主要有RequireJS、AMD、CMD、CommonJS、ES6四种模式。

RequireJS是一个小巧的JavaScript模块载入框架,压缩后14k,具有轻量级、异步加载模块等特点。

AMD是Asynchronous Module Definition的缩写,是模块加载规范,提倡提前加载、依赖前置,RequireJS就是AMD是实践者。

CMD是国内发展起来,Common Module Definition的缩写,提倡延迟加载、依赖后置。

CommonJS主要用于服务器端模块管理,是同步加载模块,因为服务器端程序(nodejs)和模块都运行在磁盘上,加载延迟可以忽略,客户端使用需考虑网络延迟,效果一般。

ES6通过import和export关键字,旨在建立客户端和服务器端通用的加载规范。

查看原文

赞 1 收藏 0 评论 0

Fardwn 发布了文章 · 2019-04-19

webpack原理及使用解析

webpack是JavaScript前端静态资源打包器(module bundler)。

一、实例应用
①首先安装webpack(可以全局安装,也可以局部安装)

全局:cnpm install -g webpack
局部:cnpm install --save-dev webpack

②安装webpack-cli(webpack4+需要,用于在命令行运行webpack)

npm install --save-dev webpack-cli

③创建package.json文件(NPM的标准说明文件,包含项目的基本信息、模块依赖、运行的脚本信息)

cnpm init(执行时,需要数据项目的相关信息,可跳过)
cnpm init -y(全都默认)

④创建webpack.config.js文件(webpack的配置文件,需手动创建,可在任何位置,一般在整个文件夹的首层子目录)

一个简单的webpack.config.js配置:

module.exports = {
  entry:  __dirname + "/src/main.js",//已多次提及的唯一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js"//打包后输出文件的文件名
  }
}

⑤入口文件main.js(作为入口文件,里面写入需要依赖的模块。。。链式依赖)

一个简单的main.js文件:

const greeter = require('./Greeter.js');//main.js依赖的模块,Greeter.js依次链式依赖下去
document.querySelector("#root").appendChild(greeter());

⑥执行打包
运行webpack命令,即可执行webpack.config.js
简洁的执行打包命令,需要在pakeage.json中的script属性中配置:
一个简单的package.json配置文件

{
  "name": "webpack-sample-project",
  "version": "1.0.0",
  "description": "Sample webpack project",
  "scripts": {
    "start": "webpack" // 修改的是这里,JSON文件不支持注释,引用时请清除
  },
  "author": "zhang",
  "license": "ISC",
  "devDependencies": {
    "webpack": "3.10.0"
  }
}

npm的start命令是个特殊的命令,可直接执行npm start,一般情况下执行脚本命令的格式是 npm run {script name}例如npm run bulid。

二、webpack打包原理和组成。

webpack由四个核心概念:入口(entry)、出口(output)、loader、插件(plugins)。

entry:表示webpack打包开始的入口文件。
output:标识webpack打包结束的导出文件(路径和文件名)。
loader:转换非JavaScript文件(webpack只能识别js文件),如Typescript、css、scss等,可配置对某一文件类型做具体的loader转化。
plugins:可以执行范围更广的任务(后续更新……)
module:可以选择development和production模式,webpack内部会进行相应的优化。

项目中使用
1⃣️本地调试,配置跨域请求
vue2:
config-->index.js配置

module.exports = {
    dev: {
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        proxyTable: {
            '/baseURL/': {
                target: 'http://110.221.9.210:8090',//服务器域名
                changeOrigin: true,
                pathRewrite: {
                    '^/baseURL': 'baseURL'
                }
            }
        },
        host: 'localhost',
        port: 8080,
    }
}

build-->webpack.base.conf.js配置

module.exports = {
    entry: {},
    output: {},
    resolve: {
        extensions: ['.js','.vue','.json'],
        alias: {
            '@': resolve('src')//resolve属于path模块,path.resolve()
        }
    }
}

vue3:
vue.config.js配置

let path = require('path');
function resolve(dir){
    return path.join(_dirname, dir)
}
module.exports = {
    devServer: {
        proxy: {
            '/baseURL': {
                target: 'http://110.221.9.210:8090',//服务器域名
                changeOrigin: true,
                pathRewrite: {
                    '^/baseURL': 'baseURL'
                }
            }
        }
    },
    chainWebpack: config => {
        config.resolve.alias
        .set('@',resolve('src'))
    }
}

其中axios.default.baseURL = '/baseURL'
请求接口实例:
this.$axios.get('/urlLev1/urlLev2/info.json').then(……)
浏览器请求链接:http://110.221.9.210:8090/baseURL/urlLev1/urlLev2/info.json

查看原文

赞 0 收藏 0 评论 0

Fardwn 发布了文章 · 2019-03-27

ES6特性总结

一、对象的扩展

1、ES6中做了简化:

传统对象是键值对的集合。

①初始化时key和value名字相同,可简写

function createPerson(name, age){
    retrun {
        name,
        age
    }
}

②属性值为方法,可省略冒号和function关键字(vue中的用法)

methods:{
    getName(){
        console.log('name')
    },
    getAge(){
        console.log('age')
    }
}

2、属性名可计算

之前的版本,可通过[]来访问名字为变量的属性,此种方法的前提是属性变量名已知。

ES6中属性名是可计算的

let obj = {},
    originArr=[{ name: "jack", age: "19"}, { name: "tom", age: "20"}];
for(let i=0;i<originArr.length; i++){
    obj['first-' + originArr[i].name] =   originArr[i].age 
}
console.log(obj)//{ 'first-jack': '19', 'first-tom': '20' }

3、新增方法

①比较值得方法(Object.is())

判定NaN和NaN相等:Object.is(NaN, NaN) //true
判定+0和-0不等:Object.is(+0, -0) //false(+0和-0在JavaScript引擎中是两个完全不同的实体)

②对象的混合(Object.assign())

let objA = { name: 'kit', age: '21'},
    objB = { sex: 'meal', height: '180'};
Object.assign(objA, objB);
console.log(objA) //{ name: 'kit', age: '21', sex: 'meal', height: '180' }
console.log(objB) //{ sex: 'meal', height: '180' }
查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 34 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-08-28
个人主页被 269 人浏览