2024年Web前端最新前端经典面试题 30道,大厂前端开发面试解答

最后

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

最后写上我自己一直喜欢的一句名言:世界上只有一种真正的英雄主义就是在认清生活真相之后仍然热爱它

  • 查询node_modules目录之中是否已经存在指定模块

  • 若存在,不再重新安装

  • 若不存在

      • npm 向 registry 查询模块压缩包的网址
  • 下载压缩包,存放在根目录下的.npm目录里

  • 解压压缩包到当前项目的node_modules目录

2. npm 实现原理

输入 npm install 命令并敲下回车后,会经历如下几个阶段(以 npm 5.5.1 为例):

  • 执行工程自身 preinstall

当前 npm 工程如果定义了 preinstall 钩子此时会被执行。

  • 确定首模****块

首先需要做的是确定工程中的首层依赖,也就是 dependencies 和 devDependencies 属性中直接指定的模块(假设此时没有添加 npm install 参数)。

工程本身是整棵依赖树的根节点,每个首层依赖模块都是根节点下面的一棵子树,npm 会开启多进程从每个首层依赖模块开始逐步寻找更深层级的节点。

  • 获取模块

获取模块是一个递归的过程,分为以下几步:

    • 获取模块信息。在下载一个模块之前,首先要确定其版本,这是因为 package.json 中往往是 semantic version(semver,语义化版本)。此时如果版本描述文件(npm-shrinkwrap.json 或 package-lock.json)中有该模块信息直接拿即可,如果没有则从仓库获取。如 packaeg.json 中某个包的版本是 ^1.1.0,npm 就会去仓库中获取符合 1.x.x 形式的最新版本。
  • 获取模块实体。上一步会获取到模块的压缩包地址(resolved 字段),npm 会用此地址检查本地缓存,缓存中有就直接拿,如果没有则从仓库下载。

  • 查找该模块依赖,如果有依赖则回到第1步,如果没有则停止。

  • 扁平化(dedupe

上一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。比如 A 模块依赖于 loadsh,B 模块同样依赖于 lodash。在 npm3 以前会严格按照依赖树的结构进行安装,因此会造成模块冗余。

从 npm3 开始默认加入了一个 dedupe的过程。它会遍历所有节点,逐个将模块放在根节点下面,也就是 node-modules 的第一层。当发现有重复模块时,则将其丢弃。

这里需要对重复模块进行一个定义,它指的是模块名相同且 semver****兼容。每个 semver 都对应一段版本允许范围,如果两个模块的版本允许范围存在交集,那么就可以得到一个兼容版本,而不必版本号完全一致,这可以使更多冗余模块在 dedupe 过程中被去掉。

比如 node-modules 下 foo 模块依赖 lodash@^1.0.0,bar 模块依赖 lodash@^1.1.0,则 ^1.1.0 为兼容版本。

而当 foo 依赖 lodash@^2.0.0,bar 依赖 lodash@^1.1.0,则依据 semver 的规则,二者不存在兼容版本。会将一个版本放在 node_modules 中,另一个仍保留在依赖树里。

  • 安装模****块

这一步将会更新工程中的 node_modules,并执行模块中的生命周期函数(按照 preinstall、install、postinstall 的顺序)。

  • 执行工程自身生命周****期

当前 npm 工程如果定义了钩子此时会被执行(按照 install、postinstall、prepublish、prepare 的顺序)。

最后一步是生成或更新版本描述文件,npm install 过程完成。

题 5:ES5 的继承和 ES6 的继承有什么区别?

ES5的继承时通过prototype或构造函数机制来实现。ES5****的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.apply(this))。

ES6的继承机制完全不同,实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法),然后再用子类的构造函数修改this

具体的:ES6通过class关键字定义类,里面有构造方法,类之间通过extends关键字实现继承。子类必须在constructor方法中调用super方法,否则新建实例报错。因为子类没有自己的this对象,而是继承了父类的this对象,然后对其进行加工。如果不调用super方法,子类得不到this对象。

ps:super关键字指代父类的实例,即父类的this对象。在子类构造函数中,调用super后,才可使用this关键字,否则报错。

题 6:setTimeout、Promise、Async/Await 的区别

移步此处查看答案

https://gongchenghuigch.github.io/2019/09/14/awat/

题 7:定时器的执行顺序或机制?

**因为js是单线程的,浏览器遇到setTimeout或者setInterval会先执行完当前的代码块,在此之前会把定时器推入浏览器的待执行事件队列里面,等到浏览器执行完当前代码之后会看一下事件队列里面有没有任务,有的话才执行定时器的代码。**所以即使把定时器的时间设置为0还是会先执行当前的一些代码。

输出结果:

题 8:

[‘1’,‘2’,‘3’].map(parseInt) 输出什么,为什么?

输出:[1, NaN, NaN]

  • 首先让我们回顾一下,map 函数的第一个参数 callback:

var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])

这个 callback 一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。

而 parseInt 则是用来解析字符串的,使字符串成为指定基数的整数。

parseInt(string, radix)

接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。

  • 了解这两个函数后,我们可以模拟一下运行情况
  1. parseInt(‘1’, 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照 10 为基数处理。这个时候返回 1

  2. parseInt(‘2’, 1) //基数为1(1进制)表示的数中,最大值小于2,所以无法解析,返回NaN

  3. parseInt(‘3’, 2) //基数为 2(2进制)表示的数中,最大值小于 3,所以无法解析,返回 NaN

  • map 函数返回的是一个数组,所以最后结果为[1, NaN, NaN]

**题 9:****Doctype 作用? 严格模式与混杂模式如何区分?**它们有何意义?

Doctype声明于文档最前面,告诉浏览器以何种方式来渲染页面,这里有两种模式,严格模式和混杂模式。

  • 严格模式的排版和 JS 运作模式是 以该浏览器支持的最高标准运行。

  • 混杂模式,向后兼容,模拟老式浏览器,防止浏览器无法兼容页面。

题 10:fetch 发送 2 次请求的原因

fetch 发送 post 请求的时候,总是发送 2 次,第一次状态码是 204,第二次才成功?

原因很简单,因为你用fetch的post请求的时候,导致fetch 第一次发送了一个Options请求,询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的请求。

**题 11 :**TCP 三次握手和四次挥手


三次握手之所以是三次是保证 client 和 server 均让对方知道自己的接收和发送能力没问题而保证的最小次数。


第一次 client => server 只能 server 判断出 client 具备发送能力

第二次 server => client client 就可以判断出 server 具备发送和接受能力。此时 client 还需让 server 知道自己接收能力没问题于是就有了第三次

第三次 client => server 双方均保证了自己的接收和发送能力没有问题

其中,为了保证后续的握手是为了应答上一个握手,每次握手都会带一个标识 seq,后续的 ACK 都会对这个 seq 进行加一来进行确认。

**题 12 :**img iframe script 来发送跨域请求有什么优缺点?

  • iframe

优点:跨域完毕之后 DOM 操作和互相之间的 JavaScript 调用都是没有问题的

缺点:1.若结果要以 URL 参数传递,这就意味着在结果数据量很大的时候需要分割传递,巨烦。2.还有一个是 iframe 本身带来的,母页面和 iframe 本身的交互本身就有安全性限制。

  • script

优点:可以直接返回 json 格式的数据,方便处理

缺点:只接受 GET 请求方式

  • 图片ping

优点:可以访问任何 url,一般用来进行点击追踪,做页面分析常用的方法

缺点:不能访问响应文本,只能监听是否响应

**题 13:**Cookie、sessionStorage、localStorage 的区别

共同点:都是保存在浏览器端,并且是同源的

  • Cookie:cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。(key:可以在浏览器和服务器端来回传递,存储容量小,只有大约4K左右)

  • sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。(key:本身就是一个回话过程,关闭浏览器后消失,session为一个回话,当页面不同即使是同一页面打开两次,也被视为同一次回话)

  • localStorage:localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。(key:同源窗口都会共享,并且不会失效,不管窗口或者浏览器关闭与否都会始终生效)

**题 14:**Cookie 如何防范 XSS 攻击

XSS(跨站脚本攻击)是指攻击者在返回的HTML中嵌入javascript脚本,为了减轻这些攻击,需要在HTTP头部配上,set-cookie:

  • httponly-这个属性可以防止XSS,它会禁止javascript脚本来访问cookie。

  • secure - 这个属性告诉浏览器仅在请求为https的时候发送cookie。

结果应该是这样的:Set-Cookie=…

**题 15:**浏览器和 Node 事件循环的区别?

其中一个主要的区别在于浏览器的 event loop 和 nodejs 的 event loop 在处理异步事件的顺序是不同的,nodejs 中有 micro event;其中Promise属于micro event 该异步事件的处理顺序就和浏览器不同.nodejs V11.0以上 这两者之间的顺序就相同了。

题 16:简述 HTTPS 中间人攻击

HTTPS 协议由 HTTP + SSL 协议构成,具体的链接过程可参考SSL或TLS握手的概述https://github.com/lvwxx/blog/issues/3

中间人攻击过程如下:

  1. 服务器向客户端发送公钥。

  2. 攻击者截获公钥,保留在自己手上。

  3. 然后攻击者自己生成一个【伪造的】公钥,发给客户端。

  4. 客户端收到伪造的公钥后,生成加密hash值发给服务器。

  5. 攻击者获得加密hash值,用自己的私钥解密获得真秘钥。

  6. 同时生成假的加密hash值,发给服务器。

  7. 服务器用私钥解密获得假秘钥。

  8. 服务器用加秘钥加密传输信息

**防范方法:**服务端在发送浏览器的公钥中加入 CA 证书,浏览器可以验证 CA 证书的有效性。

**题 17:**说几条 Web 前端优化策略

(1) 减少 HTTP 请求数

(2) 从设计实现层面简化页面

(3) 合理设置 HTTP 缓存

(4) 资源合并与压缩

(5) CSS Sprites

(6) Inline Images

(7) Lazy Load Images

**题 18:**你了解的浏览器的重绘和回流导致的性能问题

重绘(Repaint)和回流(Reflow)

重绘和回流是渲染步骤中的一小节,但是这两个步骤对于性能影响很大。

  • 重绘是当节点需要更改外观而不会影响布局的,比如改变 color就叫称为重绘

  • 回流是布局或者几何属性需要改变就称为回流。

回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流。

所以以下几个动作可能会导致性能问题:

  • 改变 window 大小

  • 改变字体

  • 添加或删除样式

  • 文字改变

  • 定位或者浮动

  • 盒模型

很多人不知道的是,重绘和回流其实和 Event loop 有关。

  1. 当 Event loop 执行完 Microtasks 后,会判断 document 是否需要更新。因为浏览器是 60Hz 的刷新率,每 16ms 才会更新一次。

  2. 然后判断是否有 resize或者 scroll,有的话会去触发事件,所以 resize和 scroll事件也是至少 16ms 才会触发一次,并且自带节流功能。

  3. 判断是否触发了 media query

  4. 更新动画并且发送事件

  5. 判断是否有全屏操作事件

  6. 执行 requestAnimationFrame回调

  7. 执行 IntersectionObserver回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好

  8. 更新界面

  9. 以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback回调。

减少重绘和回流

  • 使用 translate 替代 top

<div class="test"></div> <style> .test { position: absolute; top: 10px; width: 100px; height: 100px; background: red; } </style> <script> setTimeout(() => { // 引起回流 document.querySelector('.test').style.top = '100px' }, 1000) </script>

  • 使用 visibility替换 display: none,因为前者只会引起重绘,后者会引发回流(改变了布局)

把 DOM 离线后修改,比如:先把 DOM 给 display:none(有一次 Reflow),然后你修改100次,然后再把它显示出来

不要把 DOM 结点的属性值放在一个循环里当成循环里的变量

for(let i = 0; i < 1000; i++) { // 获取 offsetTop 会导致回流,因为需要去获取正确的值 console.log(document.querySelector('.test').style.offsetTop) }

  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局

  • 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame

  • CSS 选择符从右往左匹配查找,避免 DOM 深度过深

  • 将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于 video标签,浏览器会自动将该节点变为图层。

**题 19:**写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

vue 和 react 都是采用 diff 算法来对比新旧虚拟节点,从而更新节点。在 vue 的 diff函数中(建议先了解一下 diff 算法过程)。

在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的 key 去对比旧节点数组中的 key,从而找到相应旧节点(这里对应的是一个 key => index 的 map 映射)。如果没找到就认为是一个新增节点。而如果没有 key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个 map 映射,另一种是遍历查找。相比而言,map 映射的速度更快。

后话

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

对于面试,说几句个人观点。

面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。

所谓面试造火箭,工作拧螺丝。面试往往有更高的要求,也迫使我们更专心更深入地去学习一些知识,也何尝不是一种好事。

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值