1. HTTP 在一次请求完成后就会断开连接?
这句话很明显是错误的。HTTP先后有多个版本,只有在HTTP/1.0及之前的版本中,无法建立保持连接状态。
2. HTTP同时支持强缓存和协商缓存吗?
当然,这是必然的。我们所说的各种缓存判定标识,均是http请求所需的。
3. 要保证连通具有8个顶点的无向图,至少需要多少条边?
8个顶点的无向图,只要能够保证前7个点构成一个无向完全图,最后一个点任意连接都是联通的。
关于无向图与完全图的概念,可以先看一下这篇文章
有向图,无向图,连通图,完全图_二次加工是一种痛-CSDN博客立方网笔试题,6个顶点的无向图,至少有几个https://blog.csdn.net/M87138/article/details/40000991无向图两点之间的边没有方向,那么只需要一条边就可以让两个点实现互相连通;而有向图因为存在方向,仅仅通过一条边,如果可以从A点到达B点,那么就必然无法从B点再回到A点,因此就需要两条边来实现两点之间的互通。
完全图就是任意两个点之间都可以互相直达。
对于含有n个顶点的无向完全图来说,实现两点互通,只需要让任意两点之间都存在一条边即可:
公式:
而对于有向完全图,由于边是有方向的,所以任意两个点之间需要借助两条边才能实现互通。
这么一说其实对于公式的总结就有了很大的帮助。可以直接简单总结为无向完全图的两倍;也可以用排列来解决
公式则是:
再来回顾一下前面的问题,保证连通具有8个顶点的无向图,至少需要多少条边?
7个点组成一个无向完全图,也就是
所以最终答案就是 21 + 1 = 22
4. 原型链
这是一道老生常谈的题了,再来看一下题干:
Function.prototype.a = 1;
Object.prototype.b = 2;
function A(){};
var a = new A();
console.log(a.a, a.b); // undefined 2
console.log(A.a, A.b); // 1, 2
如果写题的过程中,可以将这张图的隐式原型链画出来,问题也就迎刃而解了。
我们知道,访问一个属性,当对象身上不存在这个属性时,会从该对象开始,沿着隐式原型链,一路访问到原型链的尽头,如果这期间找到了同名的属性,就会将其值打印出来,否则就会返回undefined。这就是原型链的访问流程。
就这道题来说,考了图中的两个核心概念:
- A.prototype.__proto__ = Object.prototype (A表示任意手动创建的构造函数+Function构造函数)
- A.__proto__ = Function.prototype
我们先来看a.a、a.b的访问路径:
可以看到,始终都在沿着隐式原型链检索。
a属性位于Function.prototype对象上,这个过程并没有检索至该对象,所以就会返回undefined
b位于Object.prototype上,隐式原型链上包含该对象,所以a.b就可以取出对应的值。
接下来我们来一起分析一下A.a与A.b:
这个过程既可以访问到拥有a属性的Function.prototype,也可以访问到拥有b属性的Object.prototype。所以两个属性对应的值都可以被打印出来。
我们一起来捋一捋原型链的几个重要概念:
1. 任意构造函数,他们的显式原型属性(prototype)均指向一个对象,这个对象的名字就叫做:构造函数名.prototype
2. 构造函数的原型对象(除Object的原型对象),他们的隐式原型属性均指向Object.prototype对象。【也可以理解为:这些显式原型对象,都是对象,所以都是由Object实例化而来,所以它们的隐式原型属性指向Object的显式原型属性】
3. 所有的构造函数(包括Object构造函数),都是Function的实例化对象【我们知道:实例化对象的隐式原型属性,指向构造函数 的显式原型属性 所指向的对象】,所以这些构造函数的隐式原型属性均指向Funtion.prototype。
4. 还有一个很奇妙的环,记住就好了,我们通过Function也是自身的实例化对象,来方便快速记忆
究其本质,在MDN上有这么一段话:
__proto__的读取器(getter)暴露了一个对象的内部
[[Prototype]]
。对于使用对象字面量创建的对象,这个值是 Object.prototype (en-US)。对于使用数组字面量创建的对象,这个值是 Array.prototype。对于functions,这个值是Function.prototype (en-US)。对于使用 new fun 创建的对象,其中fun是由js提供的内建构造器函数之一(Array, Boolean, Date, Number, Object, String 等等),这个值总是fun.prototype。对于用js定义的其他js构造器函数创建的对象,这个值就是该构造器函数的prototype属性。
5. 代码运行分析
var a = {
n: 1
}
a.x = a = {
n:2
}
console.log( a.x ); // undefined
对原来的代码进行一个调整:
var a = b = {
n: 1
}
b.x = a = {
n:2
}
console.log( a.x ); // undefined
两个代码的执行结果是相同的,两段代码中,a的指向,也是相同的。
为什么这么说呢?
我们知道,“ . ” 执行的优先级高于 “ = ”。因此会先解析b.x,我们可以直接将b.x理解为0x0001对象的x属性。
所以不论是b.x还是a.x,都表示为 0x0001 对象的x属性;b.x = a = {n:2}、a.x = a = {n:2}这二者都是将地址为0x0002的{n:2}对象赋值给0x0001对象的x属性。
6. 代码执行分析
console.log( parseInt('200') ); // 200
console.log( parseFloat('200.a') ); // 200
console.log( parseInt('200.a')>=200 ); // true
console.log( parseFloat('200.a')>=NaN ); // false
7. TCP协议三次握手可能造成flood攻击
TCP/IP是一个开放的协议平台,它基于的对象是可信用户群,所以缺少一些必要的安全机制。
在TCP协议三次握手的连接过程中,如果一个用户向服务器发送了SYN报文,服务器又发出 SYN+ACK 应答报文后未收到客户端的 ACK 报文,这种情况下服务器端会再次发送SYN+ACK给客户端,并等待一段时间后丢弃这个未完成的连接,这段时间的长度称为SYN Timeout,一般来说这个时间是分钟的数量级。
SYN 泛洪攻击:通过发送大量的伪造 TCP 连接报文,造成大量的 TCP 半连接,服务器端将为了维护这样一个庞大的半连接列表而消耗非常多的资源。这样服务器端将忙于处理攻击者伪造的TCP连接请求而无法处理正常连接请求,甚至会导致堆栈的溢出崩溃。
8. TCP协议如何判断包的顺序?
通过seq序号+时间戳结合判断包的顺序。
- seq只有32位,因此当包的数量超过这一值时,单纯通过seq来判断包的顺序,就会出现问题。
- 触发重传时,响应时间较短,seq就会很小,但这个seq有可能只是上一步的响应,因此还需要结合着时间来组合判断。
9. 如何将一个Set对象转化为一个数组?
let ref = [1, 2, 3];
let a = new Set([1, 2, 3]);
let b = new Set([1, 2, 3]);
let c = new Set([1, 2, 3]);
let d = new Set([1, 2, 3]);
console.log( [].slice.apply(ref)); // [ 1, 2, 3 ]
console.log( [].slice.apply(a)); // [] ×
console.log( [...b] ); // [ 1, 2, 3 ] √
console.log( Array.from(c) ); // [ 1, 2, 3 ] √
console.log( new Array(d) ); // [ Set { 1, 2, 3 } ] ×
10. 数组的常见方法中,有哪些会改变原数组?
- join()
- push() 和 pop() Y (Y表示会改变原数组)
- shift() 和 unshift() (和上面的push,pop相反,针对第一项内容) Y
- reverse() Y
- indexOf() 和 lastIndexOf()
- every()
- some()
- toString() 和 toLocaleString()
- sort() Y
- concat()
- slice()
- splice() Y
- forEach()
- map()
- filter()
- reduce() 和 reduceRight()
11. 代码执行分析
const a = [1, 2];
console.log(a == Array.from(a)); // false
console.log(a === Array.from(a)); // false
这很好理解,拿着a的元素创建了一个新的数组,两个数组内容是相同的,但是对于这种引用类型的数据来说,只是简单地比较了地址是否相同。
12. CSS优化
1. 内联首屏关键CSS
将CSS直接内联到HTML文档中能使CSS更快速地下载。而link引入外部CSS文件时,需要在HTML文档下载完成后才知道所要引用的CSS文件,然后才下载它们。所以说,内联CSS能够使浏览器开始页面渲染的时间提前,因为在HTML下载完成之后就能渲染了。
我们不需要手动确定哪些内容是首屏关键CSS,Github上有一个项目Critical CSS,可以将属于首屏的关键样式提取出来,当然为了保证正确,大家最好再亲自确认下提取出的内容是否有缺失。
不过内联CSS有一个缺点,内联之后的CSS不会进行缓存,每次都会重新下载。不过如上所说,如果我们将内联后的文件大小控制在了14.6kb以内,这似乎并不是什么大问题。
2. 异步加载CSS
CSS会阻塞渲染。如果同步加载CSS样式,对一个div进行了频繁的样式计算后,可能被一行JS代码给删了,于是就做了很多无用功。所以最好使用外部CSS,等待HTML文档加载好了,再异步加载CSS。
异步加载CSS的4个方法:
2.1)使用JS动态创建一个link元素,然后插入到DOM中
// 创建link标签
const myCSS = document.createElement( "link" );
myCSS.rel = "stylesheet";
myCSS.href = "mystyles.css";
// 插入到header的最后位置
document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );
2.2)将link的 media 属性、 rel 属性、as 属性赋予一个与用户浏览器不符的值。对浏览器来说,如果这些属性值与浏览器当前状态不同,其优先级会被放低,会在不阻塞页面渲染的情况下再进行下载。 在文档加载完成后,再将其值更改回去。
<!--
错误(可导致异步加载)
media:print - 表示打印预览模式(或者是一个不存在的值)
正确应该为:
media:screen - 计算机屏幕、
all - 所有设备
-->
<link media="noexist" rel="stylesheet" href="mystyles.css" onload="this.media='all'">
<!--
错误(可导致异步加载):
rel:alternate - 表示被链接的文档是打印页、翻译或镜像(或者是一个不存在的值)
正确应该为:
rel:stylesheet - 被链接的文档是样式表
-->
<link rel="alternate stylesheet" href="mystyles.css" onload="this.rel='stylesheet'">
<!--
错误(可导致异步加载):
as:video - 表示被预拉取的资源为视频
正确应该为:
as:style - 被预拉取的资源为样式表
-->
<link rel="prefetch" as="video" href="mystyles.css" onload="this.rel='stylesheet'">
在rel="prefetch"的前提下,设定as才会有效果。它的作用为预加载,提前识别资源类型,对资源实现一个提前加载,这样在加载下一个页面时,渲染就会更快。不过该属性暂时还没有被各个浏览器兼容
3. 借助webpack等构建工具对css进行压缩
这个也效果最明显的一个优化方法了。
4. 删除无用的样式
构建工具通常只是删除多余的空格,我们可以将被覆盖、重复的样式删除掉,来进一步优化代码。
5. 有选择地使用选择器
选择器的匹配是自右向左进行的,这一策略导致了不同种类的选择器之间的性能也存在差异。例如借助 #markdown-content-h3 直接定位一个h3标签,要比 #markdown .content h3 一层一层定位h3花费时间更少。
前者可以直接定位到对应标签,后者需要会先找到所有的h3标签,然后过滤掉祖先不是.content的,最后过滤掉.content祖先不是#markdown的。
不过由于现代浏览器对这方面已经做了很多的优化,因此选择器之间的差异已经基本可以忽略不计了。但是这几点还是尽可能避免:
- 保持简单,不要使用嵌套过多过于复杂的选择器。
- 通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用。
- 不要使用类选择器和ID选择器修饰元素标签,如
h3#markdown-content
,这样多此一举,还会降低效率。 - 不要为了追求速度而放弃可读性与可维护性。
6. 尽量少用浏览器的计算属性
例如clientHight等这一类属性,在计算时,尽量先将它们的值保存起来。直接使用这一属性时,每次遇到该属性,都要进行一次计算。十分消耗性能。
7. 尽量不要使用@import引入css文件
我们知道,link引入的css文件,可以与html一同被解析,一边解析一边渲染,这样 “html全部解析完成” 与 “css全部解析完成” 之间就不会存在较大的间隙,看起来相对是连贯的,页面上一边呈现出结构,一边渲染样式。
即使我们将link异步加载,它也是同html一并解析的,在html全部解析完毕之后,就可以直接应用已经被解析好的css样式,然后直接渲染至页面。
使用link,不论怎样,html解析和css解析都是同步进行的。
但是用@import引入css样式就不一样,它会等到html全部解析完毕之后,才去解析引入的css样式,这就导致页面先有了结构,过一段时间才会出现样式。而且如果网速慢的话,可能会导致页面的样式混乱。在IE中,对@import这种样式表引入方式甚至还有数量限制。
13. assign、[... x]实现的是什么克隆?
先看一下深克隆的定义:所有属性均与原对象脱离实现。我们暂且认定这个所有,指的是当前对象的所有属性,不包含对象内的对象的属性。
因为很多博客都讲assign对第一层实现的是深克隆,下面几层实现的均是浅克隆
let a = {
n: 1,
m: 2,
obj: {
p: 4
}
}
let b = Object.assign(a);
let c = Object.assign({}, a);
console.log( b === a ); // true
c.n = 3;
c.obj.p = 5;
console.log('a.n: ', a.n, ', c.n: ', c.n ); // a.n: 1 , c.n: 3
console.log( 'a.obj.p: ', a.obj.p , ', c.obj.p: ', c.obj.p ); // a.obj.p: 5 , c.obj.p: 5
let a = {
n: 1,
m: 2,
obj: {
p: 4
}
}
let b = {...a};
b.n = 3;
b.obj.p = 5;
console.log('a.n: ', a.n, ', b.n: ', b.n ); // a.n: 1 , b.n: 3
console.log( 'a.obj.p: ', a.obj.p , ', b.obj.p: ', b.obj.p ); // a.obj.p: 5 , b.obj.p: 5