sugelawa

sugelawa 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

sugelawa 赞了问题 · 2019-10-04

使用k8s容器管理,微服务注册与发现还需要自己引入zookeeper还是consul吗?

服务跟服务之间都是通过rpc调用

现在的使用方法是config文件里面填写的是k8s的service名称,然后rpc的地址是获取service的这个地址,至于分发的话是由k8来决定的

我想知道如果使用k8的话,还有必要手动的去注册或者管理一个注册中心吗?比如引入走哦keeper或者consul?

关注 2 回答 0

sugelawa 赞了文章 · 2019-10-01

手把手教你在Nginx上使用CertBot

前言

自己做了一个iOS App,需要访问自己的网站获取数据,但是系统默认只能直接访问https的网站。不想让应用改用http的服务。因此,研究如何启用https,本文即是介绍如何在CentOS上配合Nginx使用CertBot。

环境

  • CentOS(CentOS Linux release 7.2.1511

  • Nginx(nginx version: nginx/1.6.3

  • ExpressJS应用

安装CertBot

命令行,键入:

sudo yum install epel-release
sudo yum install certbot

配置Nginx

这里我不想使用CertBot的standalone模式,这个模式虽然可以配置好服务器,但是以后Renew的时候,需要让服务停止一下,再启动。因此抛弃这个模式,我们使用Webroot配置模式。

因为,CertBot在验证服务器域名的时候,会生成一个随机文件,然后CertBot的服务器会通过HTTP访问你的这个文件,因此要确保你的Nginx配置好,以便可以访问到这个文件。

修改你的服务器配置,在server模块添加:

location ^~ /.well-known/acme-challenge/ {
   default_type "text/plain";
   root     /usr/share/nginx/html;
}

location = /.well-known/acme-challenge/ {
   return 404;
}

可以看到,上面的root,我们让他指向了/usr/share/nginx/html,因为我的应用是通过NodeJSExpressJS写的,如果修改源代码的话,比较麻烦。因此我就让检验的链接指向了nginx默认的文件夹下。

接着重新加载Nginx配置:

sudo service nginx reload

然后在命令行输入:

sudo certbot certonly --webroot -w /usr/share/nginx/html/ -d your.domain.com

上面记得替换your.domain.com为你自己的域名。

如果提示:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/your.domain.com/fullchain.pem. Your cert
   will expire on 20XX-09-23. To obtain a new or tweaked version of
   this certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

证书生成成功!

启用443端口

同样,修改Nginx的虚拟主机配置文件,新建一个443端口的server配置:

server {
        listen 443 ssl;
        listen [::]:443 ssl ipv6only=on;

        ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;
        ssl_trusted_certificate /etc/letsencrypt/live/your.domain.com/chain.pem;
        
        // ... other settings ...
}

上面记得替换your.domain.com为你自己的域名。

接着重新加载Nginx配置:

sudo service nginx reload

现在通过浏览器访问你的网站:https://your.domain.com试试,如果看到浏览器的绿色标志,恭喜你设置成功!

不过由于这个证书的时效只有90天,我们需要设置自动更新的功能,帮我们自动更新证书的时效。

自动更新证书

先在命令行模拟证书更新:

sudo certbot renew --dry-run

模拟更新成功的效果如下:

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/your.domain.com.conf
-------------------------------------------------------------------------------
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/your.domain.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)

既然模拟成功,我们就使用crontab -e的命令来启用自动任务,命令行:

sudo crontab -e

添加配置:

30 2 * * 1 /usr/bin/certbot renew  >> /var/log/le-renew.log

上面的执行时间为:每周一半夜2点30分执行renew任务。

你可以在命令行执行/usr/bin/certbot renew >> /var/log/le-renew.log看看是否执行正常,如果一切OK,那么我们的配置到此结束!

参考

查看原文

赞 16 收藏 48 评论 7

sugelawa 关注了专栏 · 2019-09-23

数人云

数人云成立于2014年,创始团队来自谷歌、红帽和惠普,作为基于开源技术的微服务On PaaS领导者,数人云推出的微服务On PaaS ,基于容器技术,通过企业应用架构管理体系EAMS,完美对接云原生、微服务主流开发框架Spring Cloud,为客户落地生产级可用的微服务架构体系。

关注 95

sugelawa 赞了文章 · 2019-09-23

Qcon2017实录|Service Mesh:下一代微服务

Markdown

▲扫码报名活动

数人云11月Meetup报名开启,看中西方大神如何论道云原生与微服务!本文作者敖小剑老师将在本次Meetup上继续分享Service Mesh相关内容,欢迎报名~

数人云之前给大家分享过敖小剑老师的《万字解读:Service Mesh服务网格新生代--Istio》,详细地阐述了发展及理念,在Qcon2017上,敖小剑老师又做了关于Service Mesh的演讲,以下是本次演讲的实录。

敖小剑/数人云资深架构师

十五年软件开发经验,微服务专家,专注于基础架构,Cloud Native拥护者,敏捷实践者。曾在亚信,爱立信,唯品会和ppmoney任职。

Markdown

简单回顾一下过去三年微服务的发展历程。在过去三年当中,微服务成为我们的业界技术热点,我们看到大量的互联网公司都在做微服务架构的落地。也有很多传统企业在做互联网技术转型,基本上还是以微服务和容器为核心。

在这个技术转型中,我们发现有一个大的趋势,伴随着微服务的大潮,Spring Cloud微服务开发框架非常普及。而今天讲的内容在Spring Cloud之外,我们发现最近新一代的微服务开发技术正在悄然兴起,就是今天要给大家带来的Service Mesh/服务网格。

我做一个小的调查,今天在座的各位,有没有之前了解过服务网格的,请举手。(备注:调查结果,现场数百人仅有3个人举手)

既然大家都不了解,那我给大家介绍,首先,什么是Service Mesh?然后给大家讲一下Service Mesh的演进历程,以及为什么选择Service Mesh。为什么我将它称之为下一代的微服务,这是我们今天的内容。

什么是Service Mesh?

我们首先可以说一下Service Mesh,确实是一个非常非常新的名词,像刚才调查的,大部分的同学都没听过。

Markdown

我们首先说一下Service Mesh这个词,这确实是一个非常非常新的名词,像刚才调查的,大部分的同学都没听过。
这个词最早使用由开发Linkerd的Buoyant公司提出,并在内部使用。2016年9月29日第一次公开使用这个术语。2017年的时候随着Linkerd的传入,Service Mesh进入国内技术社区的视野。最早翻译为“服务啮合层”,这个词比较拗口。用了几个月之后改成了服务网格。后面我会给大家介绍为什么叫网格。

Markdown

先看一下Service Mesh的定义,这个定义是由Linkerd的CEO William给出来的。Linkerd是业界第一个Service Mesh,也是他们创造了Service Mesh这个词汇的,所以这个定义比较官方和权威。
我们看一下中文翻译,首先服务网格是一个基础设施层,功能在于处理服务间通信,职责是负责实现请求的可靠传递。在实践中,服务网格通常实现为轻量级网络代理,通常与应用程序部署在一起,但是对应用程序透明。

这个定义直接看文字大家可能会觉得比较空洞,不太容易理解到底是什么。我们来看点具体的东西。

Markdown

Service Mesh的部署模型,先看单个的,对于一个简单请求,作为请求发起者的客户端应用实例,会首先用简单方式将请求发送到本地的Service Mesh实例。这是两个独立进程,他们之间是远程调用。
Service Mesh会完成完整的服务间调用流程,如服务发现负载均衡,最后将请求发送给目标服务。这表现为Sidecar。

Markdown

Sidecar这个词中文翻译为边车,或者车斗,也有一个乡土气息浓重的翻译叫做边三轮。Sidecar这个东西出现的时间挺长的,它在原有的客户端和服务端之间加多了一个代理。

Markdown

多个服务调用的情况,在这个图上我们可以看到Service Mesh在所有的服务的下面,这一层被称之为服务间通讯专用基础设施层。Service Mesh会接管整个网络,把所有的请求在服务之间做转发。在这种情况下,我们会看到上面的服务不再负责传递请求的具体逻辑,只负责完成业务处理。服务间通讯的环节就从应用里面剥离出来,呈现出一个抽象层。

Markdown

如果有大量的服务,就会表现出来网格。图中左边绿色方格是应用,右边蓝色的方框是Service Mesh,蓝色之间的线条是表示服务之间的调用关系。Sidecar之间的连接就会形成一个网络,这个就是服务网格名字的由来。这个时候代理体现出来的就和前面的sidecar不一样了,形成网状。

Markdown

再来回顾前面给出的定义,大家回头看这四个关键词。首先第一个,服务网格是抽象的,实际上是抽象出了一个基础设施层,在应用之外。其次,功能是实现请求的可靠传递。部署上体现为轻量级的网络代理。最后一个关键词是,对应用程序透明。
大家注意看,上面的图中,网络在这种情况下,可能不是特别明显。但是如果把左边的应用程序去掉,现在只呈现出来Service Mesh和他们之间的调用,这个时候关系就会特别清晰,就是一个完整的网络。这是Service Mesh定义当中一个非常重要的关键点,和Sidecar不相同的地方:不再将代理视为单独的组件,而是强调由这些代理连接而形成的网络。在Service Mesh里面非常强调代理连接组成的网络,而不像sidecar那样看待个体。

现在我们基本上把Service Mesh的定义介绍清楚了,大家应该可以大概了解什么是Service Mesh了。

Service Mesh的演进历程

第二个部分和大家追溯一下Service Mesh的演进历程。要注意,虽然Service Mesh这个词汇直到2016年9才有,但是它表述的东西很早以前就出现了。

Markdown

首先看“远古时代”,第一代网络计算机系统,最早的时候开发人员需要在自己的代码里处理网络通讯的细节问题,比如说数据包顺序、流量控制等等,导致网络逻辑和业务逻辑混杂在一起,这样是不行的。接下来出现了TCP/IP技术,解决了流量控制问题,从右边的图上可以看到,功能其实没发生变化:所有的功能都在,代码还是要写。但是,最重要的事情,流程控制,已经从应用程序里面抽出来了。对比左右两边的图,抽出来之后被做成了操作系统网络层的一部分,这就是TCP/IP,这样的话应用的结构就简单了。
现在写应有,就不用考虑网卡到底怎么发。在TCP/IP之后,这是完全不需要考虑的。上面说的是非常遥远的事情,大概发生在五十年前。

Markdown

微服务时代也面临着类似的一些东西,比如说我们在做微服务的时候要处理一系列的比较基础的事情,比如说常见的服务注册、服务发现,在得到服务器实例之后做负载均衡,为了保护服务器要熔断/重试等等。这些功能所有的微服务都跑不掉,那怎么办呢?只能写代码,把所有的功能写进来。我们发现最早的微服务又和刚才一样,应用程序里面又加上了大量的非功能性的代码。为了简化开发,我们开始使用类库,比如说典型的Netflix OSS套件。在把这些事情做好以后,开发人员的编码问题就解决了:只需要写少量代码,就可以把这些功能实现。因为这个原因,最近这些年大家看到Java社区Spring Cloud的普及程度非常快,几乎成为了微服务的代名词。
到了这个地步之后,完美了吗?当然,如果真的完美了,那我今天就不会站在这里了:)

Markdown

我们看这几个被称之为痛点的东西:内容比较多,门槛比较高。调查一下,大家学Spring Cloud,到你能熟练掌握,并且在产品当中应用,可以解决出现的问题,需要多长时间?一个星期够不够?大部分人一个星期是不够的,大部分人需要三到六个月。因为你在真实落地时会遇到各种问题,要能自己解决的话,需要的时间是比较长的。这里是Spring Cloud的常见子项目,只列出了最常见的部分,其中Spring Cloud Netflix下还有Netflix OSS套件的很多内容。要真正吃透Spring Cloud,需要把这些东西全部吃透,否则遇到问题时还会非常难受。
这么多东西,在座的各位相对来说学习能力比较强一点,可能一个月就搞定了,但是问题是你的开发团队,尤其是业务开发团队需要多久,这是一个很要命的事情:业务团队往往有很多比较初级的同事。

Markdown

然后事情并不止这么简单,所谓雪上加霜,我们还不得不面对一堆现实。

  • 比如首先,我们的业务开发团队的强项是什么?最强的会是技术吗?不,通常来说我们的业务开发团队最强的是对业务的理解,是对整个业务体系的熟悉程度。
  • 第二个事情,业务应用的核心价值是什么?我们辛辛苦苦写了这么多的微服务,难道是为了实现微服务吗?微服务只是我们的手段,我们最终需要实现的是业务,这是我们真正的目标。
  • 第三个事情是,就微服务这个手段而言,有比学习微服务框架更艰巨的挑战。在做微服务的真正落地时,会有更深刻的理解。比如微服务的拆分,比如要设计一个良好的API,要保持稳定并且易于扩展,还有如果涉及到跨多个服务的数据一致性,大部分团队都会头疼。最后是康威定律,但凡做服务的同学最终都会遇到这个终极问题,而大多数情况下是欲哭无泪。

但是这些还没完,比你写一个新的微服务系统更痛苦的事情,是你要对旧有的系统进行微服务改造。

所有这些加在一起,还不够,还要再加一条,这条更要命:业务开发团队往往业务压力非常大,时间人力永远不足。说下月上线就是下月上线,说双十一促销就不会推到双十二。老板是不会管你有没有时间学习spring cloud的,也不会管你的业务团队能否搞得定微服务的方方面面。业务永远看的是结果。

Markdown

第二个痛点,功能不够,这里列出了服务治理的常见功能。而Spring Cloud的治理功能是不够强大的,如果把这些功能一一应对做好,靠Spring Cloud直接提供的功能是远远不够的。很多功能都需要你在Spring Cloud的基础上自己解决。

问题是你打算投入多少时间人力资源来做这个事情。有些人说我大不了有些功能我不做了,比如灰度,直接上线好了,但是这样做代价蛮高的。

Markdown

第三个痛点,跨语言。微服务在刚开始面世的时候,承诺了一个很重要的特性:就是不同的微服务可以采用自己最擅长最喜欢的最适合的编程语言来编写,这个承诺只能说有一半是OK的,但是另外一半是不行的,是假的。因为你实现的时候,通常来说是基于一个类库或者框架来实现的,一旦开始用具体编程语言开始编码的时候你就会发现,好像不对了。为什么?左边是我从编程语言排行列表列出来的主流编程语言,排在前面的几种,大家比较熟悉.后面还有几十种没有列出来,中间是新兴的编程语言,比较小众一点。

现在的问题在于我们到底要为多少种语言提供类库和框架。

这个问题非常尖锐,为了解决这个问题,通常只有两条路可选:

  1. 一种就是统一编程语言,全公司就用一种编程语言
  2. 另外一个选择,是有多少种编程语言就写多少个类库

我相信在座的如果有做基础架构的同学,就一定遇到过这个问题。

Markdown

但是问题还没完,框架写好了,也有能够把各个语言都写一份。但是接下来会有第四个痛点:版本升级。
你的框架不可能一开始就完美无缺,所有功能都齐备,没有任何BUG,分发出去之后就再也不需要改动,这种理想状态不存在的。必然是1.0、2.0、3.0慢慢升级,功能逐渐增加,BUG逐渐被修复。但是分发给使用者之后,使用者会不会立马升级?实际上做不到的。

这种情况下怎么办,会出现客户端和服务器端版本不一致,就要非常小心维护兼容性,然后尽量督促你的使用者:我都是3.0了,你别用1.0了,你赶紧升级吧。但是如果如果他不升级,你就继续忍着,然后努力解决你的版本兼容性。

版本兼容性有多复杂?服务端数以百计起,客户端数以千计起,每个的版本都有可能不同。这是一个笛卡尔乘积。但是别忘了,还有一个前面说的编程语言的问题,你还得再乘个N!

设想一下框架的Java1.0客户端访问node.js的3.0的服务器端会发生什么事情,C++的2.0客户端访问golang的1.0服务器端会如何?你想把所有的兼容性测试都做一遍吗?这种情况下你的兼容性测试需要写多少个Case,这几乎是不可能的。

Markdown

那怎么办?怎么解决这些问题,这是现实存在的问题,总是要面对的。

我们来想一想:

  • 第一个是这些问题的根源在哪里:我们做了这么多痛苦的事情,面临这么多问题,这些多艰巨的挑战,这些和服务本身有关系吗?比如写一个用户服务,对用户做CRUD操作,和刚才说的这些东西有一毛钱关系吗?发现有个地方不对,这些和服务本身没关系,而是服务间的通讯,这才是我们需要解决的问题。
  • 然后看一下我们的目标是什么。我们前面所有的努力,其实都是为了保证将客户端发出的业务请求,发去一个正确的地方。什么是正确的地方?比如说有版本上的差异,应该去2.0版本,还是去1.0版本,需要用什么样的负载均衡,要不要做灰度。最终这些考虑,都是让请求去一个你需要的正确的地方。
  • 第三个,事情的本质。整个过程当中,这个请求是从来不发生更改的。比如我们前面说的用户服务,对用户做CRUD,不管请求怎么走,业务语义不会发生变化。这是事情的本质,是不发生变化的东西。
  • 这个问题具有一个高度的普适性:所有的语言,所有的框架,所有的组织,这些问题对于任何一个微服务都是相同的。

讲到这里,大家应该有感觉了:这个问题是不是和哪个问题特别像?

Markdown

五十年前的前辈们,他们要解决的问题是什么?为什么会出现TCP,TCP解决了什么问题?又是怎么解决的?
TCP解决的问题和这个很像,都是要将请求发去一个正确的地方。所有的网络通讯,只要用到TCP协议,这四个点都是一致的。

有了TCP之后会发生什么? 我们有了TCP之后,我们基于TCP来开发我们的应用,我们的应用需要做什么事情? 我们的应用需要关心TCP层下链路层的实现吗?不需要。同理,我们基于HTTP开发应用时,应用需要关心TCP层吗?

Markdown

为什么我们开发微服务应用的时候就要这么关心服务的通讯层?我们把服务通讯层所有的事情学一遍,做一遍,我们做这么多是为什么?

Markdown

这种情况下,自然产生了另外一个想法:既然我们可以把网络访问的技术栈向下移为TCP,我们是可以也有类似的,把微服务的技术栈向下移?
最理想的状态,就是我们在网络协议层中,增加一个微服务层来完成这个事情。但是因为标准问题,所以现在没有实现,暂时这个东西应该不太现实,当然也许未来可能出现微服务的网络层。

之前有一些先驱者,尝试过使用代理的方案,常见的nginx,haproxy,apache等代理。这些代码和微服务关系不大,但是提供了一个思路:在服务器端和客户端之间插入了一个东西完成功能,避免两者直接通讯。当然代理的功能非常简陋,开发者一看,想法不错,但是功能不够,怎么办?

Markdown

这种情况下,第一代的Sidecar出现了,Sidecar扮演的角色和代理很像,但是功能就齐全很多,基本上原来微服务框架在客户端实现的功能都会对应实现。
第一代的Sidecar主要是列出来的这几家公司,其中最有名气的还是Netflix。

在这个地方我们额外提一下,注意第四个,前面三个功能都是国外的公司,但是其实Sidecar这个东西并不是只有国外的人在玩,国内也有厂商和公司在做类似的事情。比如唯品会,我当年在唯品会基础架构部工作的时候,在2015年上半年,我们的OSP服务化框架做了一个重大架构调整,加入了一个名为Local Proxy的Sidecar。注意这个时间是2015上半年,和国外差不多。相信国内肯定还有类似的产品存在,只是不为外界所知。

Markdown

这个时期的Sidecar是有局限性的,都是为特定的基础设施而设计,通常是和当时开发Sidecar的公司自己的基础设施和框架直接绑定的,在原有体系上搭出来的。这里面会有很多限制,一个最大的麻烦是无法通用:没办法拆出来给别人用。比如Airbnb的一定要用到Zookeeper,Netflix的一定要用Eureka,唯品会的Local Proxy是绑死在Osp框架和其他基础设施上的。
之所以出现这些绑定,主要原因还是和这些Sidecar出现的动机有关。比如Netflix是为了让非JVM语言应用接入到Netflix OSS中,Soundcloud是为了让遗留的Ruby应用可以使用到JVM的基础设置。而当年我们唯品会的OSP框架,Local Proxy是为了解决非Java语言接入,还有前面提到的业务部门不愿意升级的问题。这些问题都比较令人头疼的,但是又不得不解决,因为逼的憋出来Sidecar这个一个解决方式。

因为有这样的特殊的背景和需求,所以导致第一代的Sidecar无法通用,因为它本来就是做在原有体系之上的。虽然不能单独拿出来,但是在原有体系里面还是可以很好工作的,因此也没有动力做剥离。导致虽然之前有很多公司有Sidecar这个东西,但是其实一直没怎么流传出来,因为即使出来以后别人也用不上。

这里提一个事情,在2015年年中的时候,我们当时曾经有一个想法,将Local Proxy从OSP剥离,改造为通用的Sidecar。计划支持HTTH1.1,操作Http Header就可以,Body对我们是可以视为透明的,这样就容易实现通用了。可惜因为优先级等原因未能实现,主要是有大量的其他工作比如各种业务改造,这个事情必要性不够。

所有比较遗憾,当时我们有这个想法没做实现,这是在2015年,时间点非常早的了。如果当时有实现,很可能我们就自己折腾出业界第一个Service Mesh出来了。现在想想挺遗憾的。

Markdown

但是,不只有我们会有这想法。还有有一些人想法和我们差不多,但是比较幸运的是,他们有机会把东西做出来了。这就是第一代的Service Mesh,通用性的Sidecar。
通用型的Service Mesh的出现,左边第一个Linkerd是业界第一个Service Mesh,也就是它创造了Service Mesh这个词。时间点:2016年1月15号,0.0.7发布,这是Github上看到的最早的一个版本,其实这个版本离我们当时的有想法的时间点非常近。然后是1.0版本,2017年4月份发布,离现在六个月。所以说,Service Mesh是一个非常新的名词,大家没听过非常正常。

接下来是Envoy,2016年发布的1.0版本。

这里面要特别强调,Linkerd和Envoy都加入了CNCF,Linkerd在今年1月份,而Envoy进入的时间是9月份,离现在也才1个月。在座的各位应该都明白CNCF在Cloud Native领域是什么江湖地位吧?可以说CNCF在Cloud Native的地位,就跟二战后联合国在国际秩序中的地位一样。

之后出现了第三个Service Mesh,Nginmesh,来自于大家熟悉的Nginx,2017年9月发布了第一个版本。因为实在太新,还在刚起步,没什么可以特别介绍的。

Markdown

我们来看一下Service Mesh和Sidecar的差异,前面两点是已经提到了:

  • 首先Service Mesh不再视为单独的组件,而是强调连接形成的网络
  • 第二Service Mesh是一个通用组件

然后要强调的是第三点,Sidecar是可选的,容许直连。通常在开发框架中,原生语言的客户端喜欢选择直连,其他语言选择走Sidecar。比如Java写的框架,Java客户端直连,Php客户端走Sidecar。但是也可以都选择走Sidecar,比如唯品会的OSP就是所有语言都走Local Proxy。在Sidecar中也是可选的。但是,Service Mesh会要求完全掌控所有流量,也就是所有的请求都必须通过Service Mesh。

Markdown

接下来给大家介绍Istio,这个东西我给它的评价是王者风范,来自于谷歌、IBM和Lyft,是Service Mesh的集大成者。
大家看它的图标,就是一个帆船。Istio是希腊语,英文语义是”Sail”, 翻译过来是起航的意思。大家看这个名字和图标有什么联想?Google在云时代的另外一个现象级产品,K8S,Kubernete也同样起源于希腊语,船长,驾驶员或者舵手,图标是一个舵。

Istio名字和图标与K8s可以说是一脉相承的。这个东西在2017年5月份发布了0.1,就在两周前的10月4号发布了0.2。大家都熟悉软件开发,应该明白0.1/0.2在软件迭代中是什么阶段。0.1大概相当于婴儿刚刚出世,0.2还没断奶。但是,即使在这么早期的版本中,我对他的评价已经是集大成者,王者风范,为什么?

Markdown

为什么说Istio王者风范?最重要的是他为Service Mesh带来了前所未有的控制力。以Sidecar方式部署的Service Mesh控制了服务间所有的流量,只要能够控制Service Mesh就能够控制所有的流量,也就可以控制系统中的所有请求。为此Istio带来了一个集中式的控制面板,让你实现控制。
左边是单个视图,在sidecar上增加了控制面板来控制sidecar。这个图还不是特别明显,看右边这个图,当有大量服务时,这个服务面板的感觉就更清晰一些。在整个网络里面,所有的流量都在Service Mesh的控制当中,所有的Service Mesh都在控制面板控制当中。可以通过控制面板控制整个系统,这是Istio带来的最大的革新。

Markdown

Istio由三个公司开发,前两个比较可怕,谷歌和IBM,而且都是云平台,谷歌的云平台,IBM的云平台,尤其GCP的大名想必大家都知道。所谓出身名门,大概指的就是这个样子吧?
Istio的实力非常强,我这里给了很多的赞誉:设计理念非常新颖前卫,有创意,有魄力,有追求,有格局。Istio的团队实力也非常惊人,大家有空可以去看看istio的委员会名单感受一下。Istio也是Google的新的重量级的产品,很有可能成为下一个现象级的产品。Google现在的现象级产品是什么?K8s。而Istio很有可能成为下一个K8S级别的产品。

说到应时而生,什么是势?我们今天所在的是什么时代?是互联网技术大规模普及的时代,是微服务容器如日中天的时代,是Cloud Native大势已成的时代。也是传统企业进行互联网转型的时代,今天的企业用户都想转型,这个大势非常明显,大家都在转或者准备转,但是先天不足。什么叫先天不足?没基因,没能力,没经验,没人才,而且面临我们前面说的所有的痛点。所有说Istio现在出现,时机非常合适。别忘了Istio身后还有CNCF的背景,已经即将一统江湖的k8s。

Istio在发布之后,社区响应积极,所谓应者云集。其中作为市面上仅有的几个Service Mesh之一的Envoy,甘心为Istio做底层,而另外两个实现Linkerd/nginmesh也直接放弃和Istio的对抗,选择合作,积极和Istio做集成。社区中的一众大佬,如这里列出来的,都在第一时间响应,要和istio做集成或者基于Istio做自己的产品。为什么说是第一时间?Istio出0.1版本,他们就直接表明态度开始站队了。

Markdown

Istio的架构,主要分为两大块。下面的数据面板,是给传统Service Mesh的,目前是Envoy,但是我们前面也提到Linkerd和Nginmesh都在和Istio做集成,指的就是替代Envoy做数据面板。
另外一大块就是上面的控制面板,这是Istio真正带来的内容。主要分成三大块,图中我列出了他们各自的职责和可以实现的功能。

因为时间有限,不在今天具体展开。

Markdown

这里给大家留一个地址,是我之前做的一次线上分享,对Istio的详细介绍,内容比较多,大家看看仔细看看。

万字解读:Service Mesh服务网格新生代—Istio
然后我们还组织了一个service mesh的技术社区,对istio的文档进行了翻译。

Istio官方文档中文翻译

Markdown

总结一下,Service Mesh这是一步一步过来的: 从原始的代理,到限制很多的Sidecar,再到通用性的Service Mesh,然后到加强管理功能的Istio,在未来成为下一代的微服务。
注意,离Service Mesh这个词汇出现的时间点,也才一年。

为何选择Service Mesh?

Markdown

前面三个痛点都被解决了,有了Service Mesh之后这些问题都不是问题了。升级的痛点怎么解决?Service Mesh是一个独立进程,可单独升级,而应用程序不用改。

Markdown

Service Mesh是以远程调用的方式让客户端接入,只要能发出请求,简单发给Service Mesh就可以。客户端极度简化,对于典型的Rest请求,几乎所有的语言都有完善的支持。而服务器端只要做一个事情,服务注册。这样对于多语言的支持,就变得非常舒服了。现在终于可以真正的自由选择编程语言。

Markdown

这里有一个奇迹,鱼与熊掌兼得:同时实现降低门槛,功能增加。有些信奉质量守恒的同学会感觉不科学,注意能同时实现这两个改进的原因,是把工作量最大最辛苦的事情都交给了Service Mesh。而Service Mesh是通用的,可以反复重用的。

Markdown

Service Mesh为业务开发团队带来的变革:降低入门门槛,提供稳定基座,帮助团队实现技术转型。最终达到的目的是,让业务开发团队从微服务实现的具体技术细节中解放出来,回归业务。

Markdown

第二个变革,是对运维管理团队的强化,这里如果有做运维的同学,你们可以认真思考一下:如果有了Service Mesh,你们对系统的管理和控制力会有多大的?注意很多功能的实现已经不再和应用有关,都在移到Service Mesh中,而Service Mesh通常是在运维的掌控中。

Markdown

Service Mesh对于新兴小众语言是极大的利好。对于新的语言来说,在和传统的主流编程语言竞争时,最痛苦的事情是什么?是生态,比如各种类库,各种框架。在微服务这个领域,新兴小众语言想和Java等比拼,非常的难:这是用自己的劣势对上别人的优势。而有了Service Mesh之后,小众语言就有机会避开这个弊端,再不用和Java比拼生态,而是充分发挥自己的语言特点,做自己最擅长的领域。

Markdown

Markdown

今天的内容基本上到这儿了,最后给两个资料,这两个文章,一个是对Service Mesh的解释,一个是详细介绍Service Mesh的由来,大家如果回去之后可以详细看一下。尤其第二个文章,我的PPT援引了里面的很多图片。英文不是特别好的同学可以看一下中文翻译版本,作者翻译质量非常高。

总结

Markdown

最后,我们建立了一个Service Mesh的技术社区和微信群,供以交流学习,目前微信群已建立,社区正在筹备当中,不就将会亮相。

我们今天的内容就到这里结束,非常感谢大家!

查看原文

赞 6 收藏 13 评论 0

sugelawa 赞了文章 · 2019-08-16

WEB前端面试选择题解答(共36题)

第1题

["1", "2", "3"].map(parseInt) 

A:["1", "2", "3"]

B:[1, 2, 3]

C:[0, 1, 2]

D:other

解释:该题目的答案为:[1, NaN, NaN];即选择D。该题用到了map与parseInt;parseInt() 函数的语法是parseInt(string, radix);
string 必需。要被解析的字符串。
radix可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。
如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。
如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN

实际上 map里面的callback函数接受的是三个参数 分别为元素 下标和数组 (虽然很多情况只使用第一个参数)

回调函数的语法如下所示:
function callbackfn(value, index, array1)
可使用最多三个参数来声明回调函数。

例:
var a=["1", "2", "3", "4","5",6,7,8,9,10,11,12,13,14,15];
a.map(parseInt);
返回结果为:[1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9, 11, 13, 15, 17, 19]

第2题

[typeof null, null instanceof Object]

A: ["object", false]

B: [null, false]

C: ["object", true]

D: other

解释:

考察typeof运算符和instanceof运算符,上MDN上看一下typeof运算符,一些基础类型的结果为:

Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Any other object "object"
Array "object"

null instanceof 任何类型 都是false,所以选A。

第3题

[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow)] 

A: an error

B: [9, 0]

C: [9, NaN]

D: [9, undefined]

解答:这题考的Math.powArray.prototype.reduce

Math.pow(base, exponent)接受两个参数:基数、需要计算的次方

reduce传递给其作为参数的函数几个值:

  • previousValue:上一次计算的结果
  • currentValue:当前元素的值
  • index: 当前元素在数组中的位置
  • array:整个数组

reduce本身接受两个参数,callbackinitialValue,分别是reduce的回调函数和计算初始值--也就是第一次reduce的callback被调用时的previousValue的值,默认为0

reduce在数组为空且没有定义initialValue时,会抛出错误,在火狐下报错为:TypeError: reduce of empty array with no initial value
所以选A

第4题

var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');

A: Value is Something

B: Value is Nothing

C: NaN

D: other

解答:这题考的javascript中的运算符优先级,这里'+'运算符的优先级要高于'?'所以运算符,实际上是 'Value is true'?'Something' : 'Nothing',当字符串不为空时,转换为bool为true,所以结果为'Something',选D

第5题

var name = 'World!';
(function () {
    if (typeof name === 'undefined') {
        var name = 'Jack';
        console.log('Goodbye ' + name);
    } else {
        console.log('Hello ' + name);
    }
})(); 

A: Goodbye Jack

B: Hello Jack

C: Hello undefined

D: Hello World

这题考的是javascript作用域中的变量提升,javascript的作用于中使用var定义的变量都会被提升到所有代码的最前面,于是乎这段代码就成了:

var name = 'World!';
(function () {
    var name;//现在还是undefined
    if (typeof name === 'undefined') {
        name = 'Jack';
        console.log('Goodbye ' + name);
    } else {
        console.log('Hello ' + name);
    }
})();

这样就很好理解了,typeof name === 'undefined'的结果为true,所以最后会输出'Goodbye Jack',选A

第6题

var END = Math.pow(2, 53);
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
    count++;
}
console.log(count);

A: 0

B: 100

C: 101

D: other

解答:这题考查javascript中的数字的概念:首先明确一点,javascript和其他语言不同,仅有一种数字,IEEE 754标准的64位浮点数,能够表示的整数范围是-2^53~2^53(包含边界值),所以Math.pow(2, 53)即为javascript中所能表示的最大整数,在最大整数在继续增大就会出现精度丢失的情况,END + 1 的值其实是等于END的,这也就造成了死循环。

第7题

var ary = [0,1,2];
ary[10] = 10;
ary.filter(function(x) { return x === undefined;});

A: [undefined × 7]

B: [0, 1, 2, 10]

C: []

D: [undefined]

解答:选择C Array.prototype.filter is not invoked for the missing elements(缺少的元素,不会调用过滤器)。

第8题

var two   = 0.2
var one   = 0.1
var eight = 0.8
var six   = 0.6
[two - one == one, eight - six == two]

A: [true, true]

B: [false, false]

C: [true, false]

D: other

解答:浮点数计算时的精度丢失问题。.chrome中计算出来的结果:[0.1, 0.20000000000000007],也就是[true, false],最终答案选C。

第9题

function showCase(value) {
    switch(value) {
    case 'A':
        console.log('Case A');
        break;
    case 'B':
        console.log('Case B');
        break;
    case undefined:
        console.log('undefined');
        break;
    default:
        console.log('Do not know!');
    }
}
showCase(new String('A'));

A: Case A

B: Case B

C: Do not know!

D: undefined

解答:此题考察的是关于new string();其实就是new一个实例对象。要匹配的也是object;所以答案是Do not know!,选择C。

第10题

function showCase2(value) {
    switch(value) {
    case 'A':
        console.log('Case A');
        break;
    case 'B':
        console.log('Case B');
        break;
    case undefined:
        console.log('undefined');
        break;
    default:
        console.log('Do not know!');
    }
}
showCase(String('A'));

A: Case A

B: Case B

C: Do not know!

D: undefined

和上题原理一样,不过这里没有使用new来生成字符串,所以生成的结果就是原始字符串,相当于showCase('A'),所以结果就是A

第11题

function isOdd(num) {
    return num % 2 == 1;
}
function isEven(num) {
    return num % 2 == 0;
}
function isSane(num) {
    return isEven(num) || isOdd(num);
}
var values = [7, 4, '13', -9, Infinity];
values.map(isSane);

A: [true, true, true, true, true]

B: [true, true, true, true, false]

C: [true, true, true, false, false]

D: [true, true, false, false, false]

还是JS的数字相关,不过这次考察的是取模,这题我也是瞎蒙的(果断跪了)。

前两个基本上没什么疑问,必然是true

'13'在进行计算前则会进行隐式类型转换(JS最恶心的部分之一),详细参见$雨$的文章《Javascript类型转换的规则》,这里的规则就是将字符串通过Number()方法转换为数字,所以结果为13 % 2 ,也就是true

而JS中负数取模的结果是负数,这里-9%2的结果实际上是-1,所以为false

Infinity对任意数取模都是NaN,所以是false

综上,结果为[true, true, true, false, false],也就是C

第12题

parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)

A: 3, 3, 3

B: 3, 3, NaN

C: 3, NaN, NaN

D: other

还是parseInt的题,考的和第一题类似,第一个值为3没什么好说的。如果出现的数字不符合后面输入的进制,则为NaN,所以第二个值为NaN。而radix为0时的情况第一题下面有介绍,这里也是一样为默认10,所以结果为3,所以答案为3, NaN, 3,选D

第13题

Array.isArray( Array.prototype )

A: true

B: false

C: error

D: other

死知识,MDN传送门,这是MDN官方给的例子...选A

第14题

var a = [0];
if ([0]) { 
  console.log(a == true);
} else { 
  console.log("wut");
}

A: true

B: false

C: "wut"

D: other

同样是一道隐式类型转换的题,不过这次考虑的是'=='运算符,a本身是一个长度为1的数组,而当数组不为空时,其转换成bool值为true。

而==左右的转换,会使用如果一个操作值为布尔值,则在比较之前先将其转换为数值的规则来转换,Number([0]),也就是0,于是变成了0 == true,结果自然是false,所以最终结果为B

第15题

[] == []

A: true

B: false

C: error

D: other

这题考的是数组字面量创建数组的原理和==运算符,首先JS中数组的真实类型是Object这点很明显typeof []的值为"object",而==运算符当左右都是对象时,则会比较其是否指向同一个对象。而每次调用字面量创建,都会创造新的对象,也就是会开辟新的内存区域。所以指针的值自然不一样,结果为 false,选B

第16题

'5' + 3  
'5' - 3 

A: 53, 2

B: 8, 2

C: error

D: other

又是一道隐式类型转换的题

加法: 加法运算中,如果有一个操作值为字符串类型,则将另一个操作值转换为字符串,最后连接起来

减法: 如果操作值之一不是数值,则被隐式调用Number()函数进行转换

所以第一行结果为字符串运算,为'53'。第二行结果为2,选A

第17题

1 + - + + + - + 1 

A: 2

B: 1

C: error

D: other

C语言中的经典...对于这种问题,原理什么的不懂,蒙吧,结果是2。选A

第18题

var ary = Array(3);
ary[0]=2
ary.map(function(elem) { return '1'; }); 

A: [2, 1, 1]

B: ["1", "1", "1"]

C: [2, "1", "1"]

D: other

又是考的Array.prototype.map的用法,map在使用的时候,只有数组中被初始化过元素才会被触发,其他都是undefined,所以结果为["1", undefined × 2],选D

第19题

function sidEffecting(ary) { 
  ary[0] = ary[2];
}
function bar(a,b,c) { 
  c = 10
  sidEffecting(arguments);
  return a + b + c;
}
bar(1,1,1)

A: 3

B: 12

C: error

D: other

这题考的是JS的函数arguments的概念:

在调用函数时,函数内部的arguments维护着传递到这个函数的参数列表。它看起来是一个数组,但实际上它只是一个有length属性的Object,不从Array.prototype继承。所以无法使用一些Array.prototype的方法。

arguments对象其内部属性以及函数形参创建getter和setter方法,因此改变形参的值会影响到arguments对象的值,反过来也是一样

具体例子可以参见Javascript秘密花园#arguments

所以,这里所有的更改都将生效,a和c的值都为10,a+b+c的值将为21,选D

第20题

var a = 111111111111111110000,
    b = 1111;
a + b;

A: 111111111111111111111

B: 111111111111111110000

C: NaN

D: Infinity

又是一道考查JavaScript数字的题,与第七题考察点相似。由于JavaScript实际上只有一种数字形式IEEE 754标准的64位双精度浮点数,其所能表示的整数范围为-2^53~2^53(包括边界值)。这里的111111111111111110000已经超过了2^53次方,所以会发生精度丢失的情况。综上选B

第21题

var x = [].reverse;
x();

A: []

B: undefined

C: error

D: window

这题考查的是函数调用时的thisArray.prototype.reverse方法。
首先看Array.prototype.reverse方法,首先举几个栗子:

console.log(Array.prototype.reverse.call("skyinlayer"));
//skyinlayer
console.log(Array.prototype.reverse.call({}));
//Object {}
console.log(Array.prototype.reverse.call(123));
//123

这几个栗子可以得出一个结论,Array.prototype.reverse方法的返回值,就是this

Javascript中this有如下几种情况:

全局下this,指向window对象

console.log(this);
//输出结果:
//Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}

函数调用,this指向全局window对象:

function somefun(){
    console.log(this);
}
somefun();
//输出结果:
//Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}

方法调用,this指向拥有该方法的对象:

var someobj = {};
someobj.fun = function(){
    console.log(this);
};
console.log(someobj.fun());
//输出结果:
//Object {fun: function}

调用构造函数,构造函数内部的this指向新创建对象:

function Con() {
    console.log(this);
}
Con.prototype.somefun = function(){};
console.log(new Con());
//输出结果:
//Con {somefun: function}

显示确定this:

function somefun(){
    console.log(this);
};
somefun.apply("skyinlayer");
somefun.call("skyinlayer");
//输出结果:
//String {0: "s", 1: "k", 2: "y", 3: "i", 4: "n", 5: "l", 6: "a", 7: "y", 8: "e", 9: "r", length: 10}
//String {0: "s", 1: "k", 2: "y", 3: "i", 4: "n", 5: "l", 6: "a", 7: "y", 8: "e", 9: "r", length: 10} 

这里报错:Uncaught TypeError: Array.prototype.reverse called on null or undefined,选C。

第22题

Number.MIN_VALUE > 0

A: false

B: true

C: error

D: other

考查的Number.MIN_VALUE的概念,MDN传送门,关键的几句话

  • The Number.MIN_VALUE property represents the smallest positive numeric value representable in JavaScript.

翻译:Number.MIN_VALUE表示的是JavaScript中最小的正数

  • The MIN_VALUE property is the number closest to 0, not the most negative number, that JavaScript can represent.

翻译:MIN_VALUE是接近0的数,而不是最小的数

  • MIN_VALUE has a value of approximately 5e-324. Values smaller than MIN_VALUE ("underflow values") are converted to 0.

翻译:MIN_VALUE值约等于5e-324,比起更小的值(大于0),将被转换为0

所以,这里是true,选B

顺带把Number的几个常量拉出来:

    • Number.MAX_VALUE:最大的正数
    • Number.MIN_VALUE:最小的正数
    • Number.NaN:特殊值,用来表示这不是一个数
    • Number.NEGATIVE_INFINITY:负无穷大
    • Number.POSITIVE_INFINITY:正无穷大

如果要表示最小的负数和最大的负数,可以使用-Number.MAX_VALUE-Number.MIN_VALUE

第23题

[1 < 2 < 3, 3 < 2 < 1]

A: [true, true]

B: [true, false]

C: error

D: other

运算符的运算顺序和隐式类型转换的题,从MDN上运算符优先级,'<'运算符顺序是从左到右,所以变成了[true < 3, false < 1]

接着进行隐式类型转换,'<'操作符的转换规则(来自$雨$的文章《Javascript类型转换的规则》):

  • 如果两个操作值都是数值,则进行数值比较
  • 如果两个操作值都是字符串,则比较字符串对应的字符编码值
  • 如果只有一个操作值是数值,则将另一个操作值转换为数值,进行数值比较
  • 如果一个操作数是对象,则调用valueOf()方法(如果对象没有valueOf()方法则调用toString()方法),得到的结果按照前面的规则执行比较
  • 如果一个操作值是布尔值,则将其转换为数值,再进行比较

所以,这里首先通过Number()转换为数字然后进行比较,true会转换成1,而false转换成0,就变成了[1 < 3, 0 < 1]

所以结果为A

第24题

// the most classic wtf
2 == [[[2]]]

A: true

B: false

C: undefined

D: other

又是隐式类型转换的题(汗)

题目作者的解释是:
both objects get converted to strings and in both cases the resulting string is "2"

也就是说左右两边都被转换成了字符串,而字符串都是"2"

这里首先需要对==右边的数组进行类型转换,根据以下规则(来自justjavac的文章《「译」JavaScript 的怪癖 1:隐式类型转换》):

  1. 调用 valueOf()。如果结果是原始值(不是一个对象),则将其转换为一个数字。
  2. 否则,调用 toString() 方法。如果结果是原始值,则将其转换为一个数字。
  3. 否则,抛出一个类型错误。

所以右侧被使用toString()方法转换为"2",然后又通过Number("2")转换为数字2进行比较,结果就是true了,选A

第25题

3.toString()
3..toString()
3...toString()

A: "3", error, error

B: "3", "3.0", error

C: error, "3", error

D: other

说实话这题有点常见了,很多人都踩过3.toString()的坑(包括我)...虽然JavaScript会在调用方法时对原始值进行包装,但是这个点是小数点呢、还是方法调用的点呢,于是乎第一个就是error了,因为JavaScript解释器会将其认为是小数点。

而第二个则很好说通了,第一个点解释为小数点,变成了(3.0).toString(),结果就是"3"了

第三个也是,第一个点为小数点,第二个是方法调用的点,但是后面接的不是一个合法的方法名,于是乎就error了

综上,选C

第26题

(function(){
  var x = y = 1;
})();
console.log(y);
console.log(x);

A: 1, 1

B: error, error

C: 1, error

D: other

变量提升和隐式定义全局变量的题,也是一个JavaScript经典的坑...

还是那句话,在作用域内,变量定义和函数定义会先行提升,所以里面就变成了:

(function(){
    var x;
    y = 1;
    x = 1;
})();

这点会问了,为什么不是var x, y;,这就是坑的地方...这里只会定义第一个变量x,而y则会通过不使用var的方式直接使用,于是乎就隐式定义了一个全局变量y

所以,y是全局作用域下,而x则是在函数内部,结果就为1, error,选C

第27题

var a = /123/,
    b = /123/;
a == b
a === b

A: true, true

B: true, false

C: false, false

D: other

首先需要明确JavaScript的正则表达式是什么。JavaScript中的正则表达式依旧是对象,使用typeof运算符就能得出结果:

console.log(typeof /123/);
//输出结果:
//"object"

==运算符左右两边都是对象时,会比较他们是否指向同一个对象,可以理解为C语言中两个指针的值是否一样(指向同一片内存),所以两个结果自然都是false

第28题

var a = [1, 2, 3],
    b = [1, 2, 3],
    c = [1, 2, 4]
a == b
a === b
a > c
a < c

A: false, false, false, true

B: false, false, false, false

C: true, true, false, true

D: other

和上题类似,JavaScript中Array的本质也是对象,所以前两个的结果都是false,

而JavaScript中Array的'>'运算符和'<'运算符的比较方式类似于字符串比较字典序,会从第一个元素开始进行比较,如果一样比较第二个,还一样就比较第三个,如此类推,所以第三个结果为false,第四个为true。

综上所述,结果为false, false, false, true,选A

第29题

var a = {}, b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b]

A: [false, true]

B: [true, true]

C: [false, false]

D: other

原型链的题(总会有的),考查的__proto__和prototype的区别。首先要明确对象和构造函数的关系,对象在创建的时候,其__proto__会指向其构造函数的prototype属性

Object实际上是一个构造函数(typeof Object的结果为"function"),使用字面量创建对象和new Object创建对象是一样的,所以a.__proto__也就是Object.prototype,而Object.getPrototypeOf(a)与a.__proto__是一样的,所以第二个结果为true

而实例对象是没有prototype属性的,只有函数才有,所以a.prototype其实是undefined,第一个结果为false

综上,选A

第30题

function f() {}
var a = f.prototype, b = Object.getPrototypeOf(f);
a === b

A: true

B: false

C: null

D: other

还是__proto__和prototype的区别,两者不是一个东西,所以选B

第31题

function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]

A: error

B: ["", ""]

C: ["foo", "foo"]

D: ["foo", "bar"]

考察了函数的name属性,使用函数定义方式时,会给function对象本身添加一个name属性,保存了函数的名称,很好理解oldName为"foo"。name属性时只读的,不允许修改,所以foo.name = "bar";之后,foo.name还是"foo",所以结果为["foo", "foo"],选C

PS:name属性不是一个标准属性,不要去使用,除非你想要坑别人

第32题

"1 2 3".replace(/\d/g, parseInt)

A: "1 2 3"

B: "0 1 2"

C: "NaN 2 3"

D: "1 NaN 3"

String.prototype.replace、正则表达式的全局匹配和parseInt(又是parseInt...),可以根据题意看出来题目上漏了一个''

首先需要确定replace会传给parseInt哪些参数。举个栗子:

"1 2 3".replace(/\d/g, function(){
    console.log(arguments);
});
//输出结果:
//["1", 0, "1 2 3"]
//["2", 2, "1 2 3"]
//["3", 4, "1 2 3"] 

一共三个:

  1. match:正则表达式被匹配到的子字符串
  2. offset:被匹配到的子字符串在原字符串中的位置
  3. string:原字符串

这样就很好理解了,又回到之前parseInt的问题了,结果就是parseInt("1", 10), parseInt("2", 2), parseInt("3", 4)所以结果为"1, NaN, 3",选D

第33题

function f() {}
var parent = Object.getPrototypeOf(f);
f.name // ?
parent.name // ?
typeof eval(f.name) // ?
typeof eval(parent.name) //  ?

A: "f", "Empty", "function", "function"

B: "f", undefined, "function", error

C: "f", "Empty", "function", error

D: other

又是Function.name属性的题,和三十二题一样样,f.name值为"f",而eval("f")则会输出f函数,所以结果为"function"

接着看parent,parent实际上就是f.__proto__,需要明确的是JavaScript中的函数也是对象,其也有自己的构造函数Function,所以f.__proto__ === Function.prototype结果是true,而Function.prototype就是一个名为Empty的function

console.log(Function.prototype);
console.log(Function.prototype.name);
//输出结果:
//function Empty() {}
//Empty

所以parent.name的值为Empty

如果想直接在全局作用域下调用Empty,显示未定义...因为Empty并不在全局作用域下

综上所述,结果为C

第34题

var lowerCaseOnly =  /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()]

A: [true, false]

B: error

C: [true, true]

D: [false, true]

正则表达式的test方法会自动将参数转换为字符串,原式就变成了[lowerCaseOnly.test("null"), lowerCaseOnly.test("undefined")],结果都是真,所以选C

第35题

[,,,].join(", ")

A: ", , , "

B: "undefined, undefined, undefined, undefined"

C: ", , "

D: ""

JavaScript中使用字面量创建数组时,如果最末尾有一个逗号',',会背省略,所以实际上这个数组只有三个元素(都是undefined):

console.log([,,,].length);
//输出结果:
//3

而三个元素,使用join方法,只需要添加两次,所以结果为", , ",选C

第36题

var a = {class: "Animal", name: 'Fido'};
a.class

A: "Animal"

B: Object

C: an error

D: other

经典坑中的一个,class是关键字。根据浏览器的不同,结果不同:

chrome的结果: "Animal"

Firefox的结果:"Animal"

Opera的结果:"Animal"

IE 8以上也是: "Animal"

IE 8 及以下: 报错

查看原文

赞 12 收藏 9 评论 0

sugelawa 赞了回答 · 2019-07-30

Rust跟Go的比较有哪些不同?

这篇文章你可以看一下(一个拥有3.5年go经验,进入rust的总结):https://medium.com/@deckarep/paradigms-of-rust-for-the-go-developer-210f67cd6a29

关注 4 回答 3

sugelawa 关注了用户 · 2019-07-30

欧阳思海 @sihai_5ae5ba9170388

欢迎关注微信公众号【好好学java】,这里分享我历时三个月总结的【Java 面试 + Java 后端技术学习指南】,这是本人这几年及春招的总结,已经拿到了腾讯等大厂offer,整理成了一本电子书,拿去不谢。现在免费分享大家,在我的公众号【好好学java】,回复【Java面试】即可获取。

关注 2513

sugelawa 关注了专栏 · 2019-07-30

疯狂的技术宅

本专栏文章首发于公众号:前端先锋 。

关注 24297

sugelawa 关注了专栏 · 2019-07-30

AlloyTeam 技术专栏

探索前端技术

关注 3754

sugelawa 关注了专栏 · 2019-07-30

超神经HyperAI

AI 行业实验媒体,站在科技与人文的交叉口,看懂人工智能。微信公众号:HyperAI

关注 4681

认证与成就

  • 获得 0 次点赞
  • 获得 4 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-03-15
个人主页被 140 人浏览