前端高频面试题(附答案)
说一下 HTTP 3.0
HTTP/3 基于 UDP 协议实现了类似于 TCP 的多路复用数据流、传输可靠性等功能,这套功能被称为 QUIC 协议。
流量控制、传输可靠性功能:QUIC 在 UDP 的基础上增加了一层来保证数据传输可靠性,它提供了数据包重传、拥塞控制、以及其他一些 TCP 中的特性。
集成 TLS 加密功能:目前 QUIC 使用 TLS1.3,减少了握手所花费的 RTT 数。
多路复用:同一物理连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输,解决了 TCP 的队头阻塞问题。
快速握手:由于基于 UDP,可以实现使用 0 ~ 1 个 RTT 来建立连接。
箭头函数与普通函数的区别
(1)箭头函数比普通函数更加简洁
如果没有参数,就直接写一个空括号即可
如果只有一个参数,可以省去参数的括号
如果有多个参数,用逗号分割
如果函数体的返回值只有一句,可以省略大括号
如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个 void 关键字。最常见的就是调用一个函数:
(2)箭头函数没有自己的 this
箭头函数不会创建自己的 this, 所以它没有自己的 this,它只会在自己作用域的上一层继承 this。所以箭头函数中 this 的指向在它在定义时已经确定了,之后不会改变。
(3)箭头函数继承来的 this 指向永远不会改变
对象 obj 的方法 b 是使用箭头函数定义的,这个函数中的 this 就永远指向它定义时所处的全局执行环境中的 this,即便这个函数是作为对象 obj 的方法调用,this 依旧指向 Window 对象。需要注意,定义对象的大括号{}
是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中。
(4)call()、apply()、bind()等方法不能改变箭头函数中 this 的指向
(5)箭头函数不能作为构造函数使用
构造函数在 new 的步骤在上面已经说过了,实际上第二步就是将函数中的 this 指向该对象。 但是由于箭头函数时没有自己的 this 的,且 this 指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。
(6)箭头函数没有自己的 arguments
箭头函数没有自己的 arguments 对象。在箭头函数中访问 arguments 实际上获得的是它外层函数的 arguments 值。
(7)箭头函数没有 prototype
(8)箭头函数不能用作 Generator 函数,不能使用 yeild 关键字
Loader 和 Plugin 有什么区别
Loader:直译为"加载器"。Webpack 将一切文件视为模块,但是 webpack 原生是只能解析 js 文件,如果想将其他文件也打包的话,就会用到loader
。 所以 Loader 的作用是让 webpack 拥有了加载和解析非 JavaScript 文件的能力。 Plugin:直译为"插件"。Plugin 可以扩展 webpack 的功能,让 webpack 具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
setTimeout、Promise、Async/Await 的区别
(1)setTimeout
(2)Promise
Promise 本身是同步的立即执行函数, 当在 executor 中执行 resolve 或者 reject 的时候, 此时是异步操作, 会先执行 then/catch 等,当主栈完成后,才会去调用 resolve/reject 中存放的方法执行,打印 p 的时候,是打印的返回结果,一个 Promise 实例。
当 JS 主线程执行到 Promise 对象时:
promise1.then() 的回调就是一个 task
promise1 是 resolved 或 rejected: 那这个 task 就会放入当前事件循环回合的 microtask queue
promise1 是 pending: 这个 task 就会放入 事件循环的未来的某个(可能下一个)回合的 microtask queue 中
setTimeout 的回调也是个 task ,它会被放入 macrotask queue 即使是 0ms 的情况
(3)async/await
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。
例如:
func1 的运行结果其实就是一个 Promise 对象。因此也可以使用 then 来处理后续逻辑。
await 的含义为等待,也就是 async 函数需要等待 await 后的函数执行完成并且有了返回结果(Promise 对象)之后,才能继续执行下面的代码。await 通过返回一个 Promise 对象来实现同步的效果。
浏览器是如何对 HTML5 的离线储存资源进行管理和加载?
在线的情况下,浏览器发现 html 头部有 manifest 属性,它会请求 manifest 文件,如果是第一次访问页面 ,那么浏览器就会根据 manifest 文件的内容下载相应的资源并且进行离线存储。如果已经访问过页面并且资源已经进行离线存储了,那么浏览器就会使用离线的资源加载页面,然后浏览器会对比新的 manifest 文件与旧的 manifest 文件,如果文件没有发生改变,就不做任何操作,如果文件改变了,就会重新下载文件中的资源并进行离线存储。
离线的情况下,浏览器会直接使用离线存储的资源。
vuex
代码输出结果
输出结果如下:
代码的执行过程如下:
首先会进入 Promise,打印出 3,之后进入下面的 Promise,打印出 7;
遇到了定时器,将其加入宏任务队列;
执行 Promise p 中的 resolve,状态变为 resolved,返回值为 1;
执行 Promise first 中的 resolve,状态变为 resolved,返回值为 2;
遇到 p.then,将其加入微任务队列,遇到 first().then,将其加入任务队列;
执行外面的代码,打印出 4;
这样第一轮宏任务就执行完了,开始执行微任务队列中的任务,先后打印出 1 和 2;
这样微任务就执行完了,开始执行下一轮宏任务,宏任务队列中有一个定时器,执行它,打印出 5,由于执行已经变为 resolved 状态,所以
resolve(6)
不会再执行;最后
console.log(p)
打印出Promise{<resolved>: 1}
;
setTimeout 模拟 setInterval
描述:使用setTimeout
模拟实现setInterval
的功能。
实现:
函数中的 arguments 是数组吗?类数组转数组的方法了解一下?
是类数组,是属于鸭子类型的范畴,长得像数组,
... 运算符
Array.from
Array.prototype.slice.apply(arguments)
实现一个扇形
用 CSS 实现扇形的思路和三角形基本一致,就是多了一个圆角的样式,实现一个 90°的扇形:
如果一个构造函数,bind 了一个对象,用这个构造函数创建出的实例会继承这个对象的属性吗?为什么?
不会继承,因为根据 this 绑定四大规则,new 绑定的优先级高于 bind 显示绑定,通过 new 进行构造函数调用时,会创建一个新对象,这个新对象会代替 bind 的对象绑定,作为此函数的 this,并且在此函数没有返回对象的情况下,返回这个新建的对象
什么是尾调用,使用尾调用有什么好处?
尾调用指的是函数的最后一步调用另一个函数。代码执行是基于执行栈的,所以当在一个函数里调用另一个函数时,会保留当前的执行上下文,然后再新建另外一个执行上下文加入栈中。使用尾调用的话,因为已经是函数的最后一步,所以这时可以不必再保留当前的执行上下文,从而节省了内存,这就是尾调用优化。但是 ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。
CSS 预处理器/后处理器是什么?为什么要使用它们?
预处理器, 如:less
,sass
,stylus
,用来预编译sass
或者less
,增加了css
代码的复用性。层级,mixin
, 变量,循环, 函数等对编写以及开发 UI 组件都极为方便。
后处理器, 如: postCss
,通常是在完成的样式表中根据css
规范处理css
,让其更加有效。目前最常做的是给css
属性添加浏览器私有前缀,实现跨浏览器兼容性的问题。
css
预处理器为css
增加一些编程特性,无需考虑浏览器的兼容问题,可以在CSS
中使用变量,简单的逻辑程序,函数等在编程语言中的一些基本的性能,可以让css
更加的简洁,增加适应性以及可读性,可维护性等。
其它css
预处理器语言:Sass(Scss)
, Less
, Stylus
, Turbine
, Swithch css
, CSS Cacheer
, DT Css
。
使用原因:
结构清晰, 便于扩展
可以很方便的屏蔽浏览器私有语法的差异
可以轻松实现多重继承
完美的兼容了
CSS
代码,可以应用到老项目中
new 一个构造函数,如果函数返回 return {}
、 return null
, return 1
, return true
会发生什么情况?
如果函数返回一个对象,那么 new 这个函数调用返回这个函数的返回对象,否则返回 new 创建的新对象
什么是 margin 重叠问题?如何解决?
问题描述: 两个块级元素的上外边距和下外边距可能会合并(折叠)为一个外边距,其大小会取其中外边距值大的那个,这种行为就是外边距折叠。需要注意的是,浮动的元素和绝对定位这种脱离文档流的元素的外边距不会折叠。重叠只会出现在垂直方向。
计算原则: 折叠合并后外边距的计算原则如下:
如果两者都是正数,那么就去最大者
如果是一正一负,就会正值减去负值的绝对值
两个都是负值时,用 0 减去两个中绝对值大的那个
解决办法: 对于折叠的情况,主要有两种:兄弟之间重叠和父子之间重叠 (1)兄弟之间重叠
底部元素变为行内盒子:
display: inline-block
底部元素设置浮动:
float
底部元素的 position 的值为
absolute/fixed
(2)父子之间重叠
父元素加入:
overflow: hidden
父元素添加透明边框:
border:1px solid transparent
子元素变为行内盒子:
display: inline-block
子元素加入浮动属性或定位
CSS 选择器及其优先级
对于选择器的优先级:
标签选择器、伪元素选择器:1
类选择器、伪类选择器、属性选择器:10
id 选择器:100
内联样式:1000
注意事项:
!important 声明的样式的优先级最高;
如果优先级相同,则最后出现的样式生效;
继承得到的样式的优先级最低;
通用选择器(*)、子选择器(>)和相邻同胞选择器(+)并不在这四个等级中,所以它们的权值都为 0 ;
样式表的来源不同时,优先级顺序为:内联样式 > 内部样式 > 外部样式 > 浏览器用户自定义样式 > 浏览器默认样式。
Proxy 可以实现什么功能?
在 Vue3.0 中通过 Proxy
来替换原本的 Object.defineProperty
来实现数据响应式。
Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作。
target
代表需要添加代理的对象,handler
用来自定义对象中的操作,比如可以用来自定义 set
或者 get
函数。
下面来通过 Proxy
来实现一个数据响应式:
在上述代码中,通过自定义 set
和 get
函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。
当然这是简单版的响应式实现,如果需要实现一个 Vue 中的响应式,需要在 get
中收集依赖,在 set
派发更新,之所以 Vue3.0 要使用 Proxy
替换原本的 API 原因在于 Proxy
无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy
可以完美监听到任何方式的数据改变,唯一缺陷就是浏览器的兼容性不好。
说下对 JS 的了解吧
是基于原型的动态语言,主要独特特性有 this、原型和原型链。
JS 严格意义上来说分为:语言标准部分(ECMAScript)+ 宿主环境部分
语言标准部分
2015 年发布 ES6,引入诸多新特性使得能够编写大型项目变成可能,标准自 2015 之后以年号代号,每年一更
宿主环境部分
在浏览器宿主环境包括 DOM + BOM 等
在 Node,宿主环境包括一些文件、数据库、网络、与操作系统的交互等
评论