2020年前端面试题集锦(下)

146 阅读19分钟

优化页面渲染 CSS 放前面,JS 放后面

懒加载(图片懒加载、下拉加载更多)

减少DOM 查询,对 DOM 查询做缓存

减少DOM 操作,多个操作尽量合并在一起执行( DocumentFragment )

事件节流

尽早执行操作(DOMContentLoaded)

window.addEventListener('load', function () {
    // 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function () {
    // DOM 渲染完即可执行,此时图片、视频还可能没有加载完
})

使用 SSR 后端渲染,数据直接输出到 HTML 中,减少浏览器使用 JS 模板渲染页面 HTML 的时间

面试题:什么是xss攻击

XSS全称是 Cross Site Scripting(即跨站脚本),为了和 CSS 区分,故叫它XSS 。XSS 攻击是指浏览器中执行恶意脚本(无论是跨域还是同域),从而拿到用户的信息并进行操作。

这些操作一般可以完成下面这些事情:

Cookie

通常情况,XSS 攻击的实现有三种方式—— 存储型 、 反射型 和 文档型 。原理都比较简单,我们来了解下 存储型,顾名思义就是将恶意脚本存储了起来,确实,存储型的 XSS 将脚本存储到了服务端的数据库,然后在客户端执行这些脚本,从而达到攻击的效果。

常见的场景是留言评论区提交一段脚本代码,如果前后端没有做好转义的工作,那评论内容存到了数据库,在页面渲染过程中直接执行 , 相当于执行一段未知逻辑的 JS 代码,是非常恐怖的。这就是存储型的 XSS 攻击。

反射型XSS指的是恶意脚本作为 网络请求的一部分 。

比如我输入:

http://yideng.com?q=<script>alert("你完蛋了")</script>
复制代码

这杨,在服务器端会拿到 q 参数,然后将内容返回给浏览器端,浏览器将这些内容作为HTML的一部分解析,发现是一个脚本,直接执行,这样就被攻击了。

之所以叫它 反射型 , 是因为恶意脚本是通过作为网络请求的参数,经过服务器,然后再反射到HTML文档中,执行解析。和 存储型 不一样的是,服务器并不会存储这些恶意脚本。

文档型的 XSS 攻击并不会经过服务端,而是作为中间人的角色,在数据传输过程劫持到网络数据包,然后 修改里面的 html 文档 ! 这样的劫持方式包括 WIFI路由器劫持或者本地恶意软件等。

防范措施 明白了三种 XSS 攻击的原理,我们能发现一个共同点: 都是让恶意脚本直接能在浏览器中执行。

那么要防范它,就是要避免这些脚本代码的执行。

为了完成这一点,必须做到

千万不要相信任何用户的输入!

无论是在前端和服务端,都要对用户的输入进行 转码 或者 过滤 。

如:

<script>alert('你完蛋了')</script>
复制代码

转码后变为:

<script>alert( 你完蛋了 )</script>
复制代码

这样的代码在 html 解析的过程中是无法执行的。

当然也可以利用关键词过滤的方式,将 script 标签给删除。那么现在的内容只剩下:

利用 HttpOnly 很多 XSS 攻击脚本都是用来窃取Cookie, 而设置 Cookie 的 HttpOnly 属性后,JavaScript 便无法读取 Cookie 的值。这样也能很好的防范 XSS 攻击。

面试题:说一下CSRF攻击

什么是CSRF攻击? CSRF(Cross-site request forgery), 即跨站请求伪造,指的是黑客诱导用户点击链接,打开黑客的网站,然后黑客利用用户 目前的登录状态 发起跨站请求。

举个例子, 你在某个论坛点击了黑客精心挑选的小姐姐图片,你点击后,进入了一个新的页面。

那么恭喜你,被攻击了:)

你可能会比较好奇,怎么突然就被攻击了呢?接下来我们就来拆解一下当你点击了链接之后,黑客在背后做了哪些事情。

可能会做三样事情。列举如下:

  1. 自动发 GET 请求 黑客网页里面可能有一段这样的代码:
<img src="https://xxx.com/info?user=hhh&count=100">

进入页面后自动发送 get 请求,值得注意的是,这个请求会自动带上关于 xxx.com 的 cookie 信息(这里是假定你已经在 xxx.com 中登录过)。

假如服务器端没有相应的验证机制,它可能认为发请求的是一个正常的用户,因为携带了相应的 cookie,然后进行相应的各种操作,可以是转账汇款以及其他的恶意操作。

  1. 自动发 POST 请求 黑客可能自己填了一个表单,写了一段自动提交的脚本。
<form id='hacker-form' action="https://xxx.com/info" method="POST">
  <input type="hidden" name="user" value="hhh" />
  <input type="hidden" name="count" value="100" />
</form>
<script>document.getElementById('hacker-form').submit();</script>
复制代码

同样也会携带相应的用户 cookie 信息,让服务器误以为是一个正常的用户在操作,让各种恶意的操作变为可能。

  1. 诱导点击发送 GET 请求 在黑客的网站上,可能会放上一个链接,驱使你来点击:
<a href="https://xxx/info?user=hhh&count=100" taget="_blank">点击进入修仙世界</a>

点击后,自动发送 get 请求,接下来和 自动发 GET 请求部分同理。

这就是CSRF攻击的原理。和XSS攻击对比,CSRF 攻击并不需要将恶意代码注入用户当前页面的html文档中,而是跳转到新的页面,利用服务器的 验证漏洞 和 用户之前的登录状态 来模拟用户进行操作。

防范措施

  1. 利用Cookie的SameSite属性 CSRF攻击 中重要的一环就是自动发送目标站点下的Cookie,然后就是这一份 Cookie 模拟了用户的身份。因此在 Cookie上面下文章是防范的不二之选。

恰好,在 Cookie 当中有一个关键的字段,可以对请求中 Cookie 的携带作一些限制,这个字段就是SameSiteSameSite可以设置为三个值,StrictLaxNone 。 a.在 Strict 模式下,浏览器完全禁止第三方请求携带Cookie。比如请求 sanyuan.com 网站只能在sanyuan.com域名当中请求才能携带 Cookie,在其他网站请求都不能。 b.在 Lax模式,就宽松一点了,但是只能在get 方法提交表单 况或者a 标签发送 get请求 的情况下可以携带 Cookie,其他情况均不能。 c.在None模式下,也就是默认模式,请求会自动携带上 Cookie。 2. 验证来源站点 这就需要要用到请求头中的两个字段: Origin 和 Referer 。

其中, Origin 只包含域名信息,而 Referer 包含了 具体 的 URL 路径。

当然,这两者都是可以伪造的,通过 Ajax 中自定义请求头即可,安全性略差。

  1. CSRF Token 它的原理是怎样的呢?

首先,浏览器向服务器发送请求时,服务器生成一个字符串,将其植入到返回的页面中。

然后浏览器如果要发送请求,就必须带上这个字符串,然后服务器来验证是否合法,如果不合则不予响应。这个字符串也就是 CSRF Token,通常第三方站点无法拿到这个 token, 因此也就是被服务器给拒绝。 防范措施: 利用 Cookie 的 SameSite 属性验证来源站点CSRF Token

/*
	给定一个只包括 '('')''{''}''['']' 的字符串,判断字符串是否有效
  有效字符串需满⾜:
 	 	1. 左括号必须⽤相同类型的右括号闭合。
  	2. 左括号必须以正确的顺序闭合。
  注意空字符串可被认为是有效字符串。
  示例1:
  	输⼊: "()"
  	输出: true
  示例2:
  	输⼊: "()[]{}"
  	输出: true
  示例 3:
  	输⼊: "(]"
  	输出: false
  示例 4:
  	输⼊: "([)]"
  	输出: false
  示例 5:
  	输⼊: "{[]}"
  	输出: true
*/

// 思路
出栈、入栈的思想
1)首先,我们通过上边的例子可以分析出什么样子括号匹配是复合物条件的,两种情况。
	①第一种(非嵌套情况):{} [] ;
	②第二种(嵌套情况):{ [ ( ) ] } 。
除去这两种情况都不是符合条件的。
2)然后,我们将这些括号自右向左看做栈结构,右侧是栈顶,左侧是栈尾。
3)如果编译器中的括号是左括号,我们就入栈(左括号不用检查匹配);如果是右括号,就取出栈顶元素检查是否匹配。
4)如果匹配,就出栈。否则,就返回 false;

// 代码实现
var isValid = function(s){
  let stack = [];
  var obj = {
     "[": "]",
     "{": "}",
     "(": ")",
  };
  // 取出字符串中的括号
  for (var i = 0; i < s.length;i++){
    if(s[i] === "[" || s[i] === "{" || s[i] === "("){
      // 如果是左括号,就进栈
      stack.push(s[i]);
    }else{
   		var key = stack.pop();
      // 如果栈顶元素不相同,就返回false
      if(obj[key] !== s[i]){
        return false;
      }
    }
  }
  return stack.length ===  0
}

因此这就需要一个系统的训练,进行专项突破,按分类进行学习

比如按照经典的数据结构进行分类,刷题

常见的数据结构 栈、队列、链表 集合、字典、散列表 常见算法 递归 排序 枚举 算法复杂度分析 算法思维 分治 贪心 动态规划 高级数据结构 树、图 深度优先和广度优先搜索

**框架相关

Vue响应式原理介绍一下,Object.defineProperty 的缺陷,模板编译的过程,将模板解析为 AST,优化AST,将AST转换为render函数 等等。

HOC是什么?相比mixins有什么优点**

很多人看到高阶组件(HOC)这个概念就被吓到了,认为这东西很难,其实这东西概念真的很简单,我们先来看一个例子。

function add(a, b) {
    return a + b
}
//现在如果我想给这个 add 函数添加一个输出结果的功能,那么你可能会考虑我直接使用 console.log 不就实现了么。说的没错,但是如果我们想做的更加优雅并且容易复用和扩展,我们可以这样去做:
function withLog (fn) {
    function wrapper(a, b) {
        const result = fn(a, b)
        console.log(result)
        return result
    }
    return wrapper
}
const withLogAdd = withLog(add)
withLogAdd(1, 2)
//其实这个做法在函数式编程里称之为高阶函数,大家都知道 React 的思想中是存在函数式编程的,高阶组件和高阶函数就是同一个东西。我们实现一个函数,传入一个组件,然后在函数内部再实现一个函数去扩展传入的组件,最后返回一个新的组件,这就是高阶组件的概念,作用就是为了更好的复用代码。
// 其实 HOC 和 Vue 中的 mixins 作用是一致的,并且在早期 React 也是使用 mixins 的方式。但是在使用 class 的方式创建组件以后,mixins 的方式就不能使用了,并且其实 mixins 也是存在一些问题的,比如:
隐含了一些依赖,比如我在组件中写了某个 state 并且在 mixin 中使用了,就这存在了一个依赖关系。万一下次别人要移除它,就得去 mixin 中查找依赖
多个 mixin 中可能存在相同命名的函数,同时代码组件中也不能出现相同命名的函数,否则就是重写了,其实我一直觉得命名真的是一件麻烦事。。
雪球效应,虽然我一个组件还是使用着同一个 mixin,但是一个 mixin 会被多个组件使用,可能会存在需求使得 mixin 修改原本的函数或者新增更多的函数,这样可能就会产生一个维护成本

HOC 解决了这些问题,并且它们达成的效果也是一致的,同时也更加的政治正确(毕竟更加函数式了)。

聊到具体的项目 一般在面试中,比如一面、二面基本会问到具体做过的项目,然后就是追问项目的细节。一般可能会出现这些问题

发现你简历的一个项目,直接让你介绍下这个项目 让你回忆下你做过的项目中,最值得分享(最大型/最困难/最能体现技术能力/最难忘)的 如果让你设计 xx 系统/项目,你会怎么着手干 这类跟项目相关的综合性问题,既能体现候选人的技术水平、业务水平和架构能力,也能够辨别候选人是不是真的做过项目,还能够发现候选人的一些软技能。

如何进行应对 首先简历中的项目,肯定是你精挑细选的,不能随便选几个,要做好充分的准备,简历中的项目,既要体现技术难度,又要想好细节。一般介绍一个项目可以按几步来 介绍项目背景

这个项目为什么做,当初大的环境背景是什么?还是为了解决一个什么问题而设立的项目?背景是很重要的,如果不了解背景,一上来就听一个结论性的项目,面试官可能对于项目的技术选型、技术难度会有理解偏差,甚至怀疑是否真的有过这样的项目。 比如一上来就说:我们的项目采用了「backbone」来做框架,然后。。。而「backbone」已经是三四年前比较新鲜的技术,现在会有更好的选择方案,如果不介绍项目的时间背景,面试官肯定一脸懵逼。 承担角色

项目涉及的人员角色有哪些,自己在其中扮演的角色是什么? 这里候选往往人会自己给自己挖坑,比如把自己在项目中起到的作用夸大等。一般来说,面试官细节追问的时候,如果候选人能够把细节或者技术方案等讲明白、讲清楚,不管他是真的做过还是跟别人做过,或者自己认真思考过,都能体现候选人的技术水平和技术视野。前提还是在你能够兜得住的可控范围之内做适当的「美化」。 最终的结果和收益

项目介绍过程中,应该介绍项目最终的结果和收益,比如项目最后经过多久的开发上线了,上线后的数据是怎样的,是否达到预期,还是带来了新的问题,遇见了问题自己后续又是怎样补救的。 非常重要的:项目的总结和反思

有总结和反思,才会有进步。 项目做完了往往会有一些心得和体会,这时候应该跟面试官说出来。在梳理项目的总结和反思时,可以按照下面的列表来梳理:

收获有哪些? 是否有做得不足的地方,怎么改进? 是否具有可迁移性?

比如,之前详细介绍了某个项目,这个项目当时看来没有什么问题,但是现在有更好的解决方案了,候选人就应该在这里提出来:现在看来,这个项目还有 xx 的问题,我可以通过 xx 的方式来解决。 再比如:做这个项目的时候,你做得比较出彩的地方,可以迁移到其他项目中直接使用,小到代码片段,大到解决方案,总会有你值得总结和梳理的地方。

介绍完项目总结这部分,也可以引导面试官往自己擅长的领域思考。比如上面提到项目中的问题,可以往你擅长的方面引导,即使面试官没有问到,你也介绍到了。

按照上面的四段体介绍项目,会让面试官感觉候选人有清晰的思路,对整个项目也有理解和想法,还能够总结反思项目的收益和问题,可谓「一箭三雕」。 没有做过大型项目怎么办 对于刚刚找工作的应届生,或者面试官让你进行一个大型项目的设计,候选人可能没有类似的经验。这时候不要用「我不会、没做过」一句话就带过。

如果是实在没有项目可以说,那么可以提自己日常做的练手项目,或者看到一个解决方案的文章/书,提到的某个项目,抒发下自己的想法。

如果是对于面试官提出来需要你设计的项目/系统,可以按照下面几步思考:

有没有遇见过类似的项目

有没有读过类似解决方案的文章

项目能不能拆解,拆解过程中能不能发现自己做过的项目可以用

项目解决的问题是什么,这类问题有没有更好的解决方案

总之,切记不要一句「不知道、没做过」就放弃,每一次提问都是自己表现的机会。

项目细节和技术点的追问 介绍项目的过程中,面试官可能会追问技术细节,所以我们在准备面试的时候,应该尽量把技术细节梳理清楚,技术细节包括:

技术选型方案:当时做技术选型所面临的状况

技术解决方案:最终确定某种技术方案的原因,比如:选择用 Vue 而没有用 React 是为什么? 项目数据和收益

项目中最难的地方

遇见的坑:如使用某种框架遇见哪些坑

一般来说,做技术选型的时候需要考虑下面几个因素 :

时代:现在比较火的技术是什么,为什么火起来,解决了什么问题,能否用到我的项目中?

团队:个人或者团队对某种技术的熟悉程度是怎样的,学习成本又是怎样的?

业务需求:需求是怎样的,能否套用现在的成熟解决方案/库来快速解决?

维护成本:一个解决方案的是否再能够 cover 住的范围之内?

在项目中遇见的数据和收益应该做好跟踪,保证数据的真实性和可信性。另外,遇见的坑可能是面试官问得比较多的,尤其现在比较火的一些技术(Vue、React、webpack),一般团队都在使用,所以一定要提前准备下

软技能问题回答 韧性:抗压能力,在一定项目压力下能够迎难而上,比如勇于主动承担和解决技术难题

责任心:对于自己做过的项目,能够出现 bug 之类主动解决

持续学习能力:IT 行业是个需要不断充电的行业,尤其 Web 前端这些年一直在巨变,所以持续学习能力很重要

团队合作能力:做项目不能个人英雄主义,应该融入团队,跟团队一起打仗

交流沟通能力:经常会遇见沟通需求和交互设计的工作,应该乐于沟通分享

回想下你遇见过最难打交道的同事,你是如何跟他沟通的

一般来说,工作中总会遇见一两个自己不喜欢的人,这种情况应该尽量避免冲突,从自己做起慢慢让对方感觉到自己的合作精神。

所以,遇见难打交道的同事,不要急于上报领导,应该自己主动多做一些事情,比如规划好工作安排,让他选择自己做的事情,有了结论记得发邮件确认下来,这样你们的领导和其他成员都会了解到工作的安排,在鞭笞对方的同时,也做到了职责明确。在项目当中,多主动检查项目进展,提前发现逾期的问题。

重点是突出:自己主动沟通解决问题的意识,而不是遇见问题就找领导。

当你被分配一个几乎不可能完成的任务时,你会怎么做 这种情况下,一般通过下面方式来解决:

自己先查找资料,寻找解决方案,评估自己需要怎样的资源来完成,需要多长时间

能不能借助周围同事来解决问题

拿着分析结果跟上级反馈,寻求帮助或者资源

突出的软技能:分析和解决问题,沟通寻求帮助。

业余时间都做什么?除了写码之外还有什么爱好 这类问题也是面试官的高频问题,「一个人的业余时间决定了他的未来」,如果回答周末都在追剧打游戏之类的,未免显得太不上进。

一般来说,推荐下面的回答:

周末一般会有三种状态:

和朋友一起去做做运动,也会聚会聊天,探讨下新技术之类的;

也会看一些书籍充充电,比如我最近看的 xx,有什么的想法;

有时候会闷在家用最近比较火的技术做个小项目或者实现个小功能之类的。

这样的回答,既能表现自己阳光善于社交沟通的一面,又能表现自己的上进心。

面试注意事项

提问环节

面试是一个双向选择的事情,所以面试后一般会有提问环节。在提问环节,候选人最好不要什么都不问,更不要只问薪水待遇、是否加班之类的问题。

其实这个时候可以反问面试官了解团队情况、团队做的业务、本职位具体做的工作、工作的规划,甚至一些数据(可能有些问题不会直面回答)。

还可以问一些关于公司培训机会和晋升机会之类的问题。如果是一些高端职位,则可以问一下:自己的 leader 想把这个职位安排给什么样的人,希望多久的时间内可以达到怎样的水平。

有想了解更多的小伙伴可以加Q群链接里面看一下,应该对你们能够有所帮助。