金三银四来啦!我的第一篇掘金文章《前端面试题》献给大家!(第一篇章)
从年前开始看面试题,到年后,一直在为金三银四准备着,,希望今年可以回暖一些,让大家都能找到自己想要的工作!!!冲啊!!!
一. 说说强缓和协商缓存。
什么是缓存,缓存是指浏览器(客户端)在本地磁盘中对访问过的资源保存过的副本文件,浏览器缓存有什么优点呢?减少重复请求数据,避免通过网络再次加载资源,节省流量;降低服务器的压力,提升网站性能;加快客户端加载网页的速度,提升用户体验
浏览器的请求流程:
浏览器在第一次请求后缓存资源,再次请求时:
1、浏览器获取该缓存资源的 header 中的信息,根据response header中的expires和cache-control来判断是否命中强缓存,如果命中直接从缓存中读取资源。
2、如果没有命中强缓存,浏览器就会发送请求到服务器,这次请求会带上IF-Modified-Since或者IF-None-Match,它们的值分别是第一次请求返回Last-Modified或者Etag,由服务器来对比这一堆字段来判断是否命中。
3、如果命中,则服务器返回304状态码,并且不会返回资源内容,浏览器会从缓存中获取;否则服务器最终会返回资源的实际内容,并更新header中的相关缓存字段。
强缓存
强缓存是根据返回头中的Expires或者Cache-Control两个字段控制的,都表示资源的缓存有效时间。如果二者同时存在,Cache-Control的优先级高于Expires。
Expires:是http 1.0 的规范,值是一个GMT格式的时间点字符串,例如:Expires:Mon,18 Oct 2066 23:59:59 GMT。
这个时间点代表资源失效的时间,如果当前时间戳在这个失效时间之前,则判断命中缓存。
缺点: 由于失效时间是一个绝对时间,所以当服务器时间与客户端时间偏差较大时,就会导致缓存混乱。
Cache-Control:是http 1.1的规范,一般使用该字段的max-age值来进行判断。
优点: 这个值是一个相对时间,例如:Cache-Control:max-age=3600代表资源有效期为3600秒,并且返回头中的Date表示消息发送的时间,表示当前资源在 Date ~ Date +3600s 这段时间里都是有效的。
关于Cache-Control的其他值:
- no-cache:不使用本地内存,需要使用协商缓存。
- no-store:禁止浏览器缓存数据,每次请求资源都会像服务器要完整的资源。
- public:可以被所有用户缓存,包括终端用户和cdn等中间件代理服务器。
- private:只能被终端用户的浏览器缓存。
协商缓存
协商缓存由服务器决定缓存资源是否可用。
主要涉及到两对属性字段,都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match,若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。
Last-Modified/If-Modified-Since 二者的值都是GMT格式的时间字符串。Last-Modified标记最后文件修改时间,下一次请求时,请求头会带上If-Modified-Since,用来告诉服务器本地缓存的文件最后修改的时间,服务器根据文件的最后修改时间判断资源是否有变化。若没有则返回 304 Not Modified,请求不会返回资源内容,浏览器直接使用本地缓存。若有变化,就正常返回资源内容,新的 Last-Modified会在 response header 返回,并在下次请求之前更新本地缓存的 Last-Modified,下次请求时,If-Modified-Since会启用更新后的 Last-Modified。
Etag/If-None-Match:只要资源有变化就这个值就会改变。服务器根据文件本身算出一个哈希值并通过 ETag字段返回给浏览器,接收到 If-None-Match 字段以后,服务器通过比较两者是否一致来判定文件内容是否被改变。
与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于在服务器上ETag 重新计算过,response header中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。
如何设置强缓存和协商缓存
1、后端服务器,写入代码逻辑中:
res.setHeader('max-age': '3600 public')
res.setHeader(etag: '5c20abbd-e2e8')
res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT)
2、Nginx 配置:
add_header Cache-Control "max-age=3600"
二.介绍一下TCP协议 , UDP协议
在TCP/IP网络体系结构中,TCP(传输控制协议,Transport Control Protocol、UDP(用户数据报协议,User Data Protocol)是传输层最重要的两种协议,为上层用户提供级别的通信可靠性。
传输控制协议(TCP) :TCP最大的特点就是提供的是面向连接、可靠的字节流服务。
“面向连接” 就是在正式通信前必须要与对方建立起连接,是按照电话系统建模的。比如你给别人打电话,必须等线路接通了、对方拿起话筒才能相互通话。
TCP协议是一种可靠的、一对一的、面向有连接的通信协议,TCP主要通过下列几种方式保证数据传输的可靠性:
(1)在使用TCP协议进行数据传输时,往往需要客户端和服务端先建立一个“通道“、且这个通道只能够被客户端和服务端使用,所以TCP传输协议只能面向一对一的连接。
(2)为了保证数据传输的准确无误,TCP传输协议将用于传输的数据包分为若干个部分(每个部分的大小根据当时的网络情况而定),然后在它们的首部添加一个检验字节。当数据的一个部分被接收完毕之后,服务端会对这一部分的完整性和准确性进行校验,校验之后如果数据的完整度和准确度都为100%,在服务端会要求客户端开始数据下一个部分的传输,如果数据的完整性和准确性与原来不相符,那么服务端会要求客户端再次传输这个部分。
客户端与服务端在使用TCP传输协议时要先建立一个“通道”,在传输完毕之后又要关闭这“通道”,前者可以被形象地成为“三次握手”,而后者则可以被称为“四次挥手”。
通道的建立——三次握手:
(1)在建立通道时,客户端首先要向服务端发送一个SYN同步信号。
(2)服务端在接收到这个信号之后会向客户端发出SYN同步信号和ACK确认信号。
(3)当服务端的ACK和SYN到达客户端后,客户端与服务端之间的这个“通道”就会被建立起来。
通道的关闭——四次挥手:
(1)在数据传输完毕之后,客户端会向服务端发出一个FIN终止信号。
(2)服务端在收到这个信号之后会向客户端发出一个ACK确认信号。
(3)如果服务端此后也没有数据发给客户端时服务端会向客户端发送一个FIN终止信号。
(4)客户端在收到这个信号之后会回复一个确认信号,在服务端接收到这个信号之后,服务端与客户端的通道也就关闭了。
TCP协议能为应用程序提供可靠的通信连接,使一台计算机发出的字节流无差错地发往网络上的其他计算机,对可靠性要求高的数据通信系统往往使用TCP协议传输数据。
用户数据报协议 (UDP) :UDP(用户数据报协议)是一个简单的面向数据报的传输层协议。提供的是非面向连接的、不可靠的数据流传输。UDP不提供可靠性,也不提供报文到达确认、排序以及流量控制等功能。它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。因此报文可能会丢失、重复以及乱序等。但由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。 [2]
无连接的UDP协议
“无连接”就是在正式通信前不必与对方先建立连接,不管对方状态就直接发送。与手机短信非常相似:你在发短信的时候,只需要输入对方手机号就OK了。
UDP传输协议是一种不可靠的、面向无连接、可以实现多对一、一对多和一对一连接的通信协议。UDP在传输数据前既不需要建立通道,在数据传输完毕后也不需要将通道关闭。只要客户端给服务端发送一个请求,服务端就会一次性地把所有数据发送完毕。UDP在传输数据时不会对数据的完整性进行验证,在数据丢失或数据出错时也不会要求重新传输,因此也节省了很多用于验证数据包的时间,所以以UDP建立的连接的延迟会比以TCP建立的连接的延迟更低。UDP不会根据当前的网络情况来控制数据的发送速度,因此无论网络情况是好是坏,服务端都会以恒定的速率发送数据。虽然这样有时会造成数据的丢失与损坏,但是这一点对于一些实时应用来说是十分重要的。基于以上三点,UDP在数据传输方面速度更快,延迟更低,实时性更好,因此被广泛地用于通信领域和视频网站当中。
UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送ICMP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。例如,在默认状态下,一次“ping”操作发送4个数据包。大家可以看到,发送的数据包数量是4包,收到的也是4包(因为对方主机收到后会发回一个确认收到的数据包)。这充分说明了UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效率高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。
协议差别
TCP/IP 和UDP最大的区别就是:TCP/IP是面向连接的,UDP是无连接的。TCP协议和UDP协议各有所长、各有所短,适用于不同要求的通信环境。TCP协议和UDP协议之间的差别如下表所示。
在实际的使用中,TCP主要应用于文件传输精确性相对要求较高且不是很紧急的情景,比如电子邮件、远程登录等。有时在这些应用场景下即使丢失一两个字节也会造成不可挽回的错误,所以这些场景中一般都使用TCP传输协议。由于UDP可以提高传输效率,所以UDP被广泛应用于数据量大且精确性要求不高的数据传输,比如我们平常在网站上观看视频或者听音乐的时候应用的基本上都是UDP传输协议。
三.聊聊常用的ts,相对js有那些语法糖
字符串的新特性:多行字符串,
参数类型:声明变量中指定类型;声明函数时指定类型;函数参数也可以指定类型
类:在类的内部,有三种访问控制符:public控制的属性,可以在类的内部和外部访问到,是默认的访问控制符;private控制的属性,只可以在类的内部被访问;protected控制的属性,可以在类的内部和子类的被访问
泛型:参数化类型,一般用来限制集合的内容
接口:用来建立某种代码约定,使得其他开发者在调用某个方法或创建新的类时必须遵循接口所定义的代码约定;interface关键字时声明一个接口;implements关键字是声明一个类去实现一个接口的方法
模块 module:export用来控制模块对外暴露什么东西;import用来引入其他模块暴露东西
注解:注解为程序的元素(类、方法、变量)加上更直观更明了的说明。这些说明信息与程序的业务逻辑无关,而是供指定的工具或框架使用的。
四.every,some,find,filter,forEach,map的区别,for in for of的区别等等
-
map():返回一个新的Array,每个元素为调用func的结果
-
filter():返回符合func条件的元素数组
-
find():返回第一个符合条件的元素对象
-
some():返回一个boolean,判断是否有元素是否符合func条件
-
every():返回一个boolean,判断每个元素是否符合func条件
-
forEach():没有返回值,只是针对每个元素调用func
-
for-in:循环实际是为循环“enumerable”对象而设计的,for in也可以循环数组,但是不推荐这样使用,for-in是用来循环带有字符串key的对象的方法
缺点:只能获得对象的键名,不能直接获取键值
-
for-of:可以使用的范围包括数组、Set和Map结构、某些类似数组的对象(比如arguments对象、DOM NodeList对象)、后文的Generator对象,以及字符串
五.箭头函数和普通函数有什么区别
-
箭头函数是匿名函数,不能作为构造函数,不能使用new。
-
箭头函数不绑定arguments,取而代之用rest参数...解决
-
箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
-
箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
-
箭头函数没有原型属性
-
箭头函数不能当做Generator函数,不能使用yield关键字
六.什么是跨域 , 如何解决跨域问题
什么是跨域:指的是浏览器不能执行其他网站的脚本,他是由浏览器的同源策略造成的,是浏览器对js施加的安全限制。同源策略:是指协议(protocol)、域名(host)、端口号(port),都必须相同,其中一个不同就会产生跨域。
非同源限制:无法读取非同源网页的cookie、localstorage、indexedDB;无法接触非同源网页的DOM;无法向非同源地址发送AJAX请求。
解决跨域的方法
1、JSONP
核心思想:网页通过添加一个'script'元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
优点:简单适用,兼容性好(可以兼容低版本IE)
缺点:只支持 get 请求,不支持 post 请求,导致数据不安全
- 原生实现(JSONP 需要服务端的支持)
<script src='http://test.com/data?callback=func'></script>
//向服务器 test.com 发出请求,该请求的查询字符串有一个 callback 参数,用来指定回调函数的名字
//给客户端返回数据 "func('+JSON.stringify(data)+')" ,浏览器把字符串变成 js 表达式执行
//func 需要是一个全局函数
<script type='text/javascript'>
function func(res){
// 处理获得的数据
}
</script>
- jQuery 提供了 JSONP 的处理方式
$.ajax({
url: 'http://www.test.com:8000/login',
type: 'get',
dataType: 'jsonp',// 设置请求方式为 jsonp
jsonpCallback: 'handleCallback',// 自定义回调函数名
data: {}
})
- Vue.js
this.$http.jsonp('http://www.test.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then(res => {});
2、CORS 跨域资源共享(Cross-Origin Resource Sharing)
这种方式最主要的特点就是会在响应的 HTTP 首部增加 Access-Control-Allow-Origin 等信息,从而判定那些资源站可以进行跨域请求。CORS 与 JSONP 对比来说又是比较明显,JSONP 只支持 GET 方式,而且 JSONP 并不符合处理业务的正常流程。采用 CORS 的方式,前端编码与正常非跨域请求没有什么不同。
客户端发送请求(ajax):在真正的发送跨域请求之前会发送一个试探性请求(OPTIONS),服务器接收到 OPTIONS请求之后,做一个处理,返回成功,表示可以发送跨域请求,再发送真正的跨域请求。服务端设置相关的头信息(需要处理试探性请求OPTIONS)
带 cookie 跨域请求:前后端都需要进行设置。
1、前端设置:根据 xhr.withCredentials 字段判断是否带有 cookie
- 原生 ajax
var xhr = new XMLHttpRequest();// ie8/9 需用 window.XDomainRequest 兼容
// 前端设置是否带 cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.test.com:8000/index', true);
xhr.setRequestHeader('Content-ype', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText)
}
};
- jQuery ajax
$.ajax({
url: 'http://www.test.com:8000/index',
type: 'get',
data: {},
xhrFields: {
withCredentials: true // 设置前端是否带 cookie },
crossDomain: true // 会让请求头中包含跨域的额外信息,但不会含 cookie
});
- vue-resource
Vue.http.options.credentials = true
- axios
axios.defaults.withCredentials = true
2、服务端设置 服务器端对于 CORS 的支持,主要是通过设置 Access-Control-Allow-Origin 来进行的。如果浏览器检测到相应的设置,就可以允许 Ajax 进行跨域访问
// 不使用*,自动适配跨域域名,避免携带Cookie时失效
String origin = request.getHeader('Origin');
if (StringUtils.isNotBlank(origin)) {
response.setHeader('Access-Control-Allow-Origin', origin);
}
// 自适应所有自定义头
String headers = request.getHeader('Access-Control-Request-Headers');
if (StringUtils.isNotBlank(headers)) {
response.setHeader('Access-Control-Allow-Headers', headers);
response.setHeader('Access-Control-Expose-Headers', headers);
}
// 允许跨域的请求方法类型
response.setHeader('Access-Control-Allow-Methods', '*');
// 预检命令(OPTIONS)缓存时间,单位:秒
response.setHeader('Access-Control-Max-Age', '3600');
// 明确许可客户端发送Cookie,不允许删除字段即可
response.setHeader('Access-Control-Allow-Credentials', 'true');
3、反向代理
既然不能跨域请求,那么我们不跨域就可以了,通过在请求到达服务器前部署一个服务,将接口请求进行转发,这就是反向代理。通过一定的转发规则可以将前端的请求转发到其他的服务。
通过反向代理我们将前后端项目统一通过反向代理来提供对外的服务,这样在前端看上去就跟不存在跨域一样。
反向代理麻烦之处就在于对 Nginx 等反向代理服务的配置,在目前前后端分离的项目中很多都是采用这种方式。
proxyTable: {
'/api': {
target:'http://api.douban.com/v2', // 你请求的第三方接口
changeOrigin:true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite:{ // 路径重写,
'^/api': '' // 替换target中的请求地址,也就是说以后你在请求http://api.douban.com/v2/XXXXX这个地址的时候直接写成/api即可。
}
}
},
七.介绍@support 、@media、 calc
1、calc() 函数用于动态计算长度值。
● 需要注意的是,运算符前后都需要保留一个空格,例如:width: calc(100% - 10px);
● 任何长度值都可以使用calc()函数进行计算;
● calc()函数支持 “+”, “-”, “*”, “/” 运算;
● calc()函数使用标准的数学运算优先级规则;
2、@support主要是用于检测浏览器是否支持CSS的某个属性,其实就是条件判断,如果支持某个属性,你可以写一套样式,如果不支持某个属性,你也可以提供另外一套样式作为替补。
3、@media 查询,你可以针对不同的媒体类型定义不同的样式。
八.标准盒子模型 , IE盒模型
标准盒模型box-sizing: border-box,是默认值,width/height 不包含padding和border。
ie 盒子模型box-sizing: content-box,width/height 包含了padding和border。
标准盒模型会因为padiing被撑大,如果不想盒子因为padding被撑大就可以设置盒子box-sizing为border-box;
整理的时候查了下资料,关于盒模型理论上是四种,但是margin-box没有出现过,padding-box已经被抛弃了,就剩border-box和content-box了。
九.re m\em\vh\vw\px 的区别
px是像素,每个像素点都是大小相等的,所以像素为计量单位被分在了绝对长度单位中
em是相对长度单位,相对单位,基准点为父节点字体的大小,如果自身定义了font-size按自身来计算,整个页面内1em不是一个固定的值
rem相对单位,相对的知识HTML根元素Font-size的值
如果想要简化font-size的转化,我们可以在根元素html中加入font-size: 62.5%
html {font-size: 62.5%; } /* 公式16px*62.5%=10px */
这样页面中1rem=10px、1.2rem=12px、1.4rem=14px、1.6rem=16px;
「vh、vw」:主要用于页面视口大小布局,在页面布局上更加方便简单
十.怎么处理 1 像素的问题
Transform:scale(0.5)方案-推荐:很灵活
设置height:1px,根据媒体查询结合transform缩放为相应尺寸
div{
Height:1px;
Background:#000;
-webkit-transform:scaleY(0.5);
}
十一.水平垂直居中
1、绝对定位 absolute+负margin
2、绝对定位absolute+margin auto
3、absolute+calc(计算)
4、absolute+transform(过渡)
5、table-cell:middle center(直接给父级设置)
6、弹性盒子flex
display:flex;
justify-content: center;’
align-items: center
7、Grid(网格布局)
给父级设display:grid;
给子元素设align-self:center;justify-self:center
十二.说一下原型链
Javascript是面向对象的,每个实例对象都有一个_proto_属性,该属性指向它的原型对象,这个实例对象的构造函数有一个原型属性prototype,与实例的proto属性指向同一个对象。当一个对象在查找一个属性的时候,自身没有就会根据_proto _向它的原型进行查找,如果都没有,则向它的原型的原型继续查找,直到查到Object.prototype.proto_为null,这样也就形成了原型链。原型链的源头是Object!
为什么要使用原型链呢?
1.为了实现继承,简化代码,实现代码重用!
2.只要是这个链条上的内容,都可以被访问和使用到
使用原型链有什么作用?
继承:prototype用来实现基于原型的继承与属性的共享
避免了代码冗余,公用的属性和方法,可以放到原型对象中,这样,通过该构造函数实例化的所有对象都可以使用该对象的构造函数中的属性和方法!减少了内存占用
__proto__和prototype的区别
proto :是实例对象指向原型对象的指针,隐式原型,是每个对象都会有的一个属性。
prototype:是构造函数的原型对象,显式原型,只有函数才会有。
十三.说一下常用的继承
1、原型链继承 :子类型的原型为父类型的一个实例对象。
2、构造继承
3、实例继承
4、拷贝继承
5、组合继承
6、寄生组合继承
7、ES6的继承(extends)
十四.说一下闭包
闭包(closure)是指函数能够访问其词法作用域之外的变量,即使在函数被调用后仍然可以访问。
换句话说,当一个函数在其外部定义的变量的作用域外被调用时,它可以访问这些变量,并且可以在调用完成之后继续访问这些变量。这就是闭包的作用。闭包的主要作用就是:延伸了变量的作用范围。
至于闭包的使用场景,其实在日常开发中使用到是非常频繁的
- 防抖节流函数
- 定时器回调
优点:内部变量是私有的,可以做到隔离作用域,保持数据的不被污染性
缺点:内部变量是私有的,可以做到隔离作用域,那也就是说垃圾回收机制是无法清理闭包中内部变量的,那最后结果就是内存泄漏
十五.apply,bind,call的区别
相同点:作用相同,都是动态修改this指向;都不会修改原先函数的this指向
不同点:
1、执行方式不同:call和apply是改变后页面加载之后就立即执行,是同步代码。bind是异步代码,改变后不会立即执行;而是返回一个新的函数
2、传参方式不同:call和bind传参是一个一个逐一传入,不能使用剩余参数的方式传参。 apply可以使用数组的方式传入,数组方式可以使用剩余参数的方式传入
3、修改this的性质不同: call、apply只是临时的修改一次,也就是call和apply方法的那一次;当再次调用原函数的时候,她的指向还是原来的指向。bind是永久修改函数this指向,但是它修改的不是原来的函数;而是返回一个修改过后新的函数,此函数的this永远被改变了,绑定了就修改不了
十六.toString 和 Value of 的区别
返回值类型的差别:
· 1. toString一定将所有内容转为字符串
· 2. valueOf取出对象内部的值,不进行类型转换
用途的差别:
· 1. valueOf专用于算数计算和关系运算
· 2. toString专用于输出字符串
共同的缺点: 无法获取null和undefined的值
十七.es6的新特性有哪些 ,然后Map 和 普通的 key value有什么区别?介绍weakMap
新特性
1、let 和 const
let 表示申明变量。const 表示申明常量
常量定义了就不能改了。对象除外,因为对象指向的地址没变。
const在申明是必须被赋值。
两者都为块级作用域。
2、模板字符串
3、解构
4、函数的默认值
5、Spread / Rest 操作符,三个点…
6、箭头函数
7、for of
for of遍历的是键值对中的值
for in遍历的是键值对中的键
8、class类,原型链的语法糖表现形式
9、导入导出
导入improt
导出export default
10、promise
Promise 用于更优雅地处理异步请求。
11、async/await
比promise更好的解决了回调地狱
12、Symbol,新的基本类型
13、Set集合
存储任何类型的唯一值,即集合中所保存的元素是不重复的。类数组结构。
let arrNew = new Set(待去重的数组)
Map和普通的key value的区别
1、初始化与使用普通对象可以直接使用字面量进行初始化,而 Map 需要 Map() 构造函数进行初始化,如果想要有初始值,则需要传递一个数组或其他元素为键值对的可迭代对象。这些键值对中的每一个都将被添加到一个新的 Map 中。
const obj = {name: 1,age: 2,};const map = new Map([["name", 1],["age", 2],]);
与普通对象相比,Map 作为哈希表提供了许多有用的功能。比如判断一个key是否在hash表中,在map中可以使用has方法轻松判断,但是在普通对象中可能会增加复杂度。
另外,set方法可以为Map设置key值,get方法可以获取value,size属性可以返回当前Map中key/value对的数量,而plain对象需要手动计算使用 自己的方法等
2、 密钥类型
普通对象只接受字符串和符号作为键值,其他类型将被强制转换为字符串类型,而 Map 可以接受任何类型的键值(包括函数、对象或任何原语)。
WeakMap
WeakMap是 ECMAScript6 新增的一种 “弱映射” 集合类型,它这门语言带来了增强的键/值对存储机制
WeakMap 中的“weak”(弱),描述的是 JavaScript 垃圾回收程序对待“弱映射”中键的方式;它的键被弱保持,也就是说,当其键所指对象没有其他地方引用的时候,它会被 GC ( Garbage Collection 垃圾回收) 回收掉
基本使用
WeakMap 的键必须是Object类型或继承Object的类型(尝试使用非对象设置键会抛出TypeError),值可以是任意类型;
十八.web应用是怎么处理错误上报问题的
1、try...catch...错误捕获(能捕获包裹体内的同步执行错误;不能捕获语法错误;不能捕获异步任务错误;不能捕获promise任务错误;不能捕获资源加载错误)
2、Window.onerror需要额外注意:跨域脚本加载错误只有一个“Script error”,并不能获取到错误信息。可以通过在
Window.onerror=function(message,source,lineno,colno,error){
Console.log(‘error’,{message,source,lineno,colno,error})
}
(能捕获所有同步执行错误;不能捕获语法错误;能捕获普通异步任务错误;不能捕获Promise任务错误;不能捕获async任务错误;不能捕获资源加载错误)
3、window.addEventListener(‘error’)捕获的错误和第二个一样
4、window.addEventListener(‘unhandledrejection’)可以捕获promise和异步
5、语法错误的话可以3和4一起搭配用
十九.箭头函数和普通函数的区别
1、外形不同:箭头函数使用箭头定义,普通函数中没有。
2、箭头函数全都是匿名函数:普通函数可以有匿名函数,也可以有具名函数
3、箭头函数不能用于构造函数:普通函数可以用于构造函数,以此创建对象实例。
4、箭头函数中 this 的指向不同,说箭头函数本身没有this,但是它在声明时可以捕获其所在上下文的this供自己使用,任何方法都改变不了其指向,如 call() , bind() , apply():在普通函数中,this总是指向调用它的对象,如果用作构造函数,它指向创建的对象实例。
5、箭头函数不具有 arguments 对象,取而代之用rest参数…解决,每一个普通函数调用后都具有一个arguments 对象,用来存储实际传递的参数。但是箭头函数并没有此对象。
6、其他区别:箭头函数不具有 prototype 原型对象。箭头函数不具有 super。
箭头函数不具有 new.target
二十.说一下进程和线程的区别
区别1:从属关系不同
从属关系不同:进程是正在运行程序的实例,进程中包含了线程,而线程中不能包含进程。
区别2:描述侧重点不同
描述侧重点不同:进程是操作系统分配资源的基本单位,而线程是操作系统调度的基本单位。
区别3:共享资源不同
共享资源不同:多个进程间不能共享资源,每个进程有自己的堆、栈、虚存空间(页表)、文件描述符等信息,而线程可以共享进程资源文件(堆和方法区)。
区别4:上下文切换速度不同
上下文切换速度不同:线程上下文切换速度很快(上下文切换指的是从一个线程切换到另一个线程),而进程的上下文切换的速度比较慢。
区别5:操纵者不同
操纵者不同:一般情况下进程的操纵者是操作系统,而线程的操纵者是编程人员。
总结
进程是操作系统分配资源的基本单位,而线程是操作系统调度的基本单位。一个进程中至少包含一个线程,线程不能独立于进程而存在。进程不能共享资源,而线程可以。线程可以看作是轻量级的进程,它们的主要区别体现在:从属关系、描述侧重点、共享资源、上下文切换速度和操纵对象等不同。
二十一.数组哪些方法可以改变数组的内容
push(),pop(),unshift(),shift(),reverse(),sort(),splice()
二十二.深拷贝有哪些方法,JSON.stringify(JSON.parse(arr))的弊端
1、通过JSON对象实现深拷贝
缺点: 无法实现对对象中方法的深拷贝,会显示为undefined
newValue = JSON.parse(JSON.stringify(oldValue)
2、通过JQuery的extend方法实现
var array = [1, 2, 3, 4]
var newArray = $.extend(true, [], array)
// true为拷贝,false为浅拷贝
3、使用递归
function deepClone(obj) { // 判断拷贝的是对象还是数组
var objClone = Array.isArray(obj) ? [] : {} // 进行深拷贝的对象不能为空
if(obj && typeof obj === 'object') {
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
if(obj[key] && typeof obj[key] === 'object') {
objClone[key] = deepClone(obj[key])
} else {
objClone[key] = obj[key]
} } } }
return objClone
}
4、lodash函数库实现深拷贝
lodash.cloneDeep()
5、使用slice和concat实现对数组的深拷贝
6、使用扩展运算符实现深拷贝
二十三.说一下eventloop 事件循环机制
JS是单线程语言,同一时间只能做一件事情,但是在实际应用场景中,肯定会出现线程的调度问题,如何使用单线程完成,用户交互、代码逻辑、UI渲染等行为,防止主线程的不阻塞,Event Loop 的方案应用而生。
架构图
1、JS中包含 同步任务 & 异步任务
2、同步任务都在主线程上执行,形成一个执行栈
3、异步任务分为宏任务&微任务
3、宏任务都将被存放在宏任务队列
4、微任务都将被存放在微任务队列
5、同步任务执行完,才会执行异步任务
6、异步任务执行时,将微任务队列全部执行完,才会执行一个 宏任务
7、所有任务都会被放到执行栈中,由主线程执行
9、微任务优先级更高,每次循环称为一次tick
10、单个宏任务(macrotask)->执行所有微任务(microtask)->渲染->单个宏任务(macrotask)->…
宏任务
- script(整体代码,看做一个方法,所以会首先执行)
- setTimeout
- setInterval
- I/O
- UI交互事件
- postMessage
- MessageChannel
- setImmediate(Node.js 环境)
微任务
- Promise.then
- Object.observe
- MutationObserver
- process.nextTick(Node.js 环境)
运行机制
- 在事件循环中,每进行一次循环操作称为 tick,每一次 tick 关键步骤如下
- 执行一个宏任务(栈中没有就从事件队列中获取)
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
- 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
- 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
微任务(microtask)为什么优先级高于宏任务(macro)?
在每个宏任务执行完,会执行微任务队列中的所有微任务,再取出一个宏任务执行,所以微任务优先级更高。
二十四.说一下_ proto_ 和 prototype 的理解
prototype是函数特有的属性,是Function的静态属性;__proto__是对象特有的属性。
因为函数本身是一种对象,所以函数既有prototype属性也有__proto__属性。
当函数使用prototype属性时,是作为构造函数使用;
当函数使用__proto__属性时,是作为一个对象使用。
另外,__proto__属性内部属性,尽量不要使用。可以用setPrototypeOf()和getPrototypeOf()代替。
-
对象有属性proto,指向该对象的构造函数的原型对象
-
方法除了有属性proto,还有属性prototype,prototype指向该方法的原型对象
-
函数(Function也是函数)是new Function的结果,所以函数可以作为实例对象,其构造函数是Function(),原型对象是Function.prototype
-
对象(函数也是对象)是new Object的结果,所以对象可以作为实例对象,其构造函数是Object(),原型对象是Object.prototype
-
Object.prototype的原型对象是null
二十五.Function 和 Object 的关系
1、首先Object和Function都是构造函数,而所有的构造函数的都是Function的实例对象. 因此Object是Function的实例对象
2、Function.prototype是Object的实例对象
由以上两句话直译就是:
Object.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype
3.实例对象的原型(我们以__proto__来表示)会指向其构造函数的prototype属性,有
Function.__proto__ === Function.prototype
关于instanceof的结果不要仅从字面上理解, 它的计算规则是: 如果右侧构造函数的prototype属性能在左侧的对象的原型链中找到, 那么就返回true, 否则就返回false
Object intanceof Function: Object.__proto__ === Function.prototype, 因为结果为true
Function instanceof Object: Function.__proto__.__proto__ === Object.prototype, 因为结果也为true
Object和Function互为实例对象是不对的,应该是 : Object是Function的实例对象, Function.prototype是Object的实例对象。
5.实例对象的constructor属性指向其构造函数, 因此Object.constructor === Function, Function.constructor === Function.
总结:
Object 对象,拿不到 Function 原型上的属性而Funtion对象可以拿到 Object 原型上的属性
二十六.防抖和节流函数实现,及其应用场景
防抖:
1、原理:
当持续触发事件时,在一定时间内没有再触发事件,事件才会处理函数一次,如果在设定的时间到来之前,又触发了事件,则重新开启定时器,执行最后一次触发事件。
2、应用场景:
-
scroll事件滚动
-
浏览器窗口的缩放resize事件
-
搜索框输入查询的时候
-
表单验证
-
按钮的提交事件
3、代码
function debounce(callback,time=300){
let t;
return function(){
//事件触发,清除t重新开始计时
clearTimeout(t);
t=setTimeout(callback,time);
}
}
window.onscroll=debounce(function(){
console.log("调用了一次");
},500);
节流:
1、原理:
在持续触发事件时,在一定时间内只调用一次函数,如果在规定时间内,再次触发此事件,则直接忽略不执行,其主要目的就是减少一段时间的触发频率
2、应用场景:
DOM元素拖拽功能实现
计算鼠标移动距离
监听scroll滚动事件
搜索、提交等按钮功能
3、代码
function throttle (fn, delay = 1000) {
let time = null;
return function () {
let that = this;
// 如果已经存在定时器了,则 不做处理
if (!time) {
time = setTimeout(() => {
fn.apply(that, arguments);
// 完结时,将time改为null
time = null;
}, delay);
}
};
}
二十七.flex 布局 实现八个元素分两行平均摆放
父盒子排列方式align-content为flex-start, 从起点开始放置盒子。通过flex-flow设置换行(row wrap)
子盒子设置,设置Flex:0 0 25%;flex属性是flex-grow, flex-shrink, flex-basis的简写。默认值 :0 1 auto
flex-grow:0 //不放大
flex-shrink:0 //不缩小
flex-basis:项目占据主轴的空间
二十八.display:none 和 visiblty的区别
相同:两者都能隐藏元素
不同:
1、display:none不占页面空间,visiblity:hidden占据原先页面空间
2、display:none 的子元素也一定无法显示,visiblity:hidden 的子元素可以设置显示。
3、display:none 引起页面重绘和回流, visiblity:hidden 只引起页面重绘。
二十九.Promise原理,常用的方法
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
promise是用来解决两个问题的:
回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
promise可以支持多个并发的请求,获取并发请求中的数据,这个promise可以解决异步的问题,本身不能说promise是异步的
实现then方法、catch方法、Promise.resolve、Promise.reject、Promise.all、Promise.race
三十.html 页面的声明周期
DOMContentLoaded —— 浏览器已完全加载 HTML,并构建了 DOM 树,但样式表之类的外部资源可能尚未加载完成。
load —— 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。
beforeunload —— 当用户正在离开页面时。
unload —— 用户几乎已经离开了
生命周期的作用:
DOMContentLoaded 事件 —— DOM 已经就绪,因此处理程序可以查找 DOM 节点,并初始化接口。
load 事件 —— 外部资源已加载完成,样式已被应用,图片大小也已知了。
beforeunload 事件 —— 用户正在离开:我们可以检查用户是否保存了更改,并询问他是否真的要离开。
unload 事件 —— 用户几乎已经离开了,但是我们仍然可以启动一些操作,例如发送统计数据。