面试问题整理
计算机网络
OSI七层模型
应用层 :为应用程序提供服务,并管理应用程序之间的通信(SMTP、HTTP、FTP)
表示层 :处理数据的标识问题,比如编码、格式转化、加密解密等
会话层 :负责建立管理和断开通信连接,实现数据同步
传输层 :端到端传输数据,同时处理传输错误、控制流量等(TCP UDP)
网络层 :地址管理、路由选择(IP协议)
数据链路层 :数据分割成帧,mac寻址、差错校验、信息纠正等。(以太网)
物理层 :利用传输介质为数据链路层提供物理连接
发送端从应用层 → 物理层 打包发送
接收层从物理层 → 应用层 解析获取
路由器工作在哪一层
网络层。
路由器是连接因特网中各局域网、广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,先后顺序发送信号。
路由和交换机最主要的区别就是交换机发生在OSI参考模型第二层(数据链路层),而路由发生在第三层(网络层)。
这一区别决定了路由和交换机在移动信息的过程中需要使用不同的控制信息,所以两者实现各自功能的方式是不同的。
DNS解析过程
- 根DNS服务器:返回顶级域名 DNS 服务器的 IP 地址
- 顶级域DNS服务器:返回权威域名 DNS 服务器的 IP 地址
- 权威DNS服务器:返回相应主机的 IP 地址
以输入 www.google.com
为例,
1.浏览器检查自身缓存,有无解析此域名对应的ip
2.操作系统缓存hosts文件中查询
3.没有的话,请求本地域名服务器(LDNS)解析域名(一般在城市某处,距离不会太远)
4.如果还没有的话,就去根DNS域服务器查询,此时会给出.com的顶级域名服务器
5.然后去.com服务器查询,此时会给出这个域名google.com的地址,这是网站注册的域名服务器
6.去NameServer查询,根据映射关系表找到目标IP,返回给LDNS(LDNS缓存域名及IP)
7.LDNS解析结果返回用户(缓存到系统缓存中),域名解析结束
TCP与UDP
UDP 与 TCP 的区别是什么?
UDP 协议 是面向无连接的,不需要在正式传递数据之前先连接起双方。 UDP 协议只是数据报文的搬运工,不保证有序且不丢失的传递到对端,并且UDP 协议也没有任何控制流量的算法,总的来说 UDP 相较于 TCP 更加的轻便。一般可以用于直播、即时通讯、即时游戏等。
TCP 无论是建立连接还是断开连接都需要先需要进行握手。在传输数据的过程中,通过各种算法保证数据的可靠性,当然带来的问题就是相比 UDP 来说不那么的高效。
TCP建立连接–三次握手
起初,两端都为 CLOSED 状态。在通信开始前,双方都会创建 TCB。 服务器创建完 TCB 后便进入 LISTEN 状态,此时开始等待客户端发送数据。
第一次握手
客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。
为什么 TCP 建立连接需要三次握手,明明两次就可以建立起连接
防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。
客户端发送了一个连接请求 A,但是因为网络原因造成了超时,这时 TCP 会启动超时重传的机制再次发送一个连接请求B。此时请求顺利到达服务端,服务端应答完就建立了请求,然后接收数据后释放了连接。
假设这时候连接请求 A 在两端关闭后终于抵达了服务端,那么此时服务端会认为客户端又需要建立 TCP 连接,从而应答了该请求并进入 ESTABLISHED 状态。但是客户端其实是 CLOSED 的状态,那么就会导致服务端一直等待,造成资源的浪费。
TCP断开连接–四次握手
TCP 是全双工的,在断开连接时两端都需要发送 FIN 和 ACK。
第一次握手
若客户端 A 认为数据发送完成,则它需要向服务端 B 发送连接释放请求。
第二次握手
B收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,此时表明 A 到 B 的连接已经释放,不再接收 A 发的数据了。但是因为 TCP 连接是双向的,所以 B 仍旧可以发送数据给 A。
第三次握手
B如果此时还有没发完的数据会继续发送,完毕后会向 A 发送连接释放请求,然后 B 便进入 LAST-ACK 状态。
第四次握手
A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。当 B 收到确认应答后,也便进入 CLOSED 状态。
PS:通过延迟确认的技术(通常有时间限制,否则对方会误认为需要重传),可以将第二次和第三次握手合并,延迟 ACK 包的发送。
为什么客户端 A 要进入 TIME-WAIT 状态,等待 2MSL 时间后才进入 CLOSED 状态?
为了保证 B 能收到 A 的确认应答。若 A 发完确认应答后直接进入 CLOSED 状态,如果确认应答因为网络问题一直没有到达,那么会造成 B 不能正常关闭。
TCP如何解决数据丢包或报文顺序不对的问题?
TCP有ARQ超时重传机制。
- 一种是停止等待ARQ。
比如说A向B发送一个报文,同时启动定时器,如果超时就重新发送。B如果收到相同序号的报文就会丢弃重新应答。A接受相同序号应答也丢弃。
这种是单个单个传的,效率就比较低,但也不会有那种多个丢包的情况。 - 另一种,高效一点,连续ARQ。
它用的窗口,A持续发这个窗口内的数据,B累积确认,收到多个后统一应答A,ack标志告诉A,这个序号之前的数据已经收到了。但是如果A收到3个重复的ack,那就说明有失序或丢包的情况,就会启用快速重传/快速恢复。
快速重传TCP taho ,阈值设为当前窗口一半,窗口设为1开始慢开始,重新传送。
快速恢复TCP Remo,机制是窗口减半,阈值为当前窗口,启用拥堵避免。不过,它是重发接收端要的包,接受收到一个Ack就退出,如果丢了很多个包就尬住再3ack触发一遍。
因此快恢复进行了优化——TCP New Reno。它是区别在于它记下了这个发送段的最大序号,并且每次都比对。
比如说1-10,丢包丢了4,7。最大序号就是10。接受方发的ack包是4,发送方发4,接受方收到4,发7。那么发送方就会对比7和10,知道是丢了多个包,发7。接受方收到,发11,发送方收到11后,对比10,比10大就退出快恢复阶段了。
TCP如何实现流量控制的
通过滑动窗口和拥堵窗口实现的。
滑动窗口主要是用于接收方,保证接收方能够接受数据。接收方通过报文告知发送方当前接收窗口剩余大小,发送窗口根据该值变化大小滑动窗口(待发送区)发送报文。
拥堵窗口,主要用于网络,防止过多的数据拥堵网络,避免负载过大的情况。
Http协议
HTTP(HyperText Transfer Protocol)即超文本传输协议,是一种详细规定了浏览器和万维网服务器之间互相通信的规则,它是万维网交换信息的基础,它允许将HTML(超文本标记语言)文档从Web服务器传送到Web浏览器。
HTTP协议目前最新版的版本是1.1,HTTP是一种无状态的协议,无状态是指Web浏览器与Web服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后Web服务器返回响应(Response),连接就被关闭了,在服务器端不保留连接的有关信息。也就是说,HTTP请求只能由客户端发起,而服务器不能主动向客户端发送数据。
HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。
HTTP工作原理:
HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。
Web服务器有:Apache服务器,IIS服务器(Internet Information Services)等。
Web服务器根据接收到的请求后,向客户端发送响应信息。
HTTP默认端口号为80,但是你也可以改为8080或者其他端口。
HTTP三点注意事项:
HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTP请求与响应
HTTP遵循请求(Request)/应答(Response)模型,Web浏览器向Web服务器发送请求时,Web服务器处理请求并返回适当的应答。
HTTP请求:
POST /test.php HTTP/1.1 //请求行
HOST:www.test.com //请求头
User-Agent:Mozilla/5.0 (windows NT 6.1;rv:15.0)Gecko/20100101 Firefox/15.0 //空白行,代表请求头结束
Username=admin&password=admin //请求正文
1
2
3
4
HTTP请求包括三部分,分别是请求行(请求方法)、请求头(消息报头)和请求正文。
HTTP请求第一行为请求行,由三部分组成,第一部分说明了该请求时POST请求,第二部分是一个斜杠(/login.php),用来说明请求是该域名根目录下的login.php,第三部分说明使用的是HTTP1.1版本。
HTTP请求第二行至空白行为请求头(也被称为消息头)。其中,HOST代表请求主机地址,User-Agent代表浏览器的标识,请求头由客户端自行设定。
Post和Get有什么区别?
从用法上说,Post一般用于无副作用、幂等的场景;Post多用于有副作用、不幂等的情况。
冥等的定义:发送M和N次请求,服务器上资源状态一致。比如说,注册10个账号和11个账号是不冥等的,对文章进行了10次11次修改是幂等的,因为前者多了一个账号(资源),后者是更新同一个资源。
副作用的定义:副作用是指对服务器上资源做改变。比如搜索是无副作用的,但更新是有副作用的。
从本质上说,Post和Get都取决于http,使用哪个方法与应用层传输没有必然的联系。HTTP没有要求,如果是POST,数据就要放在BODY中。也没有要求GET,数据(参数)就一定要放在URL中而不能放在BODY中。
细节上有一些区别:
Get能请求缓存,但是Post不可以
Post支持更多编码类型
Get回退无害,Post会再次提交
Get能被保存为书签,Post不可以
由于浏览器Url有限制,所以Get的长度受限,但Post不受限(因为都在Body里)
Http常用首部
- 通用
- cache-control : 控制缓存行为
- connection : 连接的性质,比如keep-alive
- user-Agent :用户信息
- Date :报文创建时间
- 请求
- Referrer Policy : 表示来源的(浏览器所访问的前一个页面),可以用于辅助检测crsf攻击,一般浏览器的默认值是no-referrer-when-downgrade,意思是https降级http的时候不传原地址。
- Accept : 能正确接收的媒体类型
- Accept-XX(Accept-Charset/Accept-Encoding/Accept-Language):能正确接收的xx
- Expect :期待服务端的指定行文
- If-Match :两端资源标记比较
- If-Modified-Since : 比较时间 未修改返回304 Not Modified
- If-None-Match :比较标记 未修改返回304 Not Modified
- 响应
- Location : 重定向到某个location
- Server : 服务器名字
- Age :响应存在时间
- Accept-Ranges :可以接受的范围类型
http请求中connection=keep-alive
的意义在哪里
HTTP 是基于 TCP 的,每一个 HTTP 请求都需要进行三次握手。如果一个页面对某一个域名有多个请求,就会进行频繁的建立连接和断开连接。所以HTTP 1.0 中出现了Connection: keep-alive
,用于建立长连接。Keep-Alive 模式更加高效,因为避免了连接建立和释放的开销。但是,长时间的TCP连接容易导致系统资源无效占用,配置不当的keep-alive有时比重复利用连接带来的损失还更大。所以,正确设置keep-alive timeout时间非常重要。
http请求中cache-control有哪些参数可以设置
Public :表示任何缓存都可以缓存响应
private :表示响应仅供单个用户使用,不得由共享高速缓存存储。私有缓存可以存储响应。
no-cache : 强制缓存在发布缓存副本之前将请求提交到源服务器以进行验证。
no-store: 缓存不应存储有关客户端请求或服务器响应的任何内容。
状态码
1xx:通知
- 100 客户端重新发起请求
- 101 更换协议
2xx:成功
- 200 请求成功
- 201create 按照客户端请求创建一个新的资源
- 202Accept 请求无法或不被及时处理
- 204No Content 请求成功,但是报文不含实体的主体部分
- 205Reset Content 客户端充值内容
- 206Partial Content 进行范围请求
3xx:重定向
- 301 Move Permanently 永久重定向,资源被分配到新的URL
- 302 Found 临时重定向
- 303 See Other,表示资源存在另一个URL
- 304 Not Modified 允许访问资源,和缓存有关
- 307 Temporary Redirect 临时重定向
4xx:客户端错误
- 400 请求报文的语法错误
- 401 没有权限
- 403 Forbidden 资源存在但被拒绝访问
- 404 找不到请求的资源
- 405 Method Not Allowed 不支持的请求方法,比如只支持 Get
5xx:服务器错误
- 500 服务器内部错误,异常
- 501 不支持请求的方法
- 502 Bad Gateway 代理服务器发生问题
- 503 Server Unavailable 服务器暂时处于超负荷状态
GET/POST 区别
1、从浏览器的角度
GET:
- “读取”一个资源,比如GET到一个html文件
- GET因为是读取,就可以对GET请求的数据做缓存,参数保留在浏览器历史中。
- 对数据长度、数据类型的限制
- 在 URL 中对所有人都是可见的
- 用于获取信息,是无副作用的,是幂等的,且可缓存
POST:
- 用于修改服务器上的数据,有副作用,非幂等,不可缓存
- 数据不会显示在URL中
- 数据会被重新提交(浏览器应该告知用户数据会被重新提交)。
2、从协议的角度
- GET 和 POST 是HTTP协议上的两种请求方式,而 HTTP 协议是基于 TCP/IP 的应用层协议,无论 GET 还是 POST,用的都是同一个传输层协议,所以在传输上,没有区别。
- GET 方法的参数应该在 url 中,POST 方法参数应该放在 body 中
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);对于POST,浏览器先发送header,服务器响应100(continue),然后再发送data,服务器响应200(返回数据);
POST 的提交数据方式
application/x-www-form-urlencoded
,原生的<form>
提交的数据按照key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。multipart/form-data
,表单上传文件时的提交方式- application/json,告诉服务端消息主体是序列化后的 JSON 字符串
- text/xml,传输 XML 格式的数据
http/https
HTTP 缺省工作在TCP协议80端口,用户访问网站http:// 打头的都是标准HTTP服务,HTTP所封装的信息是明文的,通过抓包工具可以分析其信息内容,如果这些信息包含有你的银行卡帐号、密码,你肯定无法接受这种服务,那有没有可以加密这些敏感信息的服务呢?那就是HTTPS!
HTTPS缺省工作在TCP协议443端口,它的工作流程一般如以下方式:
1) 完成TCP三次同步握手
2) 客户端验证服务器数字证书,通过,进入步骤3
3) DH算 法协商对称加密算法的密钥、hash算法的密钥
4) SSL 安全加密隧道协商完成
5)网页以加密的方式传输,用协商的对称加密算法和密钥加密,保证数据机密性;用协商的hash
算法进行数据完整性保护,保证数据不被篡改
如果HTTPS是网银服务,以上SSL安全隧道成功建立才会要求用户输入账户信息,账户信息是在安
全隧道里传输,所以不会泄密!
https 加密
对称加密:双方都持有同一把密钥,传输数据是通过各自的密钥进行加密,将加密的数据进行传输,然后再用密钥进行解密。但是有个问题就是,密钥在第一次传输的过程中,如果被拦截,这样加密的数据也很容易被截获。因此就需要保证在传输密钥的时候也保证安全。算法 DES、AES等等。。。。
非对称加密:首先服务端会先存放一个公钥和私钥,客户端向浏览器发送一个请求,拿到公钥。客户端会生成一个 key,然后使用公钥对这个 key 进行加密,再将这个 key 发回给服务端。服务端拿到 key 之后,使用私钥对这个 key 进行解密,之后双方都使用这个 key 进行对称加密传输。即便第三方获取了公钥和加密后的 KEY,在没有私钥的情况下也无法破解 KEY (私钥存在服务器,泄露风险极小),也就保证了接下来对称加密的数据安全。
整个过程就是:
- 浏览器请求 https 的网址后,一般会访问服务器的 443 端口
- 服务器会将证书发送给客户端,证书里面有服务器的信息、过期时间。证书需要去申请,下发后会有公钥和私钥,私钥由服务端自己保存,不可泄漏。公钥则是附带在证书的信息中,可以公开的。证书本身也附带一个证书电子签名,这个签名用来验证证书的完整性和真实性,可以防止证书被篡改。
- 客户端验证证书后,会根据上面的非对称加密步骤,生成随机的 key,在建立了安全的通信后,双方再开始以对称加密的形式进行数据传输。
SSL / TLS 握手详细过程
- “client hello”消息:客户端通过发送”client hello”消息向服务器发起握手请求,该消息包含了客户端所支持的 TLS 版本和密码组合以供服务器进行选择,还有一个”client random”随机字符串。
- “server hello”消息:服务器发送”server hello”消息对客户端进行回应,该消息包含了数字证书,服务器选择的密码组合和”server random”随机字符串。
- 验证:客户端对服务器发来的证书进行验证,确保对方的合法身份,验证过程可以细化为以下几个步骤:
- 检查数字签名
- 验证证书链 (这个概念下面会进行说明)
- 检查证书的有效期
- 检查证书的撤回状态 (撤回代表证书已失效)
- “premaster secret”字符串:客户端向服务器发送另一个随机字符串”premaster secret (预主密钥)”,这个字符串是经过服务器的公钥加密过的,只有对应的私钥才能解密。
- 使用私钥:服务器使用私钥解密”premaster secret”。
- 生成共享密钥:客户端和服务器均使用 client random,server random 和 premaster secret,并通过相同的算法生成相同的共享密钥 KEY。
- 客户端就绪:客户端发送经过共享密钥 KEY加密过的”finished”信号。
- 服务器就绪:服务器发送经过共享密钥 KEY加密过的”finished”信号。
- 达成安全通信:握手完成,双方使用对称加密进行安全通信。
三次握手
HTTP 1.1 请求方法
序号 | 方法 | 描述 |
---|---|---|
1 | GET | 请求指定的页面信息,并返回实体主体。 |
2 | HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
4 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
5 | DELETE | 请求服务器删除指定的页面。 |
6 | CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
7 | OPTIONS | 允许客户端查看服务器的性能。 |
8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
9 | PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
HTTP 2.0
1、新的二进制格数数据,请求分为:Length、Type、Flag、Stream、Playload
2、多路复用 (Multiplexing),允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。
3、首部压缩(Header Compression)
4、服务端推送
HTTP 请求头
1 | Accept: text/html,image/* -- 浏览器接受的数据类型 |
TCP与UDP区别
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
对称加密和非对称加密的区别
对称加密是服务器和客户端加密和解密使用同一把密钥,DES算法。非对称加密是客户端会向服务端请求一个公钥,使用公钥对服务器加密后,服务器会使用私钥对数据进行解密,RSA 算法。
对称加密优缺点:对称加密相比非对称加密算法来说,加解密的效率要高得多、加密速度快。但是缺陷在于对于密钥的管理和分发上比较困难,不是非常安全,密钥管理负担很重。
非对称加密优缺点:安全性更高,公钥是公开的,密钥是自己保存的,不需要将私钥给别人。缺点:加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
浏览器的地址栏输入URL并按下回车的过程
1、浏览器的地址栏输入URL并按下回车。
2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
3、DNS解析URL对应的IP。
4、根据IP建立TCP连接(三次握手)。
5、HTTP发起请求。
6、服务器处理请求,浏览器接收HTTP响应。
7、渲染页面,构建DOM树。
8、关闭TCP连接(四次挥手)。
跨域问题如何解决
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
DOM 具有同源策略,即禁止对不同源页面DOM进行操作。
前台解决
前台使用 jsonp 解决,JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。
核心思想:网页通过添加一个<script>
元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
1 |
|
【1】设置document.domain解决无法读取非同源网页的 Cookie 问题
因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie(此方案仅限主域相同,子域不同的跨域应用场景。)
【2】跨文档通信 API:window.postMessage()
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
- 上面三个场景的跨域数据传递
CORS
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
【服务端设置】
服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
2、带cookie跨域请求:前后端都需要进行设置
xhr.withCredentials = true;
1 | $.ajax({ |
服务器端
服务器端可以设置 Access-Control-Allow-Origin,使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。
还可以使用 Ngnix 代理,配置之后就不需要前端做什么修改了,一般我们在前后端分离项目中开发阶段会采用这种方式
浏览器缓存
阅读这篇博客,总结的非常好。
- 强制缓存
- 强缓存生效时的区别在于新标签打开为
from disk cache
,而当前页刷新派生资源是from memory cache。
- 强缓存生效时的区别在于新标签打开为
- 协商缓存
cookie,session和localStorage,sessionStorage的区别
cookie 和 session
cookie机制:如果不在浏览器中设置过期事件,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称为会话cookie。如果在浏览器中设置了cookie的过期事件,cookie会被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期事件结束才消失。cookie是服务端发给客户端的特殊信息,cookie是以文本的方式保存在客户端,每次请求时都带上它。
session机制:当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。如果用户禁用cookie,则要使用URL重写,可以通过response.encodeURL(url)进行实现;API 对 encodeURL 的结束为,当浏览器支持cookie时,url不做任何处理;当浏览器不支持cookie的时候,将会重写 URL 将sessionid拼接到访问地址后。
cookie保存在浏览器端,session保存在服务器端;单个cookie保存的数据不能超过4kb;session大小没有限制。
session大小没有限制;cookie只能保存字符串类型,以文本的方式。session通过类似与Hashtable的数据结构来保存,能支持任何类型的对象(session中可含有多个对象)。
localStorage和sessionStorage的区别
localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。localStorage除非主动删除数据,否则数据永远不会消失。sessionStorage的生命周期是仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。
请求头的Cache-control 字段
Cache-Control
Cache-Control 是最重要的规则。常见的取值有private、public、no-cache、max-age,no-store,默认为private。
private: 客户端可以缓存
public: 客户端和代理服务器都可缓存(前端的同学,可以认为public和private是一样的)
max-age=xxx: 缓存的内容将在 xxx 秒后失效
no-cache: 需要使用对比缓存来验证缓存数据(后面介绍)
no-store: 所有内容都不会缓存,强制缓存,对比缓存都不会触发(对于前端开发来说,缓存越多越好,so…基本上和它说886)
扫码登录原理
以淘宝为例,当点击扫码登录时,网页首先会请求二维码服务器,生成一张二维码。请求与响应如下图:
1 | // 响应 |
可以看到,响应返回了二维码的URL,以及一个重要的参数lgToken
,这是是网页的唯一ID。
这时,网页开始不断的轮询,判断用户是否扫码登录,每次的请求轮询都会将lgToken
带上。
轮询的请求参数如下:
响应回来的是一段 jsonp 代码:
1 | // 轮询响应 |
当轮询时发现二维码过期时,则轮询停止。响应的 code 为 10004,message 为 QRCode expired!code=1, msg=data not exist
1 | // 二维码过期响应 |
点击刷新二维码,网页会再次请求二维码接口,生成新的二维码,并再次开启轮询。
当用户使用手机扫描二维码后,响应 code 变为10001,并提示扫描成功。
当用户在手机上点击确认登录时,会将用户信息以及二维码上的信息传到服务器并重新生成令牌token。当网页端再次轮询请求接口时,就返回真正的登陆态Token,网页端此时就可以凭着这个Token来登陆了。
轮询和长轮询
polling即轮询,是指浏览器通过周期性轮询,查看服务器是否有更新的信息;
long polling指的是长轮询,浏览器与服务器建立连接之后,服务器将此连接进行挂起,但有更新信息时,再将信息发送给浏览器端。浏览器端重新建立连接,如此循环反复,这是一种长连接的方式。
1 | 轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。 |
操作系统
线程和进程的区别
做个简单的比喻:进程=火车,线程=车厢
- 线程在进程下行进(单纯的车厢无法运行)
- 一个进程可以包含多个线程(一辆火车可以有多个车厢)
- 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
- 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
- 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
- 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
- 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
- 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-”互斥锁”
- 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
进程
是什么:
先说概念:进程是os调度资源的最小单位(资源就是cpu的计算)
进程是程序的一次执行过程,是程序在执行过程中的分配和管理资源的基本单位,每个进程都有自己的地址空间,线程至少有 5 种状态:初始态、执行态、等待态、就绪态、终止态。
线程是进程中的一部分,一个进程可以有多个线程,但线程只能存在于一个进程中。
为什么:
为什么有进程这个东西?
以前的os没有进程的,一次只能运行一个程序,单独享用cpu,
举个例子:我现在在打字聊天的同时在语音通话,
如果一次执行一个程序的话,就不能跟你们语音,只能是先打字,
打完之后再跟你们发语音,一次性只能做一件事
这样做的效率非常低,所以引进多进程是为了提高效率
另外,单独的一个进程在运行的时候,cpu的占有率也不是特别高,
或者进程在空闲等待io操作的时候,cpu的利用率就不高,
所以引进进程来提高cpu的使用率;
怎么做:
把cpu的运行切分成了多个时间片,
每一个当前正在占用cpu的程序都是一个进程,
这些进程在分别占有cpu的时间片,
比如印象笔记占1毫秒,qq占1毫秒,chrome占1毫秒,
它们在1秒钟之内切换了无数次,所以让你觉得他们是同时在运行的。
删(插一句:视频也是由每一帧画面片段组成的,只是因为它切换的特别快,所以看起来是连续的)
再就是:进程就是当前正在占用cpu的程序,
进程和程序之间的关系就是:程序是写死指令集合、进程是动态(它占有资源,如系统堆栈等)运行的程序…听起来有点绕,不知道讲清楚没有
进程的切换、调度有很多算法,不知道这些面试会不会问到,有兴趣的可以去了解下(
引入线程是因为进程的切换开销大、进程之间通信效率低。
========================================================================================
线程
那什么是线程呢,
线程是比进程更小的单位。
打个比喻:多进程是cpu的多个儿子,线程是进程的多个儿子,他们是用的差不多的模型
所以线程是更细粒度的占用资源的单位,为的也是提高运行效率
多线程我也举个例子:
我现在开着印象笔记,它是一个进程,但是它同时还在做更多的事,比如检查我鼠标的操作,检查有没有更新之类的,也就是多个线程在瓜分同一个进程,共享共同的一些数据;(多个线程在做不同的事)
进程和线程共享资源的方式不一样,
通信方式
进程之间通信有它的方式比如用文件、管道(pipe)、Socket。线程之间共享资源就是同一个进程的栈帧(开启的资源),相当于是一个全局的数据
换句话说,每一个不同的进程都占有一块独有的区域(所以他的通信麻烦一些),每一个不同的线程共享同一块区域;
多线程有个同步问题,引起这个问题的原因是不同线程的执行顺序是不同的,不知道哪个先做哪个后做,比如有个线程A要读文件,线程B要写文件,正确的执行顺序是先让线程A写完文件之后,线程B再来读文件,们两个的步骤是:线程A写文件的步骤有:1)先打开文件;2)写文件; 3)关闭文件线程B读文件的步骤有:1)打开文件;2)读文件;3)关闭文件;如果没有经过处理的两个线程同时开启,就不知道哪个线程先执行,哪个后执行可能会出现A刚打开文件还没有写文件,就切到了B线程读文件,这个时候就出现问题了..
解决这个问题有好几个办法,我讲一种加锁(互斥)操作,就是加了这个锁之后,只有一件事情完全做完之后再释放这个锁,把执行权交给另一个线程,还是按上面的例子:程序一开始给写文件的线程A的步骤1)加锁,当A的步骤3)结束之后再释放这个锁,如果没有释放的话,读文件的线程B就拿不到这个锁,无法执行;
========================================================================================
但是加锁又会产生新的问题:死锁;
同步加锁会产生死锁问题,(参考哲学家就餐问题,以及银行家算法)
1)是什么:比如只有一个独木桥,有两个人分别在不同的边,你们都走了一半的距离,谁也不想走回去让另一个人过,于是就一直等在这。这就是死锁了
2)为什么:
1- 一个资源每次只能被一个线程使用
2- 多个线程都在等待别人的资源
3)怎样做:
1-让他们获取资源(使用这个桥)的顺序是一样的,A先走完,B再走;
2-给更多的资源,多建几个桥;
========================================================================================
并发、并行问题:
不管你运行着多少个进程,只要你系统能处理多个事情,就是并发的并行就是这些事情都是在同一时间运行的一个是时刻、一个是时间段,并发是时间段,并行是时刻
总结:进程是为了提高cpu利用率,线程提高进程利用率,
原码、反码、补码
CSS
布局 Position
1、static
默认是static,使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时的top、left等属性设置无效。
2、relative
relative 是相对元素正常文档位置的情况下进行布局,使用top、left等属性使其相对原来的位置进行偏移。
3、absolute
绝对定位,脱离文档流布局。通过指定元素最近的非 static 定位元素的偏移,来确定元素定位置。可以设置外边距(margin),且不会与其他边距合并。
4、fixed
不为元素预留空间,而是指定元素相对于viewport的位置 来制定元素的位置
5、sticky
相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位
6、inherit
规定应该从父元素继承 position 属性的值。
盒模型
所有HTML元素可以看作盒子,在CSS中,”box model”这一术语是用来设计和布局时使用。
CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。
- Margin(外边距) - 清除边框外的区域,外边距是透明的。
- Border(边框) - 围绕在内边距和内容外的边框。
- Padding(内边距) - 清除内容周围的区域,内边距是透明的。
- Content(内容) - 盒子的内容,显示文本和图像。
IE 盒模型:width = content + border + padding
标准盒模型:width = content
1 | /*使用box-sizing选择使用那种盒模型*/ |
Flex 布局
采用 Flex 布局的元素,称为 Flex 容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称”项目”。
容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start
,结束位置叫做main end
;交叉轴的开始位置叫做cross start
,结束位置叫做cross end
。
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size
,占据的交叉轴空间叫做cross size
。
css垂直居中布局
- 绝对定位和负margin
- 绝对定位和transform
- 绝对定位和 calc 属性
- table 标签
- flex 布局
BFC
Block Formatting Contexts (BFC,块级格式化上下文),就是 一个块级元素 的渲染显示规则。通俗一点讲,可以把 BFC 理解为一个封闭的大箱子,,容器里面的子元素不会影响到外面的元素,反之也如此。
BFC的布局规则如下:
- 1 内部的盒子会在垂直方向,一个个地放置;
- 2 BFC是页面上的一个隔离的独立容器;
- 3 属于同一个BFC的 两个相邻Box的 上下margin会发生重叠 ;
- 4 计算BFC的高度时,浮动元素也参与计算
- 5 每个元素的左边,与包含的盒子的左边相接触,即使存在浮动也是如此;
- 6 BFC的区域不会与float重叠;
那么如何触发 BFC呢?只要元素满足下面任一条件即可触发 BFC 特性:
body 根元素;
浮动元素:float 不为 none 的属性值;
绝对定位元素:position (absolute、fixed)
display为: inline-block、table-cells、flex
overflow 除了visible以外的值 (hidden、auto、scroll)
移动端适配方案
1.使用百分比+媒体查询 test.1.html
2.使用 flexbox
3.使用 rem + viewport test.3.html
4.使用 rem test.4.html
5.固定布局视口宽度,使用 viewport 进行缩放 test.5.html
浏览器
浏览器渲染页面的过程
浏览器使用流式布局模型 (Flow Based Layout)。
浏览器会把
HTML
解析成DOM
,把CSS
解析成CSSOM
,DOM
和CSSOM
合并就产生了Render Tree
。有了
RenderTree
,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。由于浏览器使用流式布局,对
Render Tree
的计算通常只需要遍历一次就可以完成,但table
及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table
布局的原因之一。
浏览器的回流与重绘 (Reflow & Repaint)
当Render Tree
中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
会导致回流的操作:
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化
- 添加或者删除可见的
DOM
元素 - 激活
CSS
伪类(例如::hover
) - 查询某些属性或调用某些方法
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color
、background-color
、visibility
等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
JavaScript
优化
1、动画
- 可以优先采用requestAnimationFrame,实现动画帧的并发渲染
- 做减法,保留主动画性能,去除重要性不大的动画(跑马灯、过程小动画)等
- 大图动画性能消耗非常大,使用translate3d实现GPU 加速,动画结束、暂停是,切换回2d,取消加速。
- 按需加载/卸载动画;
- 每个动画帧处理函数简化,尽量减少或者去除回流、重绘。
2、加载用户体验的优化
- 首屏优先加载,保证用户体验的流畅性:优先加载欢迎界面图片的资源,所有图片load之后,在启动主动画资源的加载,与进度条动画。
- 资源的预加载,在进入主动画之前,进行主动画各资源的加载,当完成加载时,再promise结束进度条动画;。
- 常规优化:雪碧图、压缩、base64等
- 存储dom变量,减少dom tree的查找等;
3、避免回流
可以用transform等操作,来替代position;left等等的操作。当需要display:none的情况下(回流,不为元素保留其屋里空间),使用opacity:0(重绘);或者visibility:hidden(重绘),将更优。回流的性能消耗要远大于重绘。
requestAnimationFrame
它是一种动画高级的方法,存在兼容性问题。主要运作方式是浏览器要进行绘制的时候(一般16.7ms一次绘制),会通知requestAnimationFrame们,requestAnimationFrame们就跟它一起绘制。这里有几个好处,多个requestAnimationFrame可以同时进行,而setTimeout需要独立绘制;页面切换等情况,浏览器不再绘制该页面,requestAnimationFrame也停止了绘制,与浏览器同步,资源很省。相对setTimeout,是一个js的执行栈,只能串行执行,并且会影响其他js的处理。所以,使用前者,性能更佳,更流畅,交互体验更佳。特别是多个动画同时进行时,前者毫无压力,后者表示卡顿厉害。
write 和 innerHTML 的区别
write 是DOM的方法,向文档写入HTML表达式或者JavaScript代码,可以列出多个参数,参数被顺序添加到文档中;innerHTML是DOM属性。
write 方式会开启一个新的文档,导致页面内容的重写,innerHTML直接将内容写入DOM节点,不会导致页面的全部重回。innerHTML很多情况下都优于document.write,其原因在于其允许更精确的控制要刷新页面的哪一个部分。
事件委托机制
通俗来讲就是把一个元素的响应事件函数,委托到另一个元素。举个例子,就是有一堆li标签,需要给每一个li标签添加click事件,但是如果有几万个li标签,每个标签都显式地去添加事件函数,会影响性能。这里有一个解决方案就是把li的事件函数,委托到它的上一层父级ul标签去(假如它的父级是ul)
这里首先在父级ul上定义一个事件监听,变量event是回调函数获取到的事件对象,target变量是#list元素下被点击的li目标元素,通过这个target变量,我们可以获取一些例如nodeName和id这种属性。然后就是通过属性判断是否是点击的li的一些逻辑。
函数柯里化
好处:
1、参数复用
例如:
1 | var request = function(params, url, method) { |
可以看到上面的调用重复写了一些参数,如果进行柯里化后,可以写成下面的样子。
1 | var getRequest = curringRequest('url'); |
2、延迟运行
参考bind的实现机制。
1 | Function.prototype.bind = function (context) { |
继承原理
一、理解原型
js创建的每个函数都有一个prototype属性,这个属性指向一个对象。这个对象用来存储通过这个函数所创建的所有实例共有的属性和方法, 这个对象称为所有实例的原型对象。每个原型对象都包含一个constructor属性,它指向prototype属性所在的函数。
二、搜索对象属性的过程
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索proto指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。
通过实例只能访问原型对象的值,不能修改原型对象的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那么就会在实例中创建该属性,该属性将屏蔽掉原型中的那个属性。
闭包
闭包就是有权访问另外一个函数作用域中的变量的函数。
各种专业文献上的”闭包”(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包最重要的一个作用就是把某些变量隐藏起来,让外面的程序不能直接访问。
事件循环
首先 JS 是一个单线程的语言,也就是说同一时间,只能做一件事。这样就以为只所有代码里的所有任务都需要排队执行,前一个任务结束,才会执行接下来的任务。如果一个任务执行时间很长,之后的任务就不得不等待先前的任务执行完。
为了应对这种情况,JavaScript 设计者提出将等待中的任务先挂起,运行后面的代码,等到任务又了返回结果,再将挂起的任务继续执行。
于是,所有的任务可以分为两种,一种是同步任务、另外一种是异步任务。同步任务就是先后执行,异步任务则不在主线程执行,而是进入一个“任务队列”,只有任务队列通知主线程,某个任务可以执行了,才会开始执行。
异步任务的机制如下:
- 同步任务在主线程执行,形成一个执行栈
- 主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
- 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
上面的这个过程也叫做事件循环。
深拷贝 浅拷贝
– | 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 |
---|---|---|---|
赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
深浅拷贝的实现方法
浅拷贝:
1、=
复制
2、Object.assign() 方法
深拷贝:
1、递归
2、Object.create()
3、JSON做字符串转换
4、还有一些其它的第三方函数库有深拷贝function,如lodash
函数防抖和节流
函数防抖:函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
事件冒泡与实践捕获
时间事件是先捕获,后冒泡
捕获阶段是外部元素先触发然后触发内部元素
冒泡阶段是内部元素先触发然后触发外部元素
如何阻止事件冒泡?如何取消默认事件?如何阻止事件的默认行为?
阻止事件冒泡:
W3C: stopPropagation();
IE: e.cancelBubble=true;
写法 :
window.event ? window.event.cancelBubble=true:e.stop(Propagation)
取消默认事件
W3C:preventDefault()
IE: e.returnValue:false;
阻止默认行为:
return false
原生的js会阻止默认行为,但会继续冒泡;
jquery会阻止默认行为,并停止冒泡。
虚拟 DOM 对象的 Diff 算法
diff算法
首先 DOM 是一个多叉树的结构,如果需要完整的对比两颗树的差异,那么需要的时间复杂度会是 O(n ^ 3),这个复杂度肯定是不能接受的。于是 React 团队优化了算法,实现了 O(n) 的复杂度来对比差异。 实现 O(n) 复杂度的关键就是只对比同层的节点,而不是跨层对比,这也是考虑到在实际业务中很少会去跨层的移动 DOM 元素。 所以判断差异的算法就分为了两步
- 首先从上至下,从左往右遍历对象,也就是树的深度遍历,这一步中会给每个节点添加索引,便于最后渲染差异
- 一旦节点有子元素,就去判断子元素是否有不同
在第一步算法中,需要判断新旧节点的 tagName
是否相同,如果不相同的话就代表节点被替换了。如果没有更改 tagName
的话,就需要判断是否有子元素,有的话就进行第二步算法。
在第二步算法中,需要判断原本的列表中是否有节点被移除,在新的列表中需要判断是否有新的节点加入,还需要判断节点是否有移动。
举个例子来说,假设页面中只有一个列表,我们对列表中的元素进行了变更
defer 和 async 的区别
在页面加载的过程中, async 会将 js的加载与执行
文档的加载并行进行,defer 会将js的加载与文档加载并行执行,js的执行在加载之后。
宏任务和微任务
macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks: process.nextTick, Promises, Object.observe(废弃), MutationObserver
在 js 执行是,主线程会有一个事件队列以及事件循环,事件队列存放着需要处理的各种事件,这个循环则不段处理各种事件或者消息。
宿主环境提供的叫宏任务,由语言标准提供的叫微任务,常见的两种宿主环境有浏览器和 node。
宏任务本质:由宿主环境提供的参与事件循环的任务
微任务本质:直接在 Javascript 引擎中的执行的,没有参与事件循环的任务。
宏任务,微任务的优先级
promise 是在当前脚本代码执行完后,立刻执行的,它并没有参与事件循环,所以它的优先级是高于 setTimeout。
JavaScript 数字
JavaScript是如何表示数字的?
JavaScript使用Number类型表示数字(整数和浮点数),遵循 IEEE 754 标准 通过64位来表示一个数字
通过图片具体看一下数字在内存中的表示
- 第0位:符号位,0表示正数,1表示负数(s)
- 第1位到第11位:储存指数部分(e)
- 第12位到第63位:储存小数部分(即有效数字)f
既然说到这里,再给大家科普一个小知识点:js最大安全数是 Number.MAX_SAFE_INTEGER == Math.pow(2,53) - 1, 而不是Math.pow(2,52) - 1, why?尾数部分不是只有52位吗?
0.1和0.2转换成二进制后会无限循环1
20.1 -> 0.0001100110011001...(无限循环)
0.2 -> 0.0011001100110011...(无限循环)
js 的运行机制
- 第一个是JavaScript Engine,chrome 的引擎就是 V8(提供调用栈、内存 heap)
- 第二个是 WEb API,提供一些 dom 操作、ajax 的操作
- 第三个是 回调队列,也就是Web API 里的回调函数,回放入到回调队列中
- 第四个是事件循环,也就是宏任务、微任务的容器
call stack
是追踪函数执行流的一种机制,通过调用栈,可以知道当前哪一个函数正在被执行。每当调用一个函数,解释器就将改函数添加至调用栈并开始执行。如果正在执行的函数还调用了其他函数,那么新的函数也会被添加至调用栈中。当调用栈空间被占满时,就会引发“堆栈溢出”。因为只有一个调用栈,所以被称为单线程。
callback queue
回调队列,在 JavaScript 的编译阶段,将一些事件放置在执行队列中。
EventLoop 事件循环
将callback queue 中的事件放在 call stack 中执行
事件循环
JavaScript 是单线程的,执行一些耗时较长的段任务是,页面会卡,无法响应,
Webpack
原理过程
Webpack 的构建流程可以分为以下三大阶段:
- 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler。
- 编译:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归地进行编译处理。
- 输出:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件系统。
如果只执行一次构建,以上阶段将会按照顺序各执行一次。但在开启监听模式下,流程将变为如下:
在每个大阶段中又会发生很多事件,Webpack 会把这些事件广播出来供给 Plugin 使用,下面来一一介绍。
TreeShaking 原理
是什么:TreeShaking 的本质工作是消除无用的 JS 代码,称之为 DCE(dead code elimination)
为什么:js 与传统的编程源于不同的是,绝大多数代码是通过网络加载后执行,加载的文件大小越小,整体执行时间更短。所以去除无用代码以减少文件体积,对javascript来说更有意义。
原理:依赖于 ES6 的模块特性,依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析
缺点:具有Side Effect,side effects是指那些当import的时候会执行一些动作,但是不一定会有任何export。比如ployfill,ployfills不对外暴露方法给主程序使用。
Webpack 代码压缩
1、js 文件的压缩
Webpack 4 内置了uglifyjs-webpack-plugin,mode 设置为production默认开启代码压缩。
2、css 文件的压缩
使用optimize-css-assets-webpack-plugin
同时使用cssnano
配合MiniCssExtractPlugin插件把css单独分离成一个文件
配置:
1 | plugins:[ |
3.html文件的压缩
依赖html-webpack-plugin插件
配置:(设置参数)
1 | plugins: [ |
Webpack 优化
优化开发体验
优化开发体验的目的是为了提升开发时的效率,其中又可以分为以下几点:
- 优化构建速度。在项目庞大时构建耗时可能会变的很长,每次等待构建的耗时加起来也会是个大数目。
- 4-1 缩小文件搜索范围(loader 配置)
- 4-2 使用 DllPlugin(复用模块不重复打包)
- 4-3 使用 HappyPack(利用子进程)
- 4-4 使用 ParallelUglifyPlugin(多个子进程压缩代码)
- 优化使用体验。通过自动化手段完成一些重复的工作,让我们专注于解决问题本身。
优化输出质量
优化输出质量的目的是为了给用户呈现体验更好的网页,例如减少首屏加载时间、提升性能流畅度等。 这至关重要,因为在互联网行业竞争日益激烈的今天,这可能关系到你的产品的生死。
优化输出质量本质是优化构建输出的要发布到线上的代码,分为以下几点:
- 减少用户能感知到的加载时间,也就是首屏加载时间。
- 提升流畅度,也就是提升代码性能。
Babel
babel-loader,babel-core,babel-polify,babel-plugin-transform-runtime之间的关系:
babel-core:是Babel编译器的核心,把 js 代码分析成 ast,使用babel 的其他库都需要先安装 core。
babel-loader:模块转换器,将模块的内容按照需求装换成新内容。
babel-plugin-transform-runtime: 运行时引入 generators/async、babel-runtime/core-js(ES6->includes….)不会污染全局环境。
@babel/preset-env:转化最新语法如箭头函数,想要转换最新的api还需引入babel-polyfill(eg: includes)
@babel/polyfill:一些新的api:Iterator、Generator、Set、Map、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。
Git
工作区、缓存区
工作区:不包括.git在内的整个项目workspace
缓存区:版本库中包含了缓存区和 HEAD 指向的分支,使用 git add
命令会将修改的文件存放到缓存区。
git merge 和 git rebase 区别
merge 的特点:
- 会自动创建一个新的 commit,并让合并的头节点指向
- 合并遇到冲突需要解决,修改后重新commit
- 有点事记录了真实的commit 情况,包括每个分支的详情,缺点是每次merge会自动产生一个merge commit,所以在使用一些git 的GUI tools,特别是commit比较频繁时,看到分支很杂乱。
rebase 的特点:
- 找到两个分支的公共组建节点,会合并之前的commit历史
- 优点:得到更简洁的项目历史,去掉了merge commit
- 缺点:如果合并出现代码问题不容易定位,因为re-write了history
代码
Promise.All
1 | function isPromise(obj) { |
Canvas
globalCompositeOperation组合实现
globalCompositeOperation用于控制源图像在目标图像上的显示方式。
- 源图像: 指你准备绘制到画布上的图像
- 目标图像:在画布上已经绘制的图像
属性值:
值 | 描述 |
---|---|
source-over | 默认。在目标图像上显示源图像。 |
source-atop | 源图像为透明的,只有源图像与目标图像有交集的地方显示源图像,其他部分透明 |
source-in | 源图像和目标图像都透明,只有源图像和目标图像有交集的地方不透明,且显示源图像 |
source-out | 目标图像透明,源图像不透明,如果目标图像和源图像有交集的话,交集部分的图像透明 |
destination-over | 将源图像绘制在目标图像的下层 |
destination-atop | 目标图像透明,源图像不透明,如果目标图像和源图像有交集的话,在交际部分显示目标图像 |
destination-in | 源图像和目标图像都透明,只有目标图像和源图像交集的部分不透明且显示目标图像 |
destination-out | 目标图像不透明,源图像透明,如果目标图像和源图像有交集的话,交集部分图像透明 |
lighter | 源图像和目标图像一起绘制在画布中,如果两者之间有交集的话颜色会进行叠加 |
copy | 画布上只显示源图像,目标图像都不在画布中显示 |
xor | 源图像和目标图像都在画布中显示,如果两者之间有交集的话,交集部分不显示图像 |
作者:闲余幽梦
链接:https://www.jianshu.com/p/dd0487ff4293
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
图片合成:canvas.toDataURL(type, encoderOptions)
小程序
小程序底层构架
2.1 双线程模型
小程序的渲染层和逻辑层分别由2个线程管理:
- 渲染层:界面渲染相关的任务全都在
WebView
线程里执行。一个小程序存在多个界面,所以渲染层存在多个WebView
线程。 - 逻辑层:采用
JsCore
线程运行JS脚本。
视图层和逻辑层通过系统层的 WeixinJsBridage
进行通信:逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。
(页面渲染的具体流程是:在渲染层,宿主环境会把 WXML
转化成对应的 JS
对象,在逻辑层发生数据变更的时候,我们需要通过宿主环境提供的 setData
方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的Dom树上,渲染出正确的UI界面)
双线程模型
双线程模型是小程序框架与业界大多数前端 Web
框架不同之处。基于这个模型,可以更好地管控以及提供更安全的环境。缺点是带来了无处不在的异步问题(任何数据传递都是线程间的通信,也就是都会有一定的延时),不过小程序在框架层面已经封装好了异步带来的时序问题。
https://www.jianshu.com/p/ca720137818f
安全
CSRF 攻击与防御
CSRF 是一种网络攻击的方式,攻击者可以在受害者毫不知情的情况下以受害者,伪造请求发送给受攻击站点,从而在并未授权的情况下执行在权限保护之下的操作。
举个例子,A 在银行有一笔存款,通过对银行网站发送请求http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 A 把 1000000 的存款转到 B 的账号下。黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外。
CSRF 攻击的对象
在讨论如何抵御 CSRF 之前,先要明确 CSRF 攻击的对象,也就是要保护的对象。从以上的例子可知,CSRF 攻击是黑客借助受害者的 cookie 骗取服务器的信任,但是黑客并不能拿到 cookie,也看不到 cookie 的内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。
CSRF 防御策略
- 验证 HTTP Referer 字段
- 在请求中添加 token
- 在 HTTP 头中自定义属性并验证
- 将 cookie 设置为 HttpOnly:只允许通过 HTTP 请求的方式读取cookie
XSS 攻击
1、反射型 XSS
反射型XSS的原理是:反射性xss一般指攻击者通过特定的方式来诱惑受害者去访问一个包含恶意代码的URL。当受害者点击恶意链接url的时候,恶意代码会直接在受害者的主机上的浏览器执行。
攻击步骤如下:
\1. 攻击者在url后面的参数中加入恶意攻击代码。
\2. 当用户打开带有恶意代码的URL的时候,网站服务端将恶意代码从URL中取出,拼接在html中并且返回给浏览器端。
\3. 用户浏览器接收到响应后执行解析,其中的恶意代码也会被执行到。
\4. 攻击者通过恶意代码来窃取到用户数据并发送到攻击者的网站。攻击者会获取到比如cookie等信息,然后使用该信息来冒充合法用户的行为,调用目标网站接口执行攻击等操作。
2、存储行 XSS
存储型XSS的原理是:主要是将恶意代码上传或存储到服务器中,下次只要受害者浏览包含此恶意代码的页面就会执行恶意代码。
\1. 攻击者将恶意代码提交到目标网站数据库中。
\2. 用户打开目标网站时,网站服务器将恶意代码从数据库中取出,然后拼接到html中返回给浏览器中。
\3. 用户浏览器接收到响应后解析执行,那么其中的恶意代码也会被执行。
\4. 那么恶意代码执行后,就能获取到用户数据,比如上面的cookie等信息,那么把该cookie发送到攻击者网站中,那么攻击者拿到该
cookie然后会冒充该用户的行为,调用目标网站接口等违法操作。
如何防范?
\1. 后端需要对提交的数据进行过滤。
\2. 前端也可以做一下处理方式,比如对script标签,将特殊字符替换成HTML编码这些等。
3、DOM-based 型XSS
\1. 攻击者构造出特殊的URL、在其中可能包含恶意代码。
\2. 用户打开带有恶意代码的URL。
\3. 用户浏览器收到响应后解析执行。前端使用js取出url中的恶意代码并执行。
\4. 执行时,恶意代码窃取用户数据并发送到攻击者的网站中,那么攻击者网站拿到这些数据去冒充用户的行为操作。调用目标网站接口
执行攻击者一些操作。
防范:
- 防御HTML编码。编码规则:将 & < > “ ‘ / 转义为实体字符。
- 防御HTML Attribute编码。除了字母、数字、字符以外,使用 16进制格式来转义ASCII值小于256所有的字符。
- 防御之javascript编码。在onlick 等事件中,需要进行转码,
React 和 Vue 的区别
上面主要梳理了react和vue的4点不同:
- 数据是不是可变的
- 通过js操作一切还是各自的处理方式
- 类式的组件写法还是声明式的写法
- 什么功能内置,什么交给社区去做
(其中第3点在vue3.0支持类式写法之后就可以去掉了)
react整体的思路就是函数式,所以推崇纯组件,数据不可变,单向数据流,当然需要双向的地方也可以做到,比如结合redux-form,而vue是基于可变数据的,支持双向绑定。react组件的扩展一般是通过高阶组件,而vue组件会使用mixin。vue内置了很多功能,而react做的很少,很多都是由社区来完成的,vue追求的是开发的简单,而react更在乎方式是否正确。
Flutter 与react 的区别
类型 | React Native | Flutter |
---|---|---|
语言 | JavaScript | dart |
环境 | JSCore | Flutter Engine |
发布时间 | 2015 | 2017 |
star | 78k+ | 67k+ |
对比版本 | 0.59.9 | 1.6.3 |
空项目打包大小 | Android 20M(可调整至 7.3M) / IOS 1.6M | Android 5.2M / IOS 10.1M |
GSY项目大小 | Android 28.6M / IOS 9.1M | Android 11.6M / IOS 21.5M |
代码产物 | JS Bundle 文件 | 二进制文件 |
维护者 | ||
风格 | 响应式,Learn once, write anywhere | 响应式,一次编写多平台运行 |
支持 | Android、IOS、(PC) | Android、IOS、(Web/PC) |
使用代表 | 京东、携程、腾讯课堂 | 闲鱼、美团B端 |
Flutter 优点:
1、性能强大,流畅
Flutter对比
weex和
react native相比,性能的强大是有目共睹的。基于
dom`树渲染原生组件,很难与直接在原生视图上绘图比肩性能,Google作为一个轮子大厂,直接在两个平台上重写了各自的UIKit,对接到平台底层,减少UI层的多层转换,UI性能可以比肩原生,这个优势在滑动和播放动画时尤为明显。
2、语言优势
Dart
是一个静态语言,这也是相对于js的一个优势,静态语言可以避免错误,获得更多的编辑器提示词,极大的增加可维护性。
- 跨平台使用相同的UI和业务逻辑
- 节省开发时间
- 更快的迭代速度
- 无限接近原生的交互体验
- 丰富的UI动画
- 独立的渲染引擎
- 能够很容易与原生进行交互
- 不只是能运行在移动端
为什么Flutter会选择Dart语言?
- 吸取了新生代语言的优点,语法简洁优雅,简单易学
- 单线程,也没有锁,GC 相对平滑,没有了常用 JVM (特别是Android上)特色的卡顿
- 拥有完全权利,不会惹出88亿美元的官司
1.开发效率高。
Dart运行时和编译器支持Flutter的两个关键特性的组合,分别是基于JIT的快速开发周期和基于AOT的发布包。基于JIT的快速开发周期:Flutter在开发阶段,采用JIT模式,这样就避免了每次改动都需要进行编译,极大地节省了开发时间。基于AOT的发布包,Flutter在发布时可以通过AOT生成高效的ARM代码,以保证应用性能。而JavaScript则不具备这个能力。
2.高性能。
为了实现流畅、高保真的的UI体验,Flutter必须在每个动画帧中都运行大量的代码。这意味着需要一种既能支持高性能,又能保证不丢帧的周期性暂停的语言,而Dart支持AOT,在这一点上比JavaScript更有优势。
3.快速分配内存。
Flutter框架使用函数式流,这使得它在很大程度上依赖于底层的内存分配器。
React 父子组件生命周期的执行顺序
A组件包裹B组件,B组件包裹C组件,它们的 componentDidMount 触发顺序如何。
1、父类执行 ComponentWillMount
2、父类执行render
3、子类执行ComponentWillMount
4、子类执行render
5、子类执行ComponentDidMount
6、父类执行ComponentDidMount
ReactRouter
保证视图和URL的同步,而视图可以看成是资源的一种表现。当用户在页面中进行操作时,应用会在若干个交互状态中切换,路由则可以记录下某些重要的状态,比如在一个博客系统中用户是否登录、在访问哪一篇文章、位于文章归档列表的第几页。而这些变化同样会被记录在浏览器的历史中,用户可以通过浏览器的前进、后退按钮切换状态,同样可以将 URL 分享给好友。
用户可以通过手动输入或者与页面进行交互来改变 URL,然后通过同步或者异步的方式向服务端发送请求获取资源(当然,资源也可能存在于本地),成功后重新绘制 UI,原理如下图所示:
HTML 新特性
html5总的来说比html4多了十个新特性,但其不支持ie8及ie8以下版本的浏览器
一、语义标签
二、增强型表单
三、视频和音频
四、Canvas绘图
五、SVG绘图
六、地理定位
七、拖放API
八、WebWorker
九、WebStorage
十、WebSocket
排序算法的选择
前端工程化
前端的开发框架以 Vue 为主,使用 Webpack 解决接口 mock、代码检查、代码编译、构建、压缩、添加版本号、部署等全流程的工作。涉及到的技术点有 Vue、Vuex、ESlint、stylelint、Mock、Webpack、Sass、PostCSS 等。对前端的要求相比几年前已经从单纯的 JS、CSS 问题变成了更多工程化为主的问题。
- 如何进行高效的多人协作?
- 如何保证项目的可维护性?
- 如何提高项目的开发质量?
- 如何降低项目生产的风险?
前端趋势
前端工程化
小程序
在微信小程序出现以前,大家在谈 Hybird、ReactNative,但终归只是技术层面的狂欢,始终没有业务属性的注入。小程序的出现,一方面告诉业界在当前设备上 Webview 也没差到哪去,另外一方面告诉业界如何让有能力的商家在超级 APP上进行私域运营。
另一方面,从技术角度说,在上层 DSL 的严格限制下,超级 APP 就可定义符合自己诉求的 Web 标准,弥补当前 Web 标准的不足,最后和客户端配合,结合离线、预加载、定制Webview 能产出类似于 NSR 等各种酷炫的技术模型,让 Web 在端内低成本达到 Native 版的体验,端外也不会像 Weex 一样有点小别扭。
不过由于需要依赖超级APP(微信、支付宝、百度、美团、头条等),由于各家平台采用的具体方案的差异,造成目前小程序的落地方案也不一样,有时候需要开发多套代码。
跨端开发
跨端开发⽅⾯,RN ⽣态已经⾮常成熟,或者说看不到太多发展前景,因为目前还停留在0.61版本,似乎1.0版本仍然遥遥无期。因此,今年很多团队转战⾕歌⽣态的 Flutter,特别是 Flutter for Web 的第⼀个 Release,⼜让 Web 前端重燃希望、跃跃欲试。
同时,苹果公司也发布了全新的 UI 系统——SwiftUI,同时,开源社区中 SwiftUI for Web已经在路上了,SwiftUI for Android 还会远吗?
Serverless
Serverless 的⽕爆⼏乎可以归因于前端。因为 Serverless 能够较完美的⽀持Node.js,使⽤ Serverless 帮助前端开发者解决了使⽤Node.js 过程中的诸多问题。
当前的前端工程师大多都是科班出身,虽不能和正宗的服务端开发同学比,但也可写很多服务端层的业务逻辑。当前已经有很多公司在做 BFF 层,来满足这部分诉求,但依旧摆脱不掉运维、机器分配 这条拦路虎。随着 Serverless 的逐步落地,BFF 这层的代码会摆脱运维、机器分配等复杂的问题,同时大概率会由前端同学写这部分代码,服务端同学专注中台系统的实现。从业务上说,业务的试错成本也会大幅度降低。
5G
2019年一个绕不开的话题就是5G。⾸先,5G 带宽的⼤幅提升带来传统 Web ⻚⾯复杂度的进⼀步提升,如同 2G 到 4G 变⾰过程中⻚⾯从 WAP 的纯⽂本超链接时代变⾰到 4G 全图⽚视频时代。5G 对于⻚⾯的变⾰必将是巨⼤的,但肯定不会⼀蹴⽽就。因为相应的配套设施也需要逐步完善,如硬件性能和浏览器的处理速度。⽽服务端渲染(SSR)肯定是其中⼀个捷径,轻前端重后台,5G 是桥梁,把渲染放后台,不像同构那么简单,需要关注和优化渲染性能。WebAssembly 或许会在这个机遇下得到快速发展,因为它可以⽆缝对接后台多种语⾔,⽽后台渲染的优化也会带来前端⻚⾯研发模式和技术架构的变⾰。
其次,5G 带来的万物互联,⼜将带来有别于智能⼿机和普通 PC 的多样化的应⽤场景,VR、可穿戴设备、⻋载系统、智能投影、智能交互等⼜会把 Web 带⼊各种各样的垂直领域,这也意味着前端将有更多⼴阔的空间。相信随着5G的大规模商业,会诞生一批新的互联网巨头。
谈薪要问的问题
1、实习过程中有遇到什么有挑战性的嘛
字节实习:
- 面向国际化的团队,文档以及平时的沟通都会使用英文,这是一个挑战
- 人员的交接,团队中间经历过一次组织架构调整,然后需要做很多的工作交接。需要去看别人的交接文档,并且修复一些别人遗留的问题,需要花费很多时间去沟通以及对齐
- 技术上遇到的困难,在一次需求中,需要对用户进行一个运营的导入,这其中需要去判断中间的用户的冲突,例如label、owner、category 的冲突,这里涉及到一个多模态框的控制。
阿里实习:
- 几乎是按照导师的要求学习,接触了一些业务。
2、项目难点
尚朴的电商小程序项目,有涉及到商品规格的选择。最开始没有啥经验,就是傻傻的每次选中一次就去请求后台的库存,这样体验非常不好。
然后想着去优化,开始就是想着让后端吧所有的数据都先取到,结构就是一个数组,每个数据就是一个商品规格的对象,比如材质、花纹、尺寸等等,每次选择一种规格之后,都在前端去遍历一遍剩下规格的库存。这样体验会好一些,但是不好扩展,当我要新增一种规格的时候,就需要重新更改这个遍历算法。另外还有问题就是如果商品规格变多,便利的时间也会增加,页面有些许卡顿。
之后想着再进行优化,让这个更有拓展性,于是就想到了预先做一个循环,将可能的规格做一个分解,以 key 的方式存在 map中,之后每次用户选择,直接从 map 里拿库存即可。
这就是一个优化的过程。
谈薪问的问题:
- 工作时间是怎么样的?加班是否有加班费?
- 除基础薪资外,是否有其余的补助。每个月的薪资由哪些部分组成?
- 公积金缴纳比例?是否全额缴纳?每个月薪资发放的日期是什么时候?
- 试用期多久?试用期期间工资怎么发放?
- 公司的调薪制度?一年有多少次考核晋升调薪机会?
- 如果提前来实习,薪资是多少
- 我需要什么时候给出答复