阿里二面 — 从URL输入到渲染...

文章目录

  • 前言
  • 一、浏览器对URL为什么要解析?URL参数用的是什么字符编码?
    • 1.1 URL为什么要编码解码
    • 1.2哪些字符需要编码
    • 1.3 Url编码默认使用的字符集是US-ASCII
  • 二、escape、encodeURI和encodeURIComponent有什么区别
    • 2.1共同点
    • 2.2不同点
  • 三、浏览器缓存的disk cache和memory cache是什么
    • 1.1 memory cache(内存缓存)
    • 1.2 disk cache(硬盘缓存)
  • 四、预加载prefetch、preload有什么差别
  • 五、JS脚本的async和defer有什么区别
  • 六、TCP握手为什么要三次,挥手为什么要四次
  • 七、HTTPS的握手有了解过吗?

前言

当面试官问出这个题后,大部分人听到都是内心窃喜:早就背下这篇八股文。
但是稍等,下面几个问题你能答出来吗:

  1. 浏览器对URL为什么要解析?URL参数用的是什么字符编码?
  2. 那encodeURI和encodeURIComponent有什么区别?
  3. 浏览器缓存的disk cache和memory cache是什么?
  4. 预加载prefetch、preload有什么差别?
  5. JS脚本的async和defer有什么区别?
  6. TCP握手为什么要三次,挥手为什么要四次?
  7. HTTPS的握手有了解过吗?

一、浏览器对URL为什么要解析?URL参数用的是什么字符编码?

1.1 URL为什么要编码解码

HTTP协议中参数的传输是key=value这种键值对形式的,如果要传多个参数就需要用“&”符号对键值对进行分割。

?name1=value1&name2=value2,这样在服务端在收到这种字符串的时候,会用“&”分割出每一个参数,然后再用“=”来分割出参数值。

针对name1=value1&name2=value2我们来说一下客户端到服务端的概念上解析过程:
上述字符串在计算机中用ASCII吗表示为:

text
6E616D6531 3D 76616C756531 26 6E616D6532 3D 76616C756532。 
   6E616D6531:name1 
   3D:= 
   76616C756531:value1 
   26:&
   6E616D6532:name2 
   3D:= 
   76616C756532:value2 

服务端在接收到该数据后就可以遍历该字节流,首先一个字节一个字节的解析,当解析到3D这字节后,服务端就知道前面解析得字节表示一个key,再向后解析,如果遇到26,说明从刚才解析的3D到26子节之间的是上一个key的value,以此类推就可以解析出客户端传过来的参数。

现在有这样一个问题,如果我的参数值中就包含=或&这种特殊字符的时候该怎么办?

比如说name1=value1,其中value1的值是va&lu=e1字符串,那么实际在传输过程中就会变成这样name1=va&lu=e1。我们的本意是就只有一个键值对,但是服务端会解析成两个键值对,这样就产生了歧义。

如何解决上述问题带来的歧义呢?解决的办法就是对参数进行URL编码
URL编码只是简单的在特殊字符的各个字节前加上%,例如,我们对上述会产生奇异的字符进行URL编码后结果:name1=va%26lu%3D,这样服务端会把紧跟在“%”后的字节当成普通的字节,就是不会把它当成各个参数或键值对的分隔符。

1.2哪些字符需要编码

RFC3986文档规定,Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符。RFC3986文档对Url的编解码问题做出了详细的建议,指出了哪些字符需要被编码才不会引起Url语义的转变,以及对为什么这些字符需要编码做出了相应的解释。

RFC3986中指定了以下字符为保留字符! * ' ( ) ; : @ & = + $ , / ? # [ ]

不安全字符:还有一些字符,当他们直接放在Url中的时候,可能会引起解析程序的歧义。这些字符被视为不安全字符,原因有很多。

  • 空格:Url在传输的过程,或者用户在排版的过程,或者文本处理程序在处理Url的过程,都有可能引入无关紧要的空格,或者将那些有意义的空格给去掉。
  • 引号以及<>:引号和尖括号通常用于在普通文本中起到分隔Url的作用
  • [#]:通常用于表示书签或者锚点
  • %:百分号本身用作对不安全字符进行编码时使用的特殊字符,因此本身需要编码
  • {}|1`~:某一些网关或者传输代理会篡改这些字符

需要注意的是:对于Url中的合法字符,编码和不编码是等价的,但是对于上面提到的这些字符,如果不经过编码,那么它们有可能会造成Url语义的不同。因此对于Url而言,只有普通英文字符和数字,特殊字符%$-_.+!*'()还有保留字符,才能出现在未经编码的Url之中。其他字符均需要经过编码之后才能出现在Url中。

其实不止中文需要编码,因为最初搞这些技术的,或者说技术最初技术牛逼的大多都是那些说英文的,所以除了英文和那些与英文一起常常出现的特殊符号被url认可,其他的什么韩语、日语还有一些其他国家与英文不同根源的字符都需要编码成url认可的,也可以说编码成老外(讲英文的老外)看的懂的东西才是url认可的,但是实际应用中一些老外看的懂的也是需要编码的,一些url认可的字符仍然需要编码,防止服务器解析的时候出现错误

1.3 Url编码默认使用的字符集是US-ASCII

二、escape、encodeURI和encodeURIComponent有什么区别

JavaScript中提供了3对函数用来对Url编码以得到合法的Url,它们分别是escape / unescape, encodeURI / decodeURIencodeURIComponent / decodeURIComponent。由于解码和编码的过程是可逆的,因此这里只解释编码的过程。

2.1共同点

这三个编码的函数都是用于将不安全不合法的Url字符转换为合法的Url字符表示

2.2不同点

  • 安全字符不同:
    下面列出了这三个函数的安全字符(即函数不会对这些字符进行编码)
    escape(69个):*/@+-._0-9a-zA-Z
    encodeURI(82个):!#$&'()*+,/:;=?@-._~0-9a-zA-Z
    encodeURIComponent(71个):!'()*-._~0-9a-zA-Z
  • 兼容性不同:
    escape函数是从Javascript 1.0的时候就存在了,其他两个函数是在Javascript 1.5才引入的。但是由于Javascript 1.5已经非常普及了,所以实际上使用encodeURI和encodeURIComponent并不会有什么兼容性问题。
  • 对Unicode字符的编码方式不同:
    这三个函数对于ASCII字符的编码方式相同,均是使用百分号+两位十六进制字符来表示
    但是对于Unicode字符,escape的编码方式是%uxxxx,其中的xxxx是用来表示unicode字符的4位十六进制字符。这种方式已经被ECMAScript v3 已从标准中删除了 unescape() 函数,并反对使用它,因此应该用 decodeURI() 和 decodeURIComponent() 取而代之。encodeURI和encodeURIComponent则使用UTF-8对非ASCII字符进行编码,然后再进行百分号编码。这是RFC推荐的。因此建议尽可能的使用这两个函数替代escape进行编码。
  • 适用场合不同:
  1. encodeURI被用作对一个完整的URI进行编码
  2. encodeURIComponent被用作对**URI的一个组件进行编码。**从上面提到的安全字符范围表格来看,我们会发现,encodeURIComponent编码的字符范围要比encodeURI的大。
  3. 我们上面提到过,保留字符一般是用来分隔URI组件或者子组件,如号用于分隔scheme和主机,?号用于分隔主机和路径。
    由于encodeURI操纵的对象是一个完整的的URI,这些字符在URI中本来就有特殊用途,因此这些保留字符不会被encodeURI编码,否则意义就变了
    组件内部有自己的数据表示格式,但是这些数据内部不能包含有分隔组件的保留字符,否则就会导致整个URI中组件的分隔混乱。因此对于单个组件使用encodeURIComponent

三、浏览器缓存的disk cache和memory cache是什么

都属于强缓存。

1.1 memory cache(内存缓存)

Memory Cache 也就是内存中的缓存

优点:读取速度快
缺点:一旦我们关闭 Tab 页面,内存中的缓存也就被释放了
如何触发:当我们访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存

1.2 disk cache(硬盘缓存)

Disk Cache 也就是存储在硬盘中的缓存

优点:缓存再硬盘中,容量大
缺点:读取速度慢
如何触发:根据浏览器请求头确定

  • 200 form memory cache
    不访问服务器,一般已经加载过该资源且缓存在了内存当中,直接从内存中读取缓存。浏览器关闭后,数据将不存在(资源被释放掉了),再次打开相同的页面时,不会出现from memory cache。
  • 200 from disk cache
    不访问服务器,已经在之前的某个时间加载过该资源,直接从硬盘中读取缓存,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放掉下次打开仍然会是from disk cache。
  • 304 Not Modified
    访问服务器,发现数据没有更新,服务器返回此状态码。然后从缓存中读取数据。

四、预加载prefetch、preload有什么差别

  • preload 是告诉浏览器页面必定需要的资源,浏览器一定会加载这些资源
  • prefetch 是告诉浏览器页面可能需要的资源,浏览器不一定会加载这些资源
     
    所以,对于当前页面很有必要的资源使用 preload,对于可能在将来的页面中使用的资源使用 prefetch

详细描述

五、JS脚本的async和defer有什么区别

script标签存在两个属性,defer和async,因此script标签的使用分为三种情况:


  1. 没有defer或async属性,浏览器会立即加载并执行相应的脚本。也就是说在渲染script标签之后的文档之前,不等待后续加载的文档元素,读到就开始加载和执行,此举会阻塞后续文档的加载;

  2. 有了async属性,表示后续文档的加载和渲染与js脚本的加载和执行是并行进行的,即异步执行

  3. 有了defer属性,加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步),js脚本的执行需要等到文档所有元素解析完成之后,DOMContentLoaded事件触发执行之前。

图可以直观的看出三者之间的区别:
阿里二面 — 从URL输入到渲染..._第1张图片
蓝色代表js脚本网络加载时间,红色代表js脚本执行时间,绿色代表html解析。
从图中我们可以明确一下几点:

  • 1.defer和async在网络加载过程是一致的,都是异步执行的,不会阻塞页面的加载;
  • 2.两者的区别在于脚本加载完成之后何时执行,可以看出defer更符合大多数场景对应用脚本加载和执行的要求;
  • 3.如果存在多个有defer属性的脚本,那么它们是按照加载顺序执行脚本的;而对于async,它的加载和执行是紧紧挨着的,无论声明顺序如何,只要加载完成就立刻执行,它对于应用脚本用处不大,因为它完全不考虑依赖。

六、TCP握手为什么要三次,挥手为什么要四次

【从浏览器地址栏输入 url 到请求返回的过程】—— 7. 断开TCP连接:四次挥手

七、HTTPS的握手有了解过吗?

HTTPS加密的过程你了解吗?


  1. ↩︎

你可能感兴趣的:(前端,面试)