今日一题:
下面JS代码执行后,最终的三个console.log的输出结果是什么?
var obj = { num: 1 };
var arr1 = [1, 2, obj];
var arr2 = arr1.slice(1);
arr2[0]++;
arr2[1].num++;
obj.num++;
arr1[2].num++;
console.log(arr1[1] === arr2[0]);
console.log(arr1[2] === arr2[1]);
console.log(obj.num);
答案和解析可在文章底部查看。
今日面试题:
1、浏览器地址栏中输入URL到显示页面,中间都经历了什么?
① 输入URL并回车之后,首先查找是否有对应资源的缓存。
浏览器查找资源缓存,如果找到该资源的缓存,则将缓存中的资源直接返回显示。
如果未缓存该资源,则会继续向下执行。
② 解析URL,获取对应的协议、ip、端口等信息。
获取主机ip地址,会按照以下路径进行查找:浏览器缓存 — 系统缓存 — 系统的hosts文件 — 路由缓存 — DNS服务器。
③ 浏览器根据获取的相关信息,构建HTTP请求,进行三次握手,建立TCP链接,发起请求。
TCP三次握手,下面有详细介绍。
④ 服务器接收请求,并转发到具体应用程序进行处理,将对应的html作为响应结果通过TCP链接返回给浏览器。
⑤ 浏览器接收请求响应结果,并关闭TCP连接,关闭时需要进行四次挥手。
TCP四次挥手,下面有详细介绍。
⑥ 浏览器解析响应的HTML文档,构建DOM树和CSSOM树,并加载相关资源。
在构建DOM树时,如果遇到同步 JS 代码,则暂停构建DOM树,先去执行 JS,执行结束之后,再继续构建DOM树 。如果是异步的JS代码,则会边构建DOM树,边加载JS代码,并在加载完成后尽快执⾏。
DOM树和CSSOM树构建完成之后,合并构建一棵渲染树,从DOM树的根节点遍历所有可⻅节点,对每个可⻅节点,找到对应的CSSOM规则并应⽤,然后布局和渲染页面。其中不可见的节点包括:被CSS隐藏的节点,如:设置display: none;
的节点;或者像是script
、meta
等本身就不可见的节点。
在渲染页面时,如果遇到了图片、视频等外部资源,则会并行下载这些资源(浏览器对每个域的并行下载数量有限制,一般为4-6个)。
⑦ 在HTML解析过程中,会逐步显示页面内容,直到页面渲染结束,内容全部展示。
2、请解释一下TCP建立连接时的三次握手?
① 第一次握手
客户端先发送一个带有SYN标志的数据包给服务端,请求建立连接,并进入SYN_SENT等待状态,在一定的延迟时间内等待服务端的回复。如果客户端在规定延迟时间内没有收到回复则默认服务端没有收到请求,而再次发送,直到收到回复为止。
② 第二次握手
服务端收到SYN数据包后,先对客户端的SYN进行确认,确认后返回一个带有SYN/ACK标志的数据包,确认自己收到了客户端的请求,并进入SYN_RECV状态,等待客户端回应是否收到服务端的回应。
② 第三次握手
客户端收到SYN/ACK数据包后,再向服务端发送一个带有ACK标志的确认数据包,确认收到了服务端的回应。服务端接收数据包并确认后,客户端和服务端进入ESTABLISHED(TCP连接成功)状态。至此完成三次握手,可以开始传输数据了。
注:SYN是指同步序列编号(Synchronize Sequence Numbers)
3、请解释一下TCP断开连接时的四次挥手?
① 第一次挥手
客户端进程向服务端发出连接释放报文,停止发送数据,并进入FIN-WAIT-1(终止等待1)状态,等待服务端的确认。
② 第二次挥手
服务端接收到客户端的连接释放报文,并返回一个确认报文,服务端进入CLOSE-WAIT(关闭等待状态)。客户端接收到服务器的确认请求之后,进入FIN-WAIT-2(终止等待2)状态,等待服务端发送连接释放报文。此时TCP连接处于半关闭状态,即客户端不能向服务端发送数据,但服务端可以继续向客户端发送数据,客户端也能正常接收数据,这种状态还会持续一段时间,也就是服务端CLOSE-WAIT 状态的持续时间。
③ 第三次挥手
服务端将所有数据发送完毕后,向客户端发送连接释放报文,并且服务器进入LAST-ACK(最后确认)状态,等待客户端的回应。
④ 第四次挥手
客户端收到服务端连接释放报文后,返回一个确认报文,并且客户端进入TIME-WAIT(时间等待)状态,但TCP连接还需要经过2MSL(MSL-最长报文段寿命,一般为两分钟)的等待时间,如果服务端接收到确认报文,且没有传输新的数据,客户端就会关闭TCP连接,进入CLOSED状态。服务端在接收到客户端返回的确认报文之后,会立即进入CLOSED状态,所以服务端进入CLOSED状态的时间会比客户端早一些。
2MSL等待时间的主要目的是为了确保服务端收到了最后的确认报文,如果服务器未收到确认报文,则会向客户端发送重传FIN报文,客户端会在2MSL等待时间内重新发送ACK报文。
今日一题答案:false、true、4
答案解析:
本题考验的重点是简单数据类型和引用数据类型的区别,下面我们来逐句分析这段代码
var obj = { num: 1 };
这一句是通过字面量在内存中声明了一个对象数据,并将该对象的引用地址赋值给变量obj
,此时我们应该注意对象数据属于引用类型。
var arr1 = [1, 2, obj];
这一句是通过字面量在内存中声明了一个数组,并把数组的引用地址赋值给变量arr1
,其中前两个元素为Number
类型,第三个元素是前面声明的对象变量obj
,也就是对内存中对象数据的引用。
var arr2 = arr1.slice(1);
这一句是借助数组的slice()
方法从arr1
中截取生成一个新数组。此时应该想到slice(1)
截取的是下标从1到最后一个元素,返回的是截取元素组成的新数组[2, obj]
,且不会对原数组arr1
产生影响。
由于2
是简单数据类型,所以会在内存中拷贝数据,arr2
中的2
与arr1
中的2
无关联,是两份独立的数据;但是obj
是对象数据的引用(关键),因此拷贝的是对数据的引用,arr2
中的obj
与arr1
中的obj
指向内存中的同一份数据。
arr2[0]++;
这一句将arr2
中第一个元素2
进行加一操作,此时arr2
的内容变为:[3, obj]
。
arr2[1].num++;
这一句是将arr2
中第二个元素obj
中的num
字段进行加一操作,此时obj
的内容变为:{ num: 2 }
。
obj.num++;
这一句是直接操作obj
变量中的num
字段进行加一操作,此时obj
的内容变为:{ num: 3 }
。
arr1[2].num++;
这一句是将arr1
中第三个元素obj
中的num
字段进行加一操作,此时obj
的内容变为:{ num: 4 }
。至此,所有数据操作都已完成,此时声明的三个变量的值分别为:
// obj的值
{ num: 4 }
// arr1的值
[1, 2, obj]
// arr2的值
[3, obj]
根据上面各个变量的值,我们开始看第一个console.log
语句
console.log(arr1[1] === arr2[0]);
// arr1[1] 的值为 2
// arr2[0] 的值为 3
// 所以 2 === 3 结果为false
第二个console.log
语句
console.log(arr1[2] === arr2[1]);
// arr1[2] 的值为 obj 注意是引用类型
// arr2[1] 的值为 obj 注意是引用类型
// 两者的 obj 指向同一份数据
// 所以 obj === obj 结果为true
第三个console.log
语句
console.log(obj.num);
// obj.num 初始值为1
// arr2[1].num++; 进行了一次+1 值为2
// obj.num++; 进行第二次+1 值为3
// arr1[2].num++; 进行第三次+1 值为4
// 所以最终结果为 4