面向面经之我自己的第二次腾讯前端一面凉经
1、自我介绍,讲一下你的项目
王婆卖瓜,自卖自夸
2、从输入url到与后台通信,是怎么通信的
我的答案:
1、检查这个url是否合法
2、查看本地是否有缓存(浏览器缓存->系统缓存->路由器缓存)(保存的是IP地址),有缓存则直接显示,没有缓存则next
3、查询DNS服务器,得到服务器的ip地址
4、开始tcp的三次握手
5、发送http请求
6、浏览器收到http响应的数据,根据html构造dom树,在本地窗口渲染并显示网页。如果该响应可以缓存,则存入缓存。(cache-control)
7、当浏览器页面被关闭时,终止http会话并关闭连接。
3、浏览器是怎么渲染页面的-->浏览器渲染机制
参考:https://blog.csdn.net/liujianfeng1214/article/details/86690284
- 解析html生成dom树
- 解析css生成cssom规则树
- 将dom树与cssom规则树合并在一起生成渲染树
- 遍历渲染树开始布局,计算每个节点的位置大小信息
- 将渲染树的每个节点绘制到屏幕
4、你说到获取服务器IP地址,是怎么获取的(dns)
1、在浏览器中输入www . qq .com 域名,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。
2、如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。
3、如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/ip参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。
4、如果要查询的域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性。
5、如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址(http://qq.com)给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找http://qq.com域服务器,重复上面的动作,进行查询,直至找到www . qq .com主机。
6、如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。不管是本地DNS服务器用是是转发,还是根提示,最后都是把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机。
从客户端到本地DNS服务器是属于递归查询,而DNS服务器之间就是的交互查询就是迭代查询。
5、tcp的三次握手
子问题:为什么不能两次握手
如果两次握手的话,请求发生超时重传就会有两个连接。
子问题:怎么确认发过去的是哪个连接
确认号,序列号
6、你说到http,但是现在常用的是https,https和http有什么区别
a. https协议需要到ca申请证书,一般免费证书很少,需要交费。
b. http是超文本传输协议,信息是明文传输;https 则是具有安全性的ssl加密传输协议。
c. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
d. http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
7、https的通信过程
https://www.jianshu.com/p/5a1cd768ac1e
简易过程:
- 客户端向服务端发送请求
- 服务端返回数字证书
- 客户端用自己的CA(主流的CA机构证书一般都内置在各个主流浏览器中)公钥去解密证书,如果证书有问题会提示风险
- 如果证书没问题,客户端会生成一个对称加密的随机秘钥然后再和刚刚解密的服务器端的公钥对数据进行加密,然后发送给服务器端
- 服务器端收到以后会用自己的私钥对客户端发来的对称秘钥进行解密
- 双方就拿着这个对称加密秘钥来进行正常的通信
https是http+ssl或者tls,即http下加入ssl层,https的安全基础就是ssl,因此加密的详细内容就需要ssl,ssl/tls层负责客户端和服务器之间的加解密算法协商、密钥交换、通信连接的建立。
https在传输数据之前需要客户端与服务端之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。tls/ssl中使用了非对称加密,对称加密以及hash算法。
过程:
- (clientHello请求)客户端发起请求,以明文传输请求信息,包含支持的版本信息,支持的加密方法,支持的压缩方法,随机数(用于生成对话密钥),扩展字段信息。
- 服务器回应(serverHello)服务器端返回协商的信息结果。
- 确认使用的加密通信版本
- 一个服务器生成的随机数,用于生成对话密钥
- 确认使用的加密方法
- 服务器证书(如果需要确认客户端的身份,就会再要求包含一项请求,要求客户端提供客户端证书)
- 客户端回应,验证服务器证书(域名、是否是可信机构颁布、是否过期),如果证书没有问题,客户端就会从证书中取出服务器的公钥。向服务器发送以下信息:
- 随机数(用服务器公钥加密,防止被窃听)(pre master secret)
- 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送
- 客户端握手结束
- 双方使用事先商定的加密方法,各自生成本次会话所用的同一把会话密钥
- 服务器的最后回应
服务器收到第三个随机数后,计算生成本次会话所用的会话密钥,然后,向客户端发送:
- 编码改变通知,随后的消息都是用商定的加密方法和密钥发送
- 服务器握手结束通知,表示服务器的握手已经结束
----ps:为什么要用三个随机数来生成会话密钥?----
这样生成的密钥才不会每次都一样。因为证书是静态的,所以要通过这个随机因素来保证协商出来的密钥的随机性。
对于 RSA 密钥交换算法来说,pre-master-key 本身就是一个随机数,再加上 hello 消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。
pre master secret 的存在在于 SSL 协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么 pre master secret 就有可能被猜出来,那么仅适用 pre master secret 作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上 pre master secret 三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。
前两个随机数都是明文传输,窃听者是可以轻易获取的,只有最后一个是加密传输,只有拥有服务器私钥才能解密
8、原型链
每一个函数对象都有一个prototype对象,每一个对象都有一个_proto_属性,_proto_指向的就是它的构造函数的prototype。这个函数对象的prototype的构造函数就是object.prototype
我们都知道对象都有一个toString方法。上述的实例化对象b也可以toString,
而实例化对象b本身并没有toString的方法,那他就会沿着它的proto向他的构造函数B的prototype对象去找,而这里也没有,那他就会 继续沿着B.prototype.proto向上找。而B.prototype.proto指向的就是Object.prototype。
就那个那个原型链的图
0->0
......|
0->0
......|
0那个
子问题:js的原型链和java有什么不同?
我哪里知道,完全忘记了java的东西
9、数组的链表的底层实现
数组:
- 最大的优点是连续的存储空间所带来的的随机访问的能力
- 最大的缺点是连续存储空间所造成的容量的固定(不具备动态性)
- 定义时生成一个固定长度并且是连续的线性结构。访问有两种方法:1.指针,把数组名当作是数组首元素的地址,所以*(a+0)就是第一个元素。2.下标
链表:
- 物理存储单元上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。每个节点有两部分:存储数据元素的数据域和存储下一个节点地址的指针域
- 可以克服数组需要预先知道数据大小的缺点,进行灵活的内存动态管理。但是链表由于增加了节点的指针域,空间开销比较大,而且失去了随机读取的优点。
实现一个单向链表:
(代码上)
- 设置一个node节点类,里面有数据、指向下一个节点的指针。
- 设置两个指针,一是头指针(固定不动),二是可移动的指针(1.申请内存时,保存内存地址。2. 通过内存地址进行数据及地址的存储)
- 添加元素:
如果链表是空的,那么直接存入。否则申请新的空间存数据 - 遍历:从头元素一直到尾元素(注意判断链表是不是空和 判断可移动指针下一个是不是空来判定是不是遍历完成)
- 插入一个节点(在中间):创建一个新的节点,定义他的数据域,然后创建一个指针指向头结点,当遍历到指定的位置,则将前面节点的指针指向当前节点,然后将当前节点的指针指向要插入的下一个节点。
10、数组和链表插入一个数据,底层怎么实现的
11、进程和线程的区别
- 资源:进程有自己独立的地址空间,但是线程
- 调度: 进程是资源分配的基本单位,线程是独立调度的基本单位
- 通信方式:线程可以通过直接读写同一进程中的数据进行通信,进程通信比较复杂
- 系统开销:进程上下文切换开销比较大,线程开销小
- 一个进程挂掉了不会影响其他线程,而线程挂掉了会影响其他线程
12、进程间通信
主要分为:管道、系统IPC(包括消息队列、信号量、共享存储)、SOCKET
- 管道:
Linux下一切皆文件这我们必须牢记,所以管道就是一份文件,进程A能看到进程B也能够看到,同时进程A往管道中写数据进程B就可以从另一端读数据了。(在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利)
1.匿名管道,匿名管道用pipe()创建,只能用于有亲缘关系的进程间通信
2. 命名管道,是用于任意进程,面向字节流,半双工通信。
- 消息列表
就是一个链表,进程A可以向队列中写数据(写满则不能写了,因为消息队列是固定的),队列中有数据了进程B就可以开始读数据了,读完了数据就不能读了(这也就能说明消息队列面向数据报)
概念:在内核中创建一队列,队列中每个元素是一个数据报,不同的进程可以通过句柄去访问这个队列。
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法。
每个数据块都被认为是有⼀个类型,接收者进程接收的数据块可以有不同的类型值
特点:1、适用于任意进程2、面向数据报3、全双工通信(只要进程有读写权限就可以双向通信)4、生命周期随内核5、内置同步与互斥机制。
共享内存
共享内存就是一块内存,我们知道内存有随机访问的优势,所以共享内存就成为了进程间通信最快的方式。具体通信原理就是这一块物理内存在映射的时候会映射不同的虚拟地址空间,不同的虚拟地址空间就代表着不同的进程那么就可以让多个进程都看到这块内存,然后进行读写操作。信号量
信号量准确的来说就没有通信,他可以理解为是一个计数器加上等待队列,它主要侧重了同步于互斥,因为有时候多个进程同时访问临界资源就会产生死锁,那么就需要信号量记录可申请的资源的数量,每申请一次信号量减1,用完释放就加1,等待队列就是资源被申请完了(信号量为0),在申请就会信号量<0,那么此时就会将进程加入等待队列,一旦有资源释放,就可以立马申请到。
13、多线程是怎么保证线程安全的
参考:1
为什么会有多线程
- 计算资源在排队过程中严重浪费
- 任务分配的不公平
- 程序编写十分困难(如果一次只能执行一个任务,那么写程序时往往要把很多工作集成到一个程序)
当有多个进程的时候,一定程度上缓解了上面的问题,但是进程只能做一件事,如果我想取消这个事情要等到这个事情结束了之后再输入命令,所以这个是不合理的。
线程安全
线程安全是指在多线程环境下,程序可以始终执行正确的行为,符合预期的逻辑
线程不安全的原因:
如何保证线程安全
14、vue的生命周期
//里程碑式:在两个周期中间的解读说明是这两个生命周期之间发生的事情
啥都没开始
beforeCreate
数据监测,绑定data属性created
判断是否有el,有则判断是否有template,无则编译outer html,有则编译template,但是还没有挂载到到页面上beforeMount
给vue实例添加el成员,替换之前挂载的dom元素,完成模板中的html渲染到html页面中,此过程进行ajax交互mounted
挂载完成,dom树已经完成渲染页面,可进行dom操作
- beforeUpdate
- updated
- beforeDestroy
- destroyed
15、这些生命周期一般都用在什么情况
beforeCreate:可以在这里加loading,在加载实例时触发
created:初始化完成时的事件写在这里,如在这里结束loading事件,异步请求也适宜在此处调用。
mounted:挂载元素,获取dom节点
updated:如果对数据统一处理,在这里写上相应函数
beforeDestroy:可以做一个确认停止事件的确认框
nextTick:针对单一事件跟新数据后,立即操作dom
生命周期的作用:
生命周期中有多个事件钩子,让我们在控制整个vue实例的过程时更容易形成好的逻辑。
第一次页面加载会触发哪几个钩子:
beforeCreate,created,beforeMount,mounted
dom渲染在哪个周期中已经完成:mounted