DNS 的解析过程
- 浏览器缓存。当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的 IP 地址(曾经访问过该域名并且没有清空缓存)
- 系统缓存。当浏览器缓存中无域名对应的 IP 地址时,会自动检测用户计算机系统内的 Hosts 文件中是否存在域名对应的 IP 地址
- 路由器缓存。当浏览器和系统缓存中均无域名对应的 IP 地址时,则会进入路由器缓存中查找(上述三步均为客户端的 DNS 缓存 )
- ISP(互联网服务提供商)DNS 缓存。当在用户客户端找不到域名对应的 IP 地址时,会进入 ISP DNS 缓存中查找。比如你用的是电信网络,那么就会进入电信的 DNS 缓存服务器中查找
- 根域名服务器。当上述均未找到时,会进入根域名服务器中查找(全球仅有 13 台根域名服务器,其中 1 个主根域名服务器,其余 12 台为辅根域名服务器),根域名服务器在收到请求后会查看区域文件记录,如果没有,则将其管辖范围内的顶级域名服务器的 IP 地址告诉本地 DNS 服务器
- 顶级域名服务器。顶级域名服务器在收到请求后会查看域名文件记录,如果没有,则将其管辖范围内的主域名服务器的 IP 地址告诉本地 DNS 服务器
- 主域名服务器。主域名服务器在收到请求后会查询自己的缓存,如果没有,则进入下一级域名服务器中进行查找,重复该步骤直到找到正确的记录
- 保存结果至缓存。本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时将该结果反馈给客户端,客户端通过这个 IP 地址与 Web 服务器建立连接
TCP 为什么可靠?
- 超时重传机制、快速重传机制
- 流量控制(滑动窗口)
- 拥塞控制
超时重传
就是在发送数据时,设定一个定时器(RTO),当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据。该计时器的时间一般是由当前网络来决定的,一个 RTT 指的是当一个报文从发送到接收所需的时间,RTO 的取值是发送方尝试发送几个报文,然后取平均 RTT 时间来决定的。
TCP 会在以下两种情况发生超时重传:数据包丢失、确认应答丢失。
快速重传
快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
当接收方发现接收到的序列号不对的时候,会发送连续的三个 ACK 标志,告诉发送方这个报文在传输过程中出现了丢包。发送方如果接收到某个相同的序列号的三个 ACK 报文,那么此时立马重发该报文,不会等待计时器的时间结束。
滑动窗口
TCP 引入了窗口这个概念。即使在往返时间较长的情况下,它也不会降低网络通信的效率。
有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。
发送方通过维持一个发送滑动窗口来确保不会发生由于报文发送太快导致接收方无法及时处理的问题。此时发送方的报文分为四类:第一类是已经发送并且得到接收方确认的报文;第二类是已经发送但是没有接收到确认的报文;第三类是发送方还没发送,但是滑动窗口还足够巨大,允许被发送的报文;第四类是还没发送并且窗口已经被占满,不允许发送的报文。
流量控制
发送方不能无脑发数据给接收方,要考虑接收方的处理能力,否则就会导致触发重发机制,从而导致网络流量的无端浪费。
为了解决这种现象,TCP 提供一种机制可以让发送方根据接收方的实际接收能力,控制发送的数据量,这就是所谓的流量控制。
拥塞控制
流量控制是避免发送方的数据填满接收方的缓存,但是并不知道网络中发生了什么。
一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。
网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环,被不断地放大。
所以,TCP 不能忽略网络上发生的事,它被设计成一个无私的协议,当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量。
于是,就有了拥塞控制,控制的目的就是避免发送方的数据填满整个网络。
为了在发送方调节所要发送数据的量,定义了一个叫做拥塞窗口的概念。
拥塞窗口 cwnd (rwnd 表示接收窗口)是发送方维护的一个状态变量,它会根据网络的拥塞程度动态变化的。
拥塞控制主要是四个算法:慢启动、拥塞避免、拥塞发生、快速恢复。
HTTP 的状态码
- 信息状态码:1 开头
- 成功状态码:2 开头
- 重定向状态码:3 开头
- 客户端错误状态码:4 开头
- 服务端错误状态码:5 开头
- 200 状态码:成功获取资源(获取到服务器资源或者请求强缓存)
- 304 状态码:请求协议缓存资源
- 301 状态码:永久重定向,被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址,除非额外指定,否则这个响应也可缓存
- 302 状态码:临时重定向,请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求,只有在 Cache-Control 或 Expires 中进行了指定的情况下,这个响应才是可缓存的
- 401 状态码:未经授权,请求要求身份验证
GET 和 POST
- GET 一般是去请求获取资源,POST 一般是发送数据到后台使用
- GET 中的参数是拼接在 URL 后面的,不安全且长度有限制,POST 中的参数是放在 Request body 中的,相对安全且不受长度限制
- GET 在刷新浏览器或回退时不受影响,POST 在回退时会重新提交数据请求
- GET 请求可被缓存,POST 请求不会被缓存
- GET 请求只能进行 URI 编码,POST 请求支持多种编码方式
- GET 产生一个 TCP 数据包,POST 产生两个 TCP 数据包
- 对于 GET 请求,浏览器会把 HTTP HEADER 和 DATA 一并发送出去,服务器响应 200 OK(返回数据),而对于 POST 请求,浏览器会先发送 HEADER,待服务器响应 100 CONTINUE 之后,浏览器再发送 DATA,服务器响应 200 OK(返回数据)
其他请求方法
- GET:向指定的资源发出“显示”请求
- POST:向指定资源提交数据,请求服务器进行处理
- PUT:向指定资源位置上传其最新内容
- DELETE:请求服务器删除 Request-URL 所标识的资源
- TRACE:回显服务器收到的请求,主要用于测试或诊断
- CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器,通常用于 SSL 加密服务器的连接
TCP/IP 和 HTTP 协议的区别?
- TCP 处于传输层,HTTP 处于应用层
- HTTP 是基于 TCP 协议上的一种应用
- TCP 是底层协议,是定义数据传输和连接方式的规范
- HTTP 是应用层协议,是定义传输数据的内容的规范
对称加密和非对称加密
- 对称秘钥加密,又称私钥加密。即信息的发送方和接收方用同一个秘钥去加密和解密数据,它的最大优势是加/解密速度快,适合对大数据量进行加密,但秘钥管理困难
- 非对称秘钥加密,又称公钥加密。它需要使用一对秘钥来分别完成加密和解密的操作,一个公开发布,即公开秘钥,另一个由用户自己秘密保存,即私有秘钥,信息发送者用公开秘钥去加密,而信息接收者用私有秘钥去解密
- 从功能和安全角度而言,非对称加密比对称加密强大,但加/解密速度却比对称秘钥慢得多
HTTP 长连接和短连接的区别?
- HTTP 协议是无状态的,无状态是指 HTTP 协议对事务处理没有记忆能力,服务器不知道客户端的状态,每次发送请求都得重新建立连接
- HTTP/1.0 协议默认是短连接,客户端和服务器每进行一次 HTTP 请求就需要建立一次连接,结束后就中断连接,在访问网络中含有其他的 Web 资源时,每次去访问一个 Web 资源,浏览器就会新建一个 HTTP 会话
- HTTP/1.1 协议默认是长连接,保持其连接的持续性,在使用 HTTP 长连接的时候,HTTP 对应的响应头会有 Connection: keep-alive
- 长连接:当浏览器打开网页后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接通道不会关闭,等客户端再次访问服务器时,会继续使用已存在的连接。但是这个 Connection: keep-alive 也不会永远保持连接,保持的时间是由服务器设定的,实现长连接需要客户端和服务端都支持
- HTTP 协议的长连接和短连接实际上是 TCP 协议的长连接和短连接
轮询、长轮询、长连接、WebSocket 的区别?
- 轮询:客户端定时向服务器发送请求,服务器收到请求后立马返回响应信息并关闭连接。优点是后端程序编写比较容易,缺点是请求中有大半是无用的,会浪费带宽和服务器资源。这种方式由于需要不断建立 HTTP 连接,严重浪费了服务器和客户端的资源,短轮询不适合那些同时在线用户数量比较大,并且很注重性能的 Web 应用
- 长轮询:客户端向服务器发送请求,服务器收到请求后 hold 住连接,直到有新消息返回响应信息时(或到了设定的超时时间时)才关闭连接,客户端处理完响应信息后再向服务器发送新的请求。优点是减少很多不必要的 HTTP 请求次数,相比之下节约了资源,缺点是服务器 hold 连接会消耗资源,需要同时维护多个线程,而服务器所能承受的 TCP 连接数是有上限的,这种轮询很容易把连接数占满,例如:webQQ、facebook IM
- 长连接:HTTP/1.1 通过使用
Connection: keep-alive
进行长连接,HTTP/1.1 默认进行持久连接,在一次 TCP 连接中可以完成多个 HTTP 请求,但是每个请求仍然要单独发 HEADER - WebSocket:WebSocket 协议本质上是一个基于 TCP 的协议,为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发送一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,它包含了一些附加头信息,其中附加头信息
upgrade: WebSocket
表明这是一个申请协议升级的 HTTP 请求,服务器解析这些附加的头信息,然后产生响应信息返回给客户端,客户端和服务器的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由地传递信息,并且这个连接会一直持续到客户端或服务器任意一方主动关闭连接 - 兼容性:短轮询 > 长轮询 > 长连接 > WebSocket
- 性能:WebSocket > 长连接 > 长轮询 > 短轮询
Session、Cookie 和 Application 的区别?
- Cookie:Cookie 存放在客户端中,因此有效时间以客户端的时间为准,可以手动设置,如果没有指定 Cookie 对象的有效期,则 Cookie 对象只存在客户端的内存,当浏览器关闭时,Cookie 就会失效,Cookie 中保存的是字符串,支持跨域名访问
- Session:Session 是服务器技术,利用这个技术,服务器可以把与会话相关的数据写到一个代表会话的 Session 对象中,用来存储用户跨网页程序的变量,只针对单一用户,Session 的有效期可以自己设置,Session 保存的是对象,不支持跨域名访问
- Application:多个用户共享的应用级别的作用域,在服务器,相比前两者它的存在时间最长,只有当关闭服务器时才会失效
Cookie 是如何工作的?
当客户端访问服务器时,服务器通过 Cookie 构造器构造一个 Cookie 实类,然后服务器在响应信息头中附带 Cookie 实类到客户端,并保存在客户端内存中,在 Cookie 生命周期内,以后的每次请求都会携带这个 Cookie。
Cookie 和 Session 是如何记住登录状态的?
当 Session 启动时,服务器会生成一个唯一值,称为 sessionid,服务器会开辟一块内存,对应该 sessionid,服务器再将该 sessionid 写入浏览器的 Cookie,服务器内有一进程,监视所有 Session 的活动状态,如果有 Session超时或主动关闭,服务器就释放该内存块。当浏览器请求服务器时,服务器会先查看请求头 Cookie 中是否有 seesionid,如果有,就检查该 sessionid 对应的内存是否有效,有效则读取内存中的值,无效则建立新的 Session。
当 Cookie 被禁止时,如何保存登录状态?
保存登陆状态的关键不是 Cookie,而是通过 Cookie 保存和传送 sessionid 的值,我们可以通过 encodeURL 编码 URL,或者 access token,然后将其保存到 localstorage 即可。
什么是死锁以及死锁的解决办法?
- 死锁:是指两个或者两个以上的进程在执行过程中,由于竞争资源或者彼此通信而造成的一个阻塞现象,若无外力作用,它们都将无法继续推进下去
- 互斥条件:进程对所分配到的资源具有排它性,即一个资源只能被一个进程所占用,直到进程被释放
- 请求和保持条件:当进程因请求资源而进入阻塞时,对方获得的资源保持不放
- 不剥夺条件:进程已获得的资源在未使用完之前不能被剥夺,只能在使用完时由自己释放
- 环路等待条件:在发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞
- 解决办法:破坏任意的必要条件即可
Web 安全防范
- XSS:跨站脚本攻击,它指的是攻击者往 Web 页面里面插入恶意代码脚本,而网站在设计输入输出部分时,没有对用户的输入内容进行过滤,当用户浏览网站时,嵌入的 Web 脚本代码就会被执行,从而达到恶意攻击用户的特殊目的。防范手段有:设置
HttpOnly
,XSS 攻击主要是想获取到 Cookie 中的 sessionid 并劫持对话,而当 Cookie 设置为HttpOnly
属性时,js 脚本将无法获取到 Cookie 信息;输入输出检查过滤;HTML 代码转译 - CSRF:跨站请求伪造攻击,它通过伪装来自信任用户的请求来攻击信任网络,或者理解为盗用用户的身份,以用户的名义来进行某些非法操作。防范手段有:验证 HTTP
Referer
字段,在 HTTP 请求头中有一个Referer
字段,它记录了该 HTTP 请求的来源地址,只需要核对Referer
字段是否是合法来源即可,注意在 IE6 中可以修改Referer
值;在请求地址中添加token
,可以在 HTTP 请求中以参数加入一个随机的token
,并在服务器上建立一个拦截器来验证这个token
;在 HTTP 头中自定义属性并验证,其方法和 token 差不多,只是将其添加到 HTTP 头自定义属性中 - SQL 注入:攻击者在 HTTP 请求中注入恶意的 SQL 代码,服务器使用参数构建数据库 SQL 命令时,恶意的 SQL 会被一起构建,从而达成某些非法的操作。防范手段有:有效性的核查和过滤;限制字符串输入长度;服务器使用预编译的 PrepareStatement
为什么浏览器会产生同源策略?
同源策略的目的是为了限制不同源的 document 或者脚本之间互相访问,以免造成干扰和混乱。如果没有限制,那么浏览器中的 Cookie 等数据就可以被任意读取,不同域下的 DOM 任意操作,ajax 任意请求其他网站的数据包括隐私数据。
浏览器的缓存机制
- 强缓存:判断是否是缓存的依据,来自于是否超出某个时间或者某个时间段,而不关心服务器文件是否已经更新。强缓存主要分为两种:
Expires
和Cache-Control
,Expires
保存的是一个绝对时间,即缓存过期时间,它是相对服务器时间来定的,浏览器会判断本地时间和Expires
来确定是否要访问本地缓存,由于服务器时间和客户端时间在很多时候是不相等的,所以有可能会造成不确定性,现在几乎很少使用Expires
;Cache-control
保存的是一个相对时间,可以通过设置max-age
来确定文件的缓存时间,其原理和Expires
一样,当两者同时存在时,Cache-Control
的优先级高于Expires
- 协商缓存:由服务器来确定缓存资源是否可用,协商缓存主要分为两种:
Last-Modify
和ETag
,Last-Modify
:浏览器第一次访问服务器的时候,会在返回的请求头中加上一个Last-Modified
字段,它是资源的最后修改时间,当浏览器再次访问时,其请求头中会包含一个If-Modified-Since
字段,服务器判断它是否相同再确定是返回 304 还是 200;ETag
:其原理和Last-Modify
几乎一样,只是它返回的是一般是哈希值或是对该资源的 md5 值(生成规则由服务器决定),它会在请求头携带If-None-Match
字段,如果两者同时存在,ETag
的优先级高于Last-Modify
,Last-Modified
只能精确到秒,秒内的内容更新只有ETag
才能检查,文件有时会定时重新生成相同的内容,Last-Modified
不能很好地识别,ETag
每次服务器生成都需要进行读写操作,而Last-Modified
只需要读取操作,Etage
的消耗更大 - 如果有强缓存,且强缓存未过期,浏览器会优先访问强缓存,并返回 200,在没有强缓存或者强缓存过期的情况下才会访问协商缓存,再根据
Etag
或Last-Modified
返回 304 或 200
浏览器从输入 URL 到页面渲染的过程
- 浏览器输入 URL 并回车
- 浏览器查找 URL 是否存在未过期的缓存
- 域名解析 URL 对象的 IP
- 根据 IP 和服务器建立 TCP 三次握手连接
- 客户端发送 HTTP 请求
- 服务器处理请求并返回响应报文
- 浏览器接收 HTTP 响应
- 构造 DOM 树、渲染页面
- 四次挥手关闭 TCP 连接
TCP 建立连接的过程
TCP/IP 三次握手
- 第一次握手。建立连接时,客户端发送 SYN 包(seq=x)到服务器,进入
SYNC_SEND
状态,等待服务器确认 - 第二次握手。服务器收到 SYN 包,必须先确认客户端的 SYN,同时服务器发送 SYN 包(ack=x+1,seq=y),进入
SYN_RECV
状态 - 第三次握手。客户端收到服务器的 SYN + ACK 包,并向服务器发送 ACK 确认包(ack=y+1),客户端和服务器都进入
ESTABLISHED
状态
如果建立连接之后客户端出现故障
TCP 设有一个 保活计时器
,服务器在每收到一次客户端的请求后都会重置该计时器,时间通常设置为 2 小时,若 2 小时后还没有收到客户端的任何数据,服务器就会发送一个探测报文段,之后每隔 75s 再发送一次,若连续发送 10 个探测报文段都仍未有响应,那么服务器便认为客户端出现故障并主动关闭连接。
TCP/IP 四次挥手
- 第一次挥手。客户端发送(FIN=1,seq=u),进入
FIN-WAIT-1
状态 - 第二次挥手。服务器收到报文后,发送确认报文(ACK=1,ack=u+1),进入
CLOSE_WAIT
状态 - 第三次挥手。服务器发送(FIN,seq=w)用来关闭客户端与服务器之间的数据传送,进入
LAST_ACK
状态 - 第四次挥手。客户端收到 FIN 包后进入
TIME_WAIT
状态,并发送 ACK 确认报文(ack=w+1)给服务器,服务器接收后进入CLOSED
状态,完成四次挥手
为什么客户端发送完最后一个 ACK 包后并不会立刻进入 CLOSED
状态,而是会等待 2MSL(最长报文段寿命)
如果网络是不可靠的,那么有可能最后一个 ACK 会丢失,而服务器将会不断重复地发送 FIN 片段,所以客户端不能立即关闭,它必须确认服务器已经接收到了该 ACK,TIME_WAIT
状态就是用来重发可能丢失的 ACK 报文,客户端会设置一个计时器并等待 2MSL 的时间,如果在该时间内再次收到 FIN,那么客户端就会重新发送一个 ACK 并再次等待 2MSL。所谓的 MSL就是一个片段在网络中的最大存活时间,2MSL 就是发送和回复所需要的最大时间,如果直到 2MSL 时间后客户端都没有再次收到 FIN,那么客户端就判断 ACK 已经被成功接收并结束 TCP 连接。
为什么要三次握手
主要是为了防止失效的连接请求报文段被服务器接收,从而产生错误,如果建立连接只需要两次握手,客户端没有太大改变,还是需要获得服务器的响应后才进入 ESTABLISHED
状态,而服务器则是在接收到连接请求后就进入 ESTABLISHED
状态。此时如果网络阻塞导致客户端发送的连接请求迟迟不到服务器,客户端便会超时重发,这时如果服务器正确接收并响应了,双方便开始通信,通信结束后释放连接。此时如果那个失效的连接请求抵达了服务器,由于只存在两次握手,那么服务器就会再次进入 ESTABLISHED
状态,等待发送数据或主动发送请求,但此时客户端已经进入 CLOSED
状态,那么服务器就会一直等待下去,浪费连接资源。
为什么要四次挥手
四次挥手中的第二次和第三次是分开的,而不是像三次握手那样一起发送,因为服务器发送报文段二的时候只是确认客户端发送的结束报文,并不代表自身的数据已经传输完毕,由于时间是不确定的,如果硬是等到自身数据传输完毕后再将确认报文 ACK 和自身结束报文 FIN 一起发送,可能会导致客户端超时重传等问题,造成资源浪费。
什么是线程,线程和进程的区别?
- 进程和线程都是一个时间段的描述,是 CPU 工作时间段的描述,它们只是颗粒大小不同
- 进程是资源分配和调度的一个独立单位,而线程是 CPU 调用的基本单位
- 同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程
- 进程的创建调用 fork 或者 vfork,而线程创建调用 pthread_create,进程结束后它拥有的所有线程都将销毁,而线程结束不会影响同个进程中的其他线程
- 线程是轻量级的进程,它的创建和销毁所需的时间比进程小很多,操作系统中所有的执行功能都是创建线程去完成的
- 线程中执行时一般都要进行同步和互斥,因为它们共享同一进程的所有资源
- 线程有自己的私有属性:TCB、线程 id、寄存器、硬件上下文,而进程也有自己的私有属性:进程控制块 PCB,这些私有属性是不被共享的,是一个进程或一个线程的标志
let、const 和 var 的区别?
- var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会
- var 声明的变量存在变量提升,let 和 const 不存在变量提升
- let 和 const 声明形成块状作用域
- 同一作用域下,let 和 const 不能声明同名变量,而 var 可以
- const 一旦声明必须赋值,且声明后不能修改,如果声明的是复合类型数据,则可以修改其属性
介绍一下 promise 相关的东西
- pedding、fulfilled、rejected(未决定、履行、拒绝),同一时间只能存在一种状态,且状态一旦改变就不能再变
- 初始化,状态:pedding
- 当调用 resolve(成功),状态:pedding -> fulfilled
- 当调用 reject(失败),状态:pedding -> rejected
js 判断类型的方法
- typeof:只能判断基本数据类型,判断引用类型都是 object
- instanceof:只能进行类型的对比,不能进行类型的判断,测试后者是否是前者的原型链上的构造函数
- Object.prototype.toString.call(arr):能较为准确判断数据类型,测试该该对象是否是括号中函数的原型
New 一个对象的过程
- 创建一个空对象 obj
- 将 obj 的 prototype 属性指向构造函数原型(prototype)
- 将构造函数内部的 this 绑定在新对象 obj 上,执行构造函数(用 apply 方法绑定 this)
- 若构造函数没有返回其他对象,则返回该新建的 obj,否则返回引用类型的值
原型链
_proto_
属性(隐式原型):它的值就是它所对应的原型对象,作用是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的_proto_
属性所指向的那个对象里找,一直向上找直到_proto_
属性的终点 null,再往上找相当于在 null 上取值,会报错,通过_proto_
属性将对象连接起来的这条链路即我们所说的原型链Prototype
属性(显示原型):是函数的原型对象,作用就是让该函数所实例化的对象们可以找到公用的属性和方法Constructor
属性:是指向该对象的构造函数,所有函数的最终构造函数都是 Function
JSON.parse(JSON.stringify()) 深拷贝的弊端
- JSON.parse(JSON.stringify()) 实现深拷贝,其原理就是利用 JSON.Stringify 将 js 对象序列化,再反序列化还原对象,序列化的作用是存储(对象本身存储的只是一个地址映射,如果断电则对象将不复存在,因此需要将对象的内容转换成字符串的形式再保存在磁盘上)和传输
- 如果 obj 里面有时间对象,用其处理后返回的只是字符串形式的数据
- 如果 obj 里有 RegExp、Error 对象,则序列化的结果会变成空对象
- 如果 obj 里有函数、undefined,则序列化的结果会把函数或 undefined 丢失
- 如果 obj 里有 NaN、Infinity 和 -Infinity,则序列化的结果会变成 null
- JSON.stringify 只能序列化可枚举的自由属性,例如:如果 obj 中的对象是由构造函数生成的,则实行深拷贝后会丢失对象的 constructor
- 如果对象中存在循环引用的情况也无法正确实现深拷贝
深拷贝
使用递归的方式实现深拷贝
function deepClone1(obj) {
// 判断要进行深拷贝的是数组还是对象, 是数组的话进行数组拷贝, 对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
通过 JSON 对象实现深拷贝
var _obj = JSON.parse(JSON.stringify(obj))
通过 Jquery 的 extend 方法实现深拷贝
$.extent(true, {}, obj);
通过 Object.assign() 拷贝
// 当对象中只有一级属性没有二级属性的时,此方法为深拷贝, 但是对象中有对象的时候, 此方法在二级属性以后就是浅拷贝
var _obj = Object.assign({}, obj);
Lodash 函数库实现深拷贝
_.CloneDeep()
两个对象比较
function diff(val1, val2) {
var o1 = val1 instanceof Object;
var o2 = val2 instanceof Object;
if (!o1 || !o2) {
return val1 === val2;
}
if (Object.keys(val1).length !== Object.keys(val2).length) {
return false;
}
for (var o in val1) {
var t1 = val1[o] instanceof Object;
var t2 = val2[o] instanceof Object;
if (t1 && t2) {
diff(t1, t2)
} else if (val1[o] !== val2[o]) {
return false;
}
}
return true;
}
HTTPS 的工作流程
- 客户端(浏览器)访问 https://www.baidu.com 百度网站
- 百度服务器返回 HTTPS 使用的 CA 证书
- 浏览器验证 CA 证书是否为合法证书
- 验证通过,证书合法,生成一串随机数并使用公钥(证书中提供的)进行加密
- 发送公钥加密后的随机数给百度服务器
- 百度服务器拿到密文,通过私钥进行解密,获取到随机数(公钥加密,私钥解密,反之也可以)
- 百度服务器把要发送给浏览器的内容,使用随机数进行加密后传输给浏览器
- 此时浏览器可以使用随机数进行解密,获取到服务器的真实传输内容。
这就是 HTTPS 的基本运作原理,使用对称加密和非对称机密配合使用,保证传输内容的安全性。