前端面试题(五)

目录

1.浏览器垃圾回收机制

2.CSRF攻击是什么?

3.三栏布局的实现方案

4.Vue3.0实现数据双向绑定的方法

5.Diff算法

6.vue 的 keep-alive


1.说一下浏览器垃圾回收机制?

浏览器垃圾回收机制根据数据的存储方式分为栈垃圾回收和堆垃圾回收。

栈垃圾回收的方式非常简便,当一个函数执行结束之后,JavaScript引擎会通过向下移动 ESP来销毁该函数保存在栈中的执行上下文,遵循先进后出的原则。

堆垃圾回收,当函数直接结束,栈空间处理完成了,但是堆空间的数据虽然没有被引用,但是还是存储在堆空间中,需要垃圾回收器将堆空间中的垃圾数据回收。为了使垃圾回收达到更好的效果,根据对象的生命周期不同,使用不同的垃圾回收的算法。在V8中会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的对象。新生区中使用Scavenge算法,老生区中使用标记-清除算法和标记-整理算法。

Scavenge算法:1.标记:对对象区域中的垃圾进行标记。2.清除垃圾数据和整理碎片化内存:副垃圾回收器会把这些存活的对象复制到空闲区域中,并且有序的排列起来,复制后空闲区域就没有内存碎片了。3.角色翻转:完成复制后,对象区域与空闲区域进行角色翻转,也就是原来的对象区域变成空闲区域,原来的空闲区域变成了对象区域,这样就完成了垃圾对象的回收操作,同时这种角色翻转的操作还能让新生代中的这两块区域无限重复使用下去。

标记-清除算法:1.标记:标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。2.清除:将垃圾数据进行清除。3.产生内存碎片:对一块内存多次执行标记-清除算法后,会产生大量不连续的内存碎片。而碎片过多会导致大对象无法分配到足够的连续内存。

标记-整理算法:1.标记:和标记-清除的标记过程一样,从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素标记为活动对象。2.整理:让所有存活的对象都向内存的一端移动。3.清除:清理掉端边界以外的内存V8是使用副垃圾回收器和主垃圾回收器处理垃圾回收的,不过由于JavaScript是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的JavaScript脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。我们把这种行为叫做全停顿。为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和JavaScript应用逻辑交替进行,直到标记阶段完成,我们把这个算法称为增量标记(Incremental Marking)算法。

2.CSRF攻击是什么?

CSRF跨站点请求伪造(Cross Site Request Forgery),就是攻击者盗用了用户的身份,以用户的身份发送恶意请求,但是对服务器来说这个请求是合理的,这样就完成了攻击者的目标。CSRF攻击的过程原理是:用户打开浏览器,访问目标网站A,输入用户名和密码请求登录;用户信息在通过认证后,网站A产生一个cookie信息返回给浏览器,这个时候用户可以正常发送请求到网站A;用户在没有退出网站A之前在同一个浏览器打开了另一个新网站B。新网站B收到用户请求之后返回一些攻击代码,并发出一个请求要求访问返回cookie的网站A;浏览器收到这些攻击性代码之后根据新网站B的请求在用户不知道的情况下以用户的权限操作了cookie并向网站A服务器发起了合法的请求。

预防CSRF攻击主要有以下策略:1.使用验证码,在表单中添加一个随机的数字或者字母验证码,强制要求用户和应用进行直接的交互。2.HTTP中Referer字段,检查是不是从正确的域名访问过来,它记录了HTTP请求的来源地址。3.使用token验证,在HTTP请求头中添加token字段,并且在服务器端建立一个拦截器验证这个token,如果token不对,就拒绝这个请求。

验证HTTP Referer字段的好处就是实施起来特别简单,普通的网站开发不需要特别担心CSRF漏洞,只需要在最后面设置一个拦截器来验证referer的值就可以了,不需要改变已有的代码逻辑,非常便捷。但是这个方法也不是万无一失的,虽然referer是浏览器提供的,但是不同的浏览器可能在referer的实现上或多或少有自身的漏洞,所以使用referer的安全保证是通过浏览器实现的。使用token验证的方法要比referer更安全一些,需要把token放在一个HTTP自定义的请求头部中,解决了使用get或者post传参的不便性。

3.说一说三栏布局的实现方案

三栏布局,要求左右两边盒子宽度固定,中间盒子宽度自适应,盒子的高度都是随内容撑高的,一般都是中间盒子内容较多,为了保证页面渲染快,在写结构的时候,需要把中间盒子放在左右盒子的前面。 实现三栏布局的方法通常是圣杯布局和双飞翼布局。

圣杯布局的实现方案:三个元素放在同一个父级元素中,代表中间盒子的元素放在最前面,父级盒子设置左右`padding`,三个盒子全部浮动,设置中间盒子宽度100%,左右盒子设置固定宽度,设置左边盒子左边距-100%同时相对自身定位,右边平移自身宽度,右边盒子设置右边距-自身宽度,最后设置父级盒子清除浮动,否则父级盒子的高度无法被撑开。优点:不需要添加dom节点;缺点:正常情况下是没有问题的,但是特殊情况下当middle部分的宽小于left部分时就会发生布局混乱。

双飞翼布局的实现方案:三个盒子对应三个元素,其中中间盒子套了两层,中间盒子内部盒子设置`margin`,三个盒子全部浮动,设置中间盒子宽度100%,左右盒子设置固定宽度,设置左边盒子左边距-100%,右边盒子设置右边距-自身宽度,最后设置父级盒子清除浮动,否则父级盒子的高度无法被撑开。优点:不会像圣杯布局那样变形,CSS样式代码更简洁。缺点:多加了一层dom节点。

4.说一说Vue3.0实现数据双向绑定的方法 ?

Vue3.0通过Proxy实现的数据双向绑定,Proxy是ES6中的新增特性,在目标对象之前设置了一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,提供了一种可以对外界的访问进行过滤和改写的机制。用法:ES6 原生提供Proxy构造函数,用来生成 Proxy 实例。

var proxy = new Proxy(target, handler) target: 是用Proxy包装的被代理对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。handler: 是一个对象,其声明了代理target的一些操作,其属性是当执行一个操作时定义代理的行为的函数。

在Vue中,`Object.defineProperty`无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。目前只针对以上方法做了hack处理,所以数组属性是检测不到的,有局限性。Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue里,是通过递归以及遍历data对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象,不管是对操作性还是性能都会有一个很大的提升。Proxy的两个优点:可以劫持整个对象,并返回一个新对象,有13种劫持。

5.说一下Diff算法?

Diff算法主要就是在虚拟DOM树发生变化后,生成DOM树更新补丁的方式,对比新旧两株虚拟DOM树的变更差异,将更新补丁作用于真实DOM,以最小成本完成视图更新。采用同级比较,深度优先原则。

Diff算法比较过程第一步:patch函数中对新老节点进行比较,如果新节点不存在就销毁老节点;如果老节点不存在,直接创建新的节点。当两个节点是相同节点的时候,进入 patctVnode的过程,比较两个节点的内部。第二步:patchVnode函数比较两个虚拟节点内部,如果两个虚拟节点完全相同,返回;当前vnode的children不是textNode,再分成三种情况:1.有新children,没有旧children,创建新的。2.没有新children,有旧children,删除旧的。3.新children、旧children都有,执行`updateChildren`比较children的差异,这里就是diff算法的核心,当前vnode的children是textNode,直接更新text。第三步:updateChildren函数子节点进行比较:1.头头比较。若相似,旧头新头指针后移(即 `oldStartIdx++` && `newStartIdx++`),真实dom不变,进入下一次循环;不相似,进入第二步。2.尾尾比较。若相似,旧尾新尾指针前移(即 `oldEndIdx--` && `newEndIdx--`),真实dom不变,进入下一次循环;不相似,进入第三步。3.头尾比较。若相似,旧头指针后移,新尾指针前移(即 `oldStartIdx++` && `newEndIdx--`),未确认dom序列中的头移到尾,进入下一次循环;不相似,进入第四步。4.尾头比较。若相似,旧尾指针前移,新头指针后移(即 `oldEndIdx--` && `newStartIdx++`),未确认dom序列中的尾移到头,进入下一次循环;不相似,进入第五步。5.若节点有key且在旧子节点数组中找到sameVnode(tag和key都一致),则将其dom移动到当前真实dom序列的头部,新头指针后移(即`newStartIdx++`);否则,vnode对应的dom(`vnode[newStartIdx].elm`)插入当前真实dom序列的头部,新头指针后移(即 `newStartIdx++`)。但结束循环后,有两种情况需要考虑:1.新的子节点数组(newCh)被遍历完(`newStartIdx > newEndIdx`)。那就需要把多余的旧dom(`oldStartIdx -> oldEndIdx`)都删除,上述例子中就是`c,d`;旧的子节点数组(oldCh)被遍历完(`oldStartIdx > oldEndIdx`)。那就需要把多余的新dom(`newStartIdx -> newEndIdx`)都添加。

6.说一说 vue 的 keep-alive ?

`<keep-alive>`作用:缓存组件,提升性能,避免重复加载一些不需要经常变动且内容较多的组件。使用方法:使用`<keep-alive>`标签对需要缓存的组件进行包裹,默认情况下被`<keep-alive>`标签包裹的组件都会进行缓存,区分被包裹的组件是否缓存有两种方法,第一种是给keep-alive添加属性,组件名称指的是具体组件添加的name,不是路由里面的name。有三个属性:include:只有匹配的组件才会被保存。exclude:只有匹配的组件才不会被保存。max:最多能保存的组件数。第二种也是最常用的一种是,和路由配合使用:在路由中添加meta属性。 使用keep-alive导致组件不重新加载,也就不会重新执行生命周期的函数,如果要解决这个问题,就需要两个属性进入时触发:activited退出时触发:deactivated。`<keep-alive>`适用的场景:首页展示固定数据的组件,比如banner九宫格。

(来源:牛客网)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值