前言
“Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。”
这次面试也是春招开始的尝试,大部分都是八股文,面试问题从css部分、js部分、http部分、浏览器部分再到框架react部分。最近准备春招的小伙伴可以看看,干货很多,希望对看这篇文章的掘友们有所帮助。
position有哪些属性和区别
position: static; 默认值。正常的的布局行为,即元素在文档常规流中的当前布局位置。此时top
,right
,bottom
,left
和z-index
属性无效。
position: relative; 相对定位,相对正常位置进行定位(元素放置在未添加定位时的位置),在不改变页面布局的前提下调整元素。对table-group
, table-row
,table-column
, table-cell
, table-caption
元素无效。
position: absolute; 绝对定位,元素会被移除正常文档流,并不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移。
position: fixed; 固定定位,元素会被移除正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口的位置来指定元素。元素的位置在屏幕滚动时不会改变。
position: sticky; 粘性定位,元素根据正常文档流进行定位,然后相对它的最近滚动祖先和最近块级祖先,基于top
,right
,bottom
和 left
的值进行偏移。偏移值不会影响任何其他的元素。
sticky 和 fixed的区别
fixed 相对于视口进行偏移,即定位基点是浏览器窗口,不管视口如何滚动,它的位置始终不变。搭配top,bottom,left,right这四个属性一起使用。
sticky 相对于最近的祖先元素随着页面的滚动而保持在固定的位置,超出祖先元素区域后就随着祖先元素一起滚动。sticky生效的前提是必须搭配top,bottom,left,right这四个属性一起使用,不能省略,用来定义“偏移距离”。
在Chrome如何支持小于12px文字
设置 -webkit-transform: scale(0.5);
可以使用 transform 元素(元素转换)的 scale 属性进行缩放,前面加上-webkit表示Chrome浏览器支持。使用该属性时只对块级元素或者设置display: block; 起作用
可以通过安装Advanced Font Settings插件支持到6px
对BFC规范的理解
BFC(Block Formatting Context) 块级格式化环境
-
BFC是css中的一个隐藏属性,可以为一个元素开始BFC,开启BFC该元素会变成一个独立的布局区域。
-
元素开启BFC后的特点:
- 开启BFC的元素不会被浮动元素所覆盖
- 开启BFC的元素的子元素和父元素外边距不会重叠
- 开启BFC的元素可以包含浮动的子元素
解决高度塌陷问题,开启BFC,使用任何属性去开启BFC 都会有一定的副作用,尽可能使用副作用表较小的方法。
则通过特殊的方式来开启BFC。对父元素使用某些属性,开启BFC。
-
根元素会创建BFC
-
设置元素的浮动(不推荐,副作用太大)
-
将元素设为行内块级元素(不推荐)
-
将元素得到
overflow
设置一个非visible的值(推荐使用,副作用很小)- 设置元素的
position
属性值(元素的position
为absolute
或fixed
) - 表格单元格(
disaplay: table-cell
,HTML表格单元个默认属性) - 弹性盒子 flex boxes(元素的
display: flex
或inline-flex
)
- 设置元素的
float: left; //(不推荐使用,副作用太大)
display: inline-block; //(不推荐使用,副作用太大)
overflow: hidden; (常用,副作用很小)
// 某个元素因为其他元素浮动的影响而发生改变,使用clear清除浮动
clear: left; // 清除左浮动
clear: right; // 清除右浮动
clear: both; // 清楚两侧浮动的影响
BFC的范围:一个BFC包含创建该上下文元素的所有子元素,但不包括创建了新BFC的子元素的内部元素。
也就是从另一个角度说明,一个元素不能同时存在于两个BFC中
BFC的一个最重要的效果是,让处于BFC内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。
BFC的效果:
- 内部的盒会在垂直方向上一个接一个排列(可以看作BFC中有一个常规流)
- 处于同一个BFC中的元素相互影响,可能会发生margin collapse(外边距重叠),只有可能垂直方向上发生外边距重叠,水平方向上不会。
- 每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此;
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会受外面元素的影响,反之亦然;
- 计算BFC的高度时,考虑BFC所包含的的所有元素,连浮动元素也参与计算;
- 浮动盒区域不叠加到BFC上;
js中有哪几种数据类型
八种 Number,String,Boolean,undefined,null,Object,Bigint,Symbol
数据类型检测的方法
typeof判断null
和数组
为object类型,function 为function类型,其他的都可以判断准确。
instanceof 通过原型链来判断数据类型,所以只能判断引用式类型。通过原型链一层一层往上查找。
Object.prototype.toString.call() 通过call() 方法绑定调用Object原型对象上的toString()方法判断数据类型。
判断数组方式的方法
Array.isArray() 判断数组方法
instanceof 操作符判断
Object.prototype.toString.call() 判断
常用的就这三种,还有的可以通过Object.getPrototypeOf()获取某个对象的原型对象,Array.prototype.isPrototypeOf()判断其是不是某个对象的原型对象。
箭头函数和普通函数的区别
- 箭头函数没有自己的this,需要依赖父作用域中的this,就是定义时所在的对象。继承父作用域中的
this
。 - 不可以当作构造函数,因为没有new命令,使用会报错。
- 没有arguments对象,可以用rest参数代替
箭头函数作为构造函数会发生什么
会发生报错,因为箭头函数中是没有new 命令的。
因为箭头函数没有自己的this
,在使用构造函数 new 一个对象的时候无法绑定和修改 this
;同时箭头函数也没有 prototype
属性,无法将该属性赋给实例对象的 __proto__
。
new 的本质是生成一个新对象,将对象的proto指向函数的prototype,再执行call 方法 普通构造函数通过 new 实例化对象时 this 指向实例对象,而箭头函数没有自己的this值,用call()或者apply()调用箭头函数时,无法对this进行绑定 箭头函数没有 prototype 属性 ,不能作为构造函数,否则 new 命令在执行时无法将构造函数的 prototype 赋值给新的对象的proto
Map和Set的区别
Set:成员唯一、无需且不重复。[value, value]键值与键名是一致的(或者说只有键值,没有键名)
Map:本质上是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
WeakMap和Map的区别
WeakMap:
- 只接受对象作为键名(null)除外,不接受其他类型的值作为键名。
- 键名是弱引用,键值是正常引用。所以键名的弱引用不会被计入垃圾回收机制,键值正常回收。
- 不能遍历。
WeakMap的专用场合就是,它的键名所对应的对象,可能会在将来消失。WeakMap
结构有助于防止内存泄漏。
拓展一下WeakSet:
- 成员只能是对象
- WeakSet中的对象都是弱引用,不计入垃圾回收机制。也就是如果其他对象都不在引用该对象,那么会自动回收该对象所占用的内存。不容易造成内存泄露。
- 不能遍历。
对闭包的理解
之前看冴羽老师的文档是这样说的。
MDN对闭包的定义为:闭包是指那些能够访问自由变量的函数。
自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。
var a = 1;
function foo() {
console.log(a);
}
foo();
函数 foo + foo 函数访问的自由变量 a 不就是构成了一个闭包嘛,还真的是这样。
所以在《JavaScript权威指南》中就讲到:从技术的角度讲,所有的JavaScript函数都是闭包。这怎么跟我们平时看到的讲到的闭包不一样,这是理论上的闭包,其实还有一个实践角度上的闭包。
ECMAScript中,闭包指的是:
-
从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
-
从实践角度:以下函数才算是闭包:
- 即使创建它的上下文已经被销毁,它仍然存在(比如,内部函数从父函数中返回)
- 在代码中引用了自由变量
我们大部分理解的都是实践角度上的闭包。
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo();
这是一个经典的闭包,当父级函数checkscope 执行完毕,从checkscope 执行上下文从执行上下文栈中弹出。然后当f 函数执行,还是可以读取到变量scope的值,即使checkscope 上下文被销毁了,但是javaScript依然会让scope活内存中,f函数依然可以f 函数的作用域链找到它。
js垃圾回收机制,工作流程
GC是什么
GC(Garbage Collection 垃圾回收)
,在我们的程序运行过程中会产生很多垃圾,这些垃圾可能是程序不用的内存
或者是之前使用过以后不需要再使用的内存空间
,而GC就是负责回收垃圾的,也就是我们常说的垃圾回收收机制
。
垃圾产生
我们在写代码时,声明的变量、对象等等都是需要占用内存的。而不同的类型的数据是保存到不同的内存中的。
基本数据类型保存在栈内存中
,因为基本数据类型占用空间小、大小固定,通过值来访问,属于被频繁使用的数据。- 引用数据类型(
Array
、Function
、Object
)时存储在堆内存
中,引用数据类型占据空间大、大小不固定,如果存储在栈中非常影响程序的性能。
对于引用数据类型,在栈内存的变量名中保存了一个地址,这个地址指向堆内存中的实际值。如果一个对象被重新赋值,与原来的对象数据之间不存在了引用关系,这个时候就是无用的对象,为了不影响系统性能,所以这个无用的对象就需要被垃圾回收。
垃圾回收的方法
在JavaScript 内存管理中有一个概念叫做可达性
,就是那些以某种方式可访问或者说可用的值,它们被保证存储在内存中,反之不可访问则需回收。
JavaScript
垃圾回收机制的原理说白了也就是定期找出那些不再用到的内存(变量),然后释放其内存
- 标记清除算法
- 引用计数算法
标记清除算法
目前在 JavaScript引擎
里这种算法是最常用的,到目前为止的大多数浏览器的 JavaScript引擎
都在采用标记清除算法。
整个标记清除算法大致过程就像下面这样
- 垃圾收集器在运行时会给内存中的所有变量都加上一个标记,假设内存中所有对象都是垃圾,全标记为0
- 然后从各个根对象开始遍历,把不是垃圾的节点改成1
- 清理所有标记为0的垃圾,销毁并回收它们所占用的内存空间
- 最后,把所有内存中对象标记修改为0,等待下一轮垃圾回收
优点
标记清除算法的优点只有一个,那就是实现比较简单,打标记也无非打与不打两种情况,这使得一位二进制位(0和1)就可以为其标记,非常简单
缺点
- 内存碎片化,空闲内存块是不连续的,容易出现很多空闲内存块,还可能会出现分配所需内存过大的对象时找不到合适的块
- 分配速度慢,因为即便是使用
First-fit
(找到大于等于size
的块立即返回)策略,其操作仍是一个O(n)
的操作,最坏情况是每次都要遍历到最后,同时因为碎片化,大对象的分配效率会更慢
引用计数算法
它的策略是跟踪记录每个变量值被使用的次数。目前很少使用这种算法了,因为它的问题很多。
- 当声明了一个变量并且将一个引用类型赋值给该变量的时候这个值的引用次数就为 1
- 如果同一个值又被赋给另一个变量,那么引用数加 1
- 如果该变量的值被其他的值覆盖了,则引用次数减 1
- 当这个值的引用次数变为 0 的时候,说明没有变量在使用,这个值没法被访问了,回收空间,垃圾回收器会在运行的时候清理掉引用次数为 0 的值占用的内存
优点
引用计数算法的优点我们对比标记清除来看就会清晰很多,首先引用计数在引用值为 0 时,也就是在变成垃圾的那一刻就会被回收,所以它可以立即回收垃圾。
缺点
引用计数的缺点想必大家也都很明朗了,首先它需要一个计数器,而此计数器需要占很大的位置,因为我们也不知道被引用数量的上限,还有就是无法解决循环引用无法回收的问题,这也是最严重的。
详细可以看一下这篇文章
对Promise的理解
Promise时ES6新增的引用类型,内部包含着异步操作,是用来解决异步编程的一种方案。new Promise(fn)
和Promise.resolve(fn)
这两种方式都会返回一个 Promise 对象。
Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。
基本过程:
- 初始化 Promise 状态(pending)
- 立即执行 Promise 中传入的 fn 函数,将Promise 内部 resolve、reject 函数作为参数传递给 fn ,按事件机制时机处理
- 执行 then(..) 注册回调处理数组(then 方法可被同一个 Promise 调用多次)
- Promise里的关键是要保证,then方法传入的参数 onFulfilled 和 onRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。
then方法的链式调用
当一个Promise 的状态被fulfilled 之后。会执行回调函数,而回调函数返回的结果会被当作value,返回给下一个Promise(也就是then 中产生的 Promise),同时下一个Promise的状态也会被改变(执行resolve 或 reject),然后再去执行其回调,以此类推下去,形成链式调用。
真正的链式Promise是指在当前Promise达到fulfilled状态后,即开始进行下一个Promise。
new Object()和Object.create()的区别
Object.create()
MDN官方定义:Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象proto
object.create(proto, propertiesObject)接受两个参数:
proto
:传递一个现在有的对象,即新对象的原型对象(新创建的对象_proto_
属性指向现有属性)。第一个参数proto的值为null
,那么创建出来的对象是一个{}
(空对象)并且没有原型。propertiesObject
:可选,给新对象添加新属性以及描述器。如果没有指定即创建一个{}
,有原型也有继承Object.prototype上的方法。
new Object()
new
创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
- 创建一个空的对象(即{})
- 链接该对象(即设置该对象的构造函数)到另一个对象
- 将步骤1新创建的对象作为this的上下文
- 如果该函数没有返回对象,则返回this
new
会创建一个新对象,并且这个新对象继承构造函数的prototype,也就是说创建的实例的proto指向构造函数的prototypenew Object()
会创建一个实例,该实例的proto指向Object的prototype
区别
- new Object()继承内置对象Object,而Object.create则是继承指定对象
- 可以通过Object.create(null) 创建一个干净的对象,也就是没有原型,而 new Object() 创建的对象是 Object的实例,原型永远指向Object.prototype。
http常见的状态码
2XX(Success 成功状态码)
- 200 OK 表明请求已经成功. 默认情况下状态码为200的响应可以被缓存。
- 204 No Content 表示目前请求成功,但客户端不需要更新其现有页面
3XX(Redirection 重定向状态码)
- 301 Moved Permanently 永久重定向。说明请求的资源已经被移动到了由 Location 头部指定的 url 上,是固定的不会再改变。搜索引擎会根据该响应修正。
- 302 Found 临时重定向。重定向状态码表明请求的资源被暂时的移动到了由 Location 头部指定的 URL 上。浏览器会重定向到这个URL,但是搜索引擎不会对该资源的链接进行更新
- 304 Not Modified 说明无需再次传输请求的内容,也就是说可以使用缓存的内容。
4XX(Client Error 客户端错误状态码)
- 401 Unauthorized 说明由于缺乏目标资源要求的身份验证凭证,发送的请求未得到满足。
- 403 Forbidden 指的是服务器端有能力处理该请求,但是拒绝授权访问。进入该状态后,不能再继续进行验证。该访问是永久禁止的,并且与应用逻辑密切相关(例如不正确的密码)
- 404 Not Found 说明服务器端无法找到所请求的资源。返回该响应的链接通常称为坏链(broken link)或死链(dead link),它们会导向链接出错处理
- 405 Method Not Allowed 表明服务器禁止了使用当前 HTTP 方法的请求。需要注意的是,GET 与 HEAD 两个方法不得被禁止,当然也不得返回状态码 405。
5XX(Server Error 服务器错误状态码)
- 500 Internal Server Error 表示所请求的服务器遇到意外的情况并阻止其执行请求。
- 502 Bad Gateway 表示作为网关或代理角色的服务器,从上游服务器(如tomcat、php-fpm)中接收到的响应是无效的。
- 503 Service Unavailable 表示服务器尚未处于可以接受请求的状态。通常造成这种情况的原因是由于服务器停机维护或者已超载。
Tcp三次握手四次挥手
三次握手的作用是确认客户端和服务端的接受与发送能力是否正常
- 第一次握手:客户端发送网络包,服务端收到了。这样的服务端就能得出结论:客户端的发送能力、服务端的接受能力是正常的。
- 第二次握手:服务端发包、客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
- 客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常
四次挥手
刚开始客户端和服务端都处于连接状态,然后某一方发起关闭请求,假如客户端先发起关闭请求
- 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态。
- 第二次握手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT状态。
- 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
- 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态。
更多详细可以看一下这篇文章
HTTP和HTTPS的区别
HTTP
协议是用于Web浏览器和网站服务器之间传递信息,HTTP
协议以明文方式发送内容,不提供任何方式的数据加密,所以,HTTP
协议不适合传输敏感信息。HTTPS
就是解决这一缺陷的,保证数据传输安全。HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
HTTPS和HTTP的区别主要如下:
- https协议需要到CA证书,一般免费证书表较少,因而需要一定的费用。
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是433。
- http的连接很简单,是无状态的;HTTPS协议是由SSL + HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
更多详细可以看一下这篇文章
浏览器的缓存机制,协商缓存
强缓存
浏览器中的缓存作用分为两种情况,一种是需要发送HTTP
请求,一种是不需要发送。
首先检查强缓存,这个阶段不需要发送HTTP请求
通过请求头中相应的字段来进行检查,在HTTP/1.0
和HTTP/1.1
当中,这个字段是不一样的。在HTTP/1.0
时期,使用的是Expires,而HTTP/1.1
使用的是Cache-Control。
Expires
Expires
即过期时间。存在于服务端返回的响应头中,告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据,无需再次请求。比如下面这样:
Expires: Wed, 22 Nov 2019 08:41:00 GMT
表示资源在2019年11月22号8点41分
过期,过期了就得向服务端发请求。
这里会有一个问题,那就是服务器的时间和浏览器的时间可能并不一致,那服务器返回的这个过期时间可能就是不准确的。因此这种方式很快在后来的HTTP1.1版本中被抛弃了。
Cache-Control
在HTTP1.1中,采用了一个非常关键的字段:Cache-Control
。这个字段也是存在于
它和Expires
本质的不同在于它并没有采用具体的过期时间点
这个方式,而是采用过期时长来控制缓存,对应的字段是max-age。比如这个例子:
Cache-Control:max-age=3600
代表这个响应返回后在 3600 秒,也就是一个小时之内可以直接使用缓存。
Cache-Control还有其他的属性,完成更多场景的缓存判断
-
public:客户端和代理服务器都可以缓存。因为一个请求可能要经过不同的
代理服务器
最后才到达目标服务器,那么结果就是不仅仅浏览器可以缓存数据,中间的任何代理节点都可以进行缓存。 -
private:这种情况就是只有浏览器能缓存了,中间的代理服务器不能缓存。
-
no-cache:跳过当前的强缓存,发送HTTP请求,即直接进入协商缓存阶段。
-
no-store:非常粗暴,不进行任何形式的缓存。
-
s-maxage:这和
max-age
长得比较像,但是区别在于s-maxage是针对代理服务器的缓存时间。
值得注意的是,当Expires和Cache-Control同时存在的时候,Cache-Control会优先考虑。
当然,还存在一种情况,当资源缓存时间超时了,也就是强缓存
失效了,接下来怎么办?没错,这样就进入到第二级屏障——协商缓存了。
协商缓存
强缓存失效之后,浏览器在请求头中携带相应的缓存tag来向服务器发请求,由服务器根据这个tag,决定是否使用缓存,这就是协商缓存。
具体来说,这样的缓存tag分为两种: Last-Modified 和 ETag。这两者各有优劣。
Last-Modified
即最后修改时间。在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。
浏览器接收到后,如果再次请求,会在请求头中携带If-Modified-Since
字段,这个字段的值也就是服务器传来的最后修改时间。
服务器拿到请求头中的If-Modified-Since
的字段后,其实会和这个服务器中该资源的最后修改时间
对比:
- 如果请求头中的这个值小于最后修改时间,说明是时候更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。
- 否则返回304,告诉浏览器直接用缓存。
ETag
ETag
是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。服务器通过响应头
把这个值给浏览器。
浏览器接收到ETag
的值,会在下次请求时,将这个值作为If-None-Match这个字段的内容,并放到请求头中,然后发给服务器。
服务器接收到If-None-Match后,会跟服务器上该资源的ETag进行比对:
- 如果两者不一样,说明要更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。
- 否则返回304,告诉浏览器直接用缓存。
可以看一下三元大大的文章,我也是看的三元大大文章学习,强烈推荐。
浏览器的同源策略,解决跨域问题的方法
同源策略(Same origin policy)是一种约定,可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。所谓的同源是一种安全机制,为了预防某些恶意行为(例如 Cookie 窃取等),浏览器限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。而满足同源要具备三方面:协议相同、域名相同、端口相同。
同源策略对于用户的信息安全是必不可少的,但是实现合理的跨域请求也是很重要的,于是 W3C 就定快乐一个叫CORS(Cross-Origin Resource Sharing)的草案,也就是跨域资源共享。其基本思想就是使用自定义的 HTTP 头部让浏览器和服务器进行沟通,从而决定请求是应该成功或是失败。
CORS(跨域资源共享)
CORS 需要浏览器和服务器同时支持,目前,所有的浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS 通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求要满足两个条件:
- 请求方法为 HEAD|GET|POST 三种之一
- HTTP的头信息不超出Accept|Accept-Language|Content-Language|Last-Event-ID|Content-Type这几种字段。
凡是不同时满足上面两个条件,就属于非简单请求。浏览器对这两种请求的处理,是不一样的。
图像 Ping
该跨域技术主要是利用<img>
标签设置src
属性(请求地址通常都带有查询字符串),然后监听该<img>
的onload
或onerror
事件来判断请求是否成功。响应的内容通常是一张 1 像素的图片或者204
响应。
图片 Ping 有两个缺点:
- 因为是通过
<img>
标签实现,所以只支持GET
请求。 - 无法访问服务器响应脚本,只能用于在浏览器与服务器之间进行单向通行。
由于以上特点,图片 Ping 方法常用于跟踪用户点击页面或动态广告的曝光次数。
JSONP 跨域
由于script
标签不受浏览器同源策略的影响,允许跨域引用资源。因此可以通过动态创建 script 标签,然后利用 src 属性进行跨域,这也就是 JSONP 跨域的基本原理。
优点
- 使用简便,没有兼容性问题,目前最流行的一种跨域方法。
缺点
- 只支持 GET 请求。
- 由于是从其它域中加载代码执行,因此如果其他域不安全,很可能会在响应中夹带一些恶意代码。
- 要确定 JSONP 请求是否失败并不容易。虽然 HTML5 给 script 标签新增了一个 onerror 事件处理程序,但是存在兼容性问题。
其他的跨域方法
- HTML5 的 postMessage
- WebSocket(当然协议就不一样了)
- document.domain(iframe)
- location.hash(iframe)
- window.name
- nginx 反向代理
对react生命周期的理解
componentDidMount()
componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。
componentDidUpdate()
componentDidUpdate() 会在组件个更新后会被立即调用。首次渲染不会执行此方法。
componentWillUnmount()
componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求。
不常用的生命周期方法
shouldComponentUpdate()
根据 shouldComponentUpdate()的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染。大部分情况下,你应该遵循默认行为。
getDerivedStateFromProps()
getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null
则不更新任何内容。
getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()。
对react Hooks的理解
没有提出Hook 之前存在的问题
-
在组件之间复用状态逻辑很难
- Hook 使你在无需修改组件结构的情况下复用状态逻辑
-
复杂组件变得难以理解
- Hook 将组件中相互关联的部分拆成更小的函数(比如设置订阅或请求数据)
-
难以理解的 class
- Hook 使你在非 class 的情况下可以使用更多的React 特性
Hook 使用规则
Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
- 只能在函数最外层调用Hook。不要在循环、条件判断或者子函数中调用。
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
没有hooks之前,react团队提出了那些解决方法
官方文档中是这样说的,React 没有提供将可复用性行为“附加”到组件的途径(例如,把组件连接到store)。在Hook 出现之前解决此类问题,是通过**reader props
** 和 高阶组件
.但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。
Hook 的出现,你可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。
Mixin 函数
Mixin 是复用类代码的一种途径,复用的类可以在不同层级,之间可以不存在继承关系,本质上可以理解为对类的功能的扩展,可以为类添加功能。
在一些大型项目中经常会存在对多个组件需要使用相同功能的情况,如果在每个组件中都重复的加入相同的代码,那么代码的维护性将变的非常差,Mixins的出现就是为了解决这个问题。可以将通用共享的方法包装成Mixins方法,然后注入各个组件实现。
列举一些hooks函数(useLayoutEffect和useEffect区别 官方文档)
useLayoutEffect
和 useEffect
主要的区别在于,useLayoutEffect会在所有的DOM 变更之后同步调用effect,可以使用它来读取 DOM 布局并同步触发重渲染,在浏览器执行绘制之前,useLayoutEffect内部的更新计划将被同步。
总结
最后说一下个人的收获吧,整理完面试问题之后,感觉其实问题并没有很难,主要是自己在面试前对八股文深度、广度准备的都不充分。同时也收获了自己在知识点上的不足之处以及回答面试官问题更好的去表达自己的知识储备,目前持续学习中,将继续尝试更多的面试,希望有经验的掘友们在评论区分享经验!!!