前端
HTTP 和 HTTPS
HTTP:
- 超文本传输协议。
- 默认端口:80
HTTPS:
- 出现原因:HTTP明文传输不安全。
- HTTPS 是 HTTP 协议的一种扩展,其在HTTP的基础上加入SSL层进行加密。
- 默认端口:443
SSL
- SSL:是用于在互联网两台计算机之间用于
身份验证和加密的一种协议。 - SSL位于HTTP和TCP之间,即应用层和传输层之间。
- 传输安全层-TLS:是SSL的后续版本叫法。
http 和 https 的对比?
- http连接快:的连接很简单,是无状态的。https 握手阶段比较
费时,即HTTPS建立连接较慢。 - http更高效:https
缓存不如 http 高效,会增加数据开销。 - https安全:加密传输,不可更改,身份验证。
- Https使用过程繁琐:Https 协议需要 ca 证书。SSL 证书需要绑定
IP,不能再同一个 IP 上绑定多个域名,IPV4 资源支持不了这种消耗。
https如何解决http存在的问题?
加密
对称加密
- 加密和解密都使用一个密钥,有密钥就可以破解加密。 非对称加密
- 公开密钥加密,私有密钥解密。
- 存在缺点:低效,不是绝对的安全(通过公钥破解私钥,中间人攻击)。 对称加密+非对称加密
- 在交换密钥环节使用非对称加密方式,之后的建立通信交换报文阶段则使用对称加密方式。
数字签名-不可更改
方法:校验数字签名
- 发送方使用Hash函数生成消息摘要,将其一并传输,接收方使用接收到的Hash函数生成新消息摘要,两个信息摘要进行对比,如果一样,则说明信息完整。
数字证书-身份验证
- 使用数字证书解决通信方身份可能被伪装的问题。
3.https 协议的工作原理
- 客户端发起一个HTTPS请求
- 服务端返回公钥证书
- 客户端验证公钥证书,通过则客户端生成对称密钥,使用公钥加密并返回给服务的
- 服务端使用私钥解密,得到对称密钥,此时双方使用对称密钥进行密文传输
TCP和UDP
传输层
TCP-传输控制协议
是面向连接的协议---在收发数据前,必须和对方建立可靠的连接。
全双工
TCP三次握手-建立连接
作用:确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。
涉及的TCP报头:
- SYN-同步:
- ACK-确认:ACK=序号+长度=下一包起始序号。
- seq-序号:随机生成
第一次握手:客户端发送SYN包。
- 此时的SYN包:客户端初始序号seq=x
- 客户端进入已发送状态
第二次握手:服务器收到SYN包并确认客户的SYN,并发送一个SYN+ACK包。
- 此时的SYN+ACK包:服务端初始序号seq=y,确认号ack=x+1
- 服务器进入已接收状态
第三次握手:客户端收到SYN+ACK包之后,会发送一个ACK包。
- 此时的ACK包:序号seq=ack(seq=x+1),确认号ack=y+1
- 客户端发送报文后处于建立连接状态,服务端接收到报文后也进入到了建立连接状态。
第二次握手:服务器并不能确认客户端的接收能力是否正常,所以需要第三次握手。在不可靠的信道上建立可靠的连接。
TCP四次挥手-断开连接
涉及的TCP报头:
- ACK-确认:ACK=序号+长度=下一包起始序号。
- seq-序号:随机生成
- FIN-结束:
客户端断开连接:
- 客户端发送包含FIN的网络包(序号:n,确认号:m),服务端收到。
- 服务端发送包含ACK的网络包(序号:m,确认号:n+1),客户端收到,此时服务端到客户端的连接关闭。
服务端断开连接:
- 服务端发送包含FIN的网络包(序号:m,确认号:n+1),客户端收到。
- 客户端发送包含ACK的网络包(序号:n+1,确认号m+1:),服务端收到,此时客户端到服务端的连接关闭。
断开连接后:
- 服务端进入超时等待状态,等待超时连接。作用:在不可靠的信道上建立可靠的连接。
- 客户端立即关闭连接。
TCP/IP如何保证数据包传输的有序可靠?
解决乱序问题:通过确认号ACK回复需要的下一个起始序号
- 接收方收到数据包后,先进行校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包(带确认号),表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可方在数据包中捎带过去。
解决丢包问题:超时重发
- 为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区;
- 并为每个已发送的数据包启动一个超时定时器;
- 如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区;
- 否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。
TCP和UDP的区别
- TCP提供面向连接的可靠服务 ,UDP提供无连接不可靠服务。
- TCP仅支持
单播传输,UDP 提供了单播,多播,广播的功能。 - UDP的
头部开销比TCP的更小,数据传输速率更高,实时性更好。
cookie 和 session 区别
-
cookie数据存放在浏览器中,session数据存放在服务器中。
-
cookie是不安全的,别人可以分析存放在本地的cookie并进行cookie诈骗。
-
session会在一定时间内保存在服务器上。当访问增多时,会比较占用服务器的性能。考虑到服务性能,应尽量使用cookie。
-
单个cookie保存的数据不能超过4k。
cookie和session都用来存储用户信息,cookie存放于客户端有可能被窃取,所以cookie一般用来存放不敏感的信息,比如用户设置的网站主题,敏感的信息用session存储,比如用户的登录信息
粘包问题分析与对策
什么是粘包?
- TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
出现原因?
- 简单得说,在流传输中出现,UDP不会出现粘包,因为它有消息边界
- 粘包情况有两种:
粘在一起的包都是完整的数据包,粘在一起的包有不完整的包。
解决方案:
- 接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开。实验证明这种方法是高效可行的。
Cookie、sessionStorage、localStorage 的区别
相同点:
- 客户端存储技术
不同点:
-
存放数据大小
- cookie数据大小不能超过4k;
- sessionStorage和localStorage的存储比cookie大得多,可以达到5M+
-
生命周期
- cookie设置的过期时间之前一直有效;
- localStorage永久存储,浏览器关闭后数据不丢失除非主动删除数据;
- sessionStorage数据在当前浏览器窗口关闭后自动删除
-
http请求
- cookie的数据每次都会携带在HTTP头中,会自动的传递到服务器,如果使用cookie保存过多数据会带来性能问题。
- sessionStorage和localStorage数据保存在本地浏览器中,不参与和服务器的通信。
-
易用性:
- cookie:需要程序员自己封装,源生的Cookie接口不友好
- localStorage和sessionStorage:源生接口可以接受,亦可再次封装来对Object和Array有更好的支持。
根据同源策略,cookie 是区分端口的,但是浏览器实现来说,“cookie 区分域,而不区分端口,也就是说,同一个 ip 下的多个端口下的 cookie 是共享的!
从输入URL到页面加载的全过程
-
URL的解析:用户在地址栏输入内容之后,浏览器会首先判断用户输入的是合法的URL还是搜索内容,如果是搜索内容就合成URL,如果是合法的URL就开始进行加载。
-
查找强缓存:强缓存其实就是存储在本地的资源-硬盘或者内存中。如果命中缓存,则直接使用缓存资源,如果没有则下一步。
-
DNS域名解析:浏览器向DNS服务器发起请求,解析该URL中的域名对应的IP地址。为了保证响应的及时,DNS解析使用的是UDP协议。
-
建立TCP连接。
-
发起HTTP请求:浏览器发起读取文件的HTTP请求。(请求行,请求头,请求体)
-
服务器响应请求并返回结果:服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器。(或错误信息,或重定向的新的url地址)
-
关闭TCP连接。
-
浏览器解析渲染页面:生成DOM树,解析css和js,渲染页面,直至显示完成。
重绘与重排
浏览器页面生成过程:
- HTML被HTML解析器解析成DOM树
- CSS被CSS解析器解析成CSSOM树
- 将DOM树和CSSOM合成渲染树
- 渲染(生成布局和将布局绘制到页面)-此过程耗时
在页面的生命周期中,网页生成的时候,至少会渲染一次。在用户访问的过程中,还会不断触发重排(reflow)和重绘(repaint) ,不管页面发生了重绘还是重排,都会影响性能,特别是重排,会使付出高额的性能代价,所以应该应尽量避免。
重排和重绘发生的时机:
- 重排发生在生成布局的阶段,因为要重新计算每个元素在设备视口内的确切位置和大小,所以相对非常耗时。
- 重绘发生在绘制布局的阶段,重绘要将渲染树中的每个节点转换成屏幕上的实际像素,性能消耗小于重排。
浏览器重绘与重排的区别?
重排/回流(Reflow):当DOM的变化影响了元素的大小位置信息,浏览器需要重新计算元素的位置属性,将其安放在界面中的正确位置,这个过程叫做重排。表现为重新生成布局,重新排列元素。重绘(Repaint): 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。表现为某些元素的外观被改变。
单单改变元素的外观,不会引起网页重新生成布局,但当浏览器完成重排之后,将会重新绘制受到此次重排影响的部分。
重排和重绘代价是高昂的,它们会破坏用户体验,并且让UI展示非常迟缓,而相比之下重排的性能影响更大,在两者无法避免的情况下,一般可选择代价更小的重绘。
重绘不一定导致重排,但重排一定会导致重绘
如何触发重排和重绘?
触发重排:
- 页面初始渲染,这是开销最大的一次重排
- 添加/删除可见的DOM元素
- 改变元素位置
- 改变元素尺寸,比如边距、填充、边框、宽度和高度等
- 改变元素内容,比如文字数量,图片大小等
- 改变元素字体大小
- 改变浏览器窗口尺寸,比如resize事件发生时
- 激活CSS伪类(例如:hover)
- 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow
- 查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等
- 调用getComputedStyle方法
- 通过display: none隐藏一个DOM节点-触发重排和重绘
触发重绘:
- 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化。
- 样式改变引起重绘,如改变颜色的行为。
如何避免重绘或者重排?
-
尽可能把样式加在在低层级的DOM节点上,减小重排范围。
-
样式集中改变,减少重绘次数
-
尽量少使用
dispaly:none,可以使用visibility:hidden代替。(dispaly:none会造成重排,visibility:hidden会造成重绘。) -
不要使用Table布局,因为一个小小的操作,可能就会造成整个表格的重排或重绘。
-
使用绝对定位和固定定位将元素提升为body的直属子元素,减少重排对于其他元素的影响,减小重排重绘开销,如:使用在动画元素上。
-
批量修改元素时,可以先让元素脱离文档流,等修改完毕后,再放入文档流。(即:DOM离线处理:display:none将DOM隐藏处理)
- JavaScript 的执行是在生成渲染树之前的。所以 JavaScript 的加载、解析与执行会阻塞 DOM 的构建,阻塞页面的渲染,当将js放在页面底部位置执行时,会产生短暂有界面内容无数据内容的现象。
浏览器的缓存机制 强制缓存 && 协商缓存
浏览器缓存机制
-
浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
-
浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中(由响应头的缓存标识决定是否缓存)
强缓存
强缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程。
获取强缓存的三种情况:
-
不存在缓存结果和缓存标识--->强缓存失效,则直接向服务器发起请求。
-
存在缓存结果和缓存标识但结果已失效--->强缓存失效,则使用协商缓存。
- 如果修改了本地时间,可能会造成缓存失效,可以通过Cache-control: max-age指定最大生命周期。
-
存在缓存结果和缓存标识且结果未失效--->强缓存生效,直接返回该缓存。
当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control优先级比Expires高。
两类强缓存:
- 内存缓存(from memory cache):
- 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。
- 时效性:一旦该进程关闭,则该进程的内存则会清空。
- 硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。
在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取;而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存。
协商缓存
协商缓存就是强缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。
-
协商缓存生效: 返回304(即:该资源无更新),在浏览器获取缓存结果。
-
协商缓存失效: 返回200和请求结果结果,并将请求结果和缓存标识存入浏览器缓存。
协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。
总结
- 强制缓存优先于协商缓存进行,若强缓存生效则直接使用缓存,若不生效则进行协商缓存,协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回304,继续使用缓存。
说下进程、线程和协程
进程
- 保存在硬盘上的程序运行后,会在内存空间里面形成一个独立的内存体,这个内存体有自己独立的地址空间。上级挂靠的单位是操作系统。
- 操作系统以进程为单位,分配资源(cpu时间片,内存等),进程是操作系统资源分配的最小的单位。
线程
- 每个进程都有独立的内存空间,他们之间的切换是非常开销资源的。所以为了进一步平衡资源的调度,减少资源的开销,引进了线程,用于cpu资源的调度和执行。 他的上级挂靠单位是进程。
- 线程也被称为轻量级进程,是操作系统调度的最小单位,他的上级是进程。
协程
- 线程虽然提高了资源的利用率,但是也存在线程资源有限的情况,而且大多数线程资源都是处于阻塞状态,且线程切换虽然比进程切换资源开销的少,但是也是存在的开销的。这时候就引入了协程。协程完全是由程序自己调度的,而不是操作系统调度。
- 特点:
- 他是一个比线程更轻量级的存在,
- 核心特点:协程的核心在于调度那块由他自己来负责,遇到阻塞操作,立刻放弃掉,并且记录当前栈上的数据,阻塞完后立刻再找一个线程恢复栈并把阻塞的结果放到这个线程上去跑,等达到一定条件后,再恢复原来的栈信息继续执行。
总结
进程和线程
- 一个进程可以拥有多个(至少一个)线程,而一个线程只属于一个进程
- 进程是cpu资源分配的最小的单位,线程是cpu资源调度的最小的单位
- 进程和协程都可以并发执行
- 进程开销比线程更大:进程拥有自己的内存空间,线程不直接拥有资源,所以进程在创建销毁的时,操作系统要回收和分配资源,导致系统的开销更大。
- 进程更加的稳定安全,他有独立的内存空间,进程的崩溃不会影响到其他的进程,而线程的只是一个进程中的不同的执行路径。
线程和协程
- 都是用于资源的调度,不过线程是由操作系统来进行资源的调度,而协程的资源是由用户自己进行调度。
- 协程比线程效率更高:他的核心思想就是在线程的基础上进一步的提高资源调度的效率。
get和post的区别
get
GET请求用于从指定资源请求数据。
- GET请求可被缓存
- GET请求保留在浏览器历史记录中
- GET请求有长度限制
post
POST请求用于将数据发送到服务器来创建/更新资源。
- POST请求不会被缓存
- POST请求不会保留在浏览器历史记录中
- POST请求对数据长度没有要求
区别
- get 参数通过 url 传递,post 放在请求体中。
- get 请求在 url 中传递的有参数长度限制,而 post 没有。
- post 比 get 相对安全,因为参数直接暴露在 url 中,所以不能用来传递敏感信息。
- get 请求只能进行 url 编码,而 post 支持多种编码方式。
- get 请求参数会被完整保留在浏览历史记录里,而 post 中的参数不会被保留。
- GET 和 POST 本质上都是 TCP 链接,本质并无差别。
AST 抽象语法树
AST 是什么
抽象语法树,简称 AST,它是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
AST 有什么用
- 编辑器的错误提示、代码格式化、代码高亮、代码自动补全;
elint、pretiier对代码错误或风格的检查;webpack通过babel转译javascript语法;
总结
为了兼容低版本浏览器 我们也通常会使用 webpack 打包编译我们的代码将 ES6 语法降低版本,比如箭头函数变成普通函数。将 const、let 声明改成 var 等等,他都是通过 AST 来完成的,只不过实现的过程比较复杂,精致。不过也都是这三个主要过程:
- js 语法解析成 AST;
- 修改 AST;
- AST 转成 js 语法;
V8
V8 是由谷歌收购并使用 C++开发并开源的 javascript 虚拟机引擎
V8 运行环境
因为 V8 并不是一个完整的系统,所以宿主和 V8 共用同一套内存空间,在执行时,它要依赖于由宿主提供的基础环境(类似于寄生关系),大致包含了我们熟悉的全局执行上下文、事件循环系统、堆栈空间、宿主环境特殊定制的 API等。除了需要宿主提供的一些基础环境之外,V8 自身会使用创建好的堆和栈并提供 JavaScript 的核心功能(Object、Function、String)以及垃圾回收(GC)。
流程: 宿主启动主进程(浏览器渲染进程、node 进程) -> 宿主初始化并启动 V8
混合动态编译技术
解释执行:
- 代码 → 解析器转码中间代码AST → 解释器 -> 执行
- 启动块,运行慢
编译执行:
- 代码 → 解析器转码中间代码AST → 编译器 → 二进制机器码 -> 执行
- 启动慢,运行块
JIT 编译技术-动态编译
- 编译时和运行时不分离,是一种在运行的过程中对代码进行动态编译的技术。
为了在保证启动块的情况下,保持快速运行,JS通过引入JIT技术,并采用混合动态编译的方式来提升 JavaScript 的运行性能。
混合动态编译:实际就是将可能会再次使用的代码通过编译器编译成二进制码储存,在后续直接提供使用。
混合动态编译运行过程:
-
启动过程:解释执行,不过在解释阶段对代码进行监控,标记重复执行频率高(热点代码)
-
运行过程:编译执行,编译热点代码:
- 对可优化的代码进行一个编译转成二进制机器码并存储,之后就地复用二进制码减少解释器和机器的压力再执行。
反编译:在解释执行过程(启动过程)中如果遇到之前被标记的热点代码(即:使用过的代码),则将编译器编译后二进制码返回解释执行。
混合动态编译的好处
- 启动速度快:在 JavaScript 启动的时候采用解释执行的方式运行,利用了解释器启动速度快的特性
- 运行性能高:在 JavaScript 运行的过程中可以对代码进行监控,从而使用 JIT 技术对代码进行编译优化
字节码、解释器、编译器
-
字节码:代码需要被解析器解析为中间代码AST才能够被解释器和编译器使用,字节码允许被解释器直接执行。
-
解释器:边解释,边执行。
-
编译器:编译完后再统一执行。