前言
面试日期为 2021-06-06 17:00
这回是第二次面字节,说实话,这次完全是以学习交流的态度来面试的。上回拳击手与拳击手的较量后,我不敢太装逼了,毕竟目前刚跟滴滴的面试官battle完。
自我介绍
首先面试官开启了摄像头,哎呦是位姐姐,那姐姐就好说话了~
之后我就开始我的嘴遁(自我介绍)之后,我宣布面试比赛正式开始。
项目相关
问:webpack如何提升打包速度?
(1)优化 Loader
对于 Loader 来说,影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST,然后对 AST 继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。当然了,这是可以优化的。
(2)HappyPack
受限于 Node 是单线程运行的,所以 Webpack 在打包的过程中也是单线程的,特别是在执行 Loader 的时候,长时间编译的任务很多,这样就会导致等待的情况。
(3)DllPlugin
DllPlugin 可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。
(4)代码压缩
在 Webpack3 中,一般使用 UglifyJS
来压缩代码,但是这个是单线程运行的,为了加快效率,可以使用 webpack-parallel-uglify-plugin
来并行运行 UglifyJS
,从而提高效率。
PS : 嗯嗯,送分题,这是高手之间的试探,我要稳扎稳打,以学习的态度,慢慢找面试官的破绽。
问:webpack中loader和plugin的区别?
不同的作⽤:
- Loader直译为"加载器"。
Webpack
将⼀切⽂件视为模块,但是Webpack
原⽣是只能解析JavaScript
⽂件,如果想将其他⽂件也打包的话,就会⽤到Loader
。 所以Loader
的作⽤是让Webpack
拥有了加载和解析非JavaScript
文件的能力; - Plugin直译为"插件"。
Plugin
可以扩展Webpack
的功能,让Webpack
具有灵活性。在Webpack
运⾏的⽣命周期中会⼴播出许多事件,Plugin
可以监听这些事件,在合适的时机通过Webpack
提供的API改变输出结果。
不同的⽤法:
- Loader在
module.rules
中配置,也就是说他作为模块的解析规则⽽存在。 类型为数组,每⼀项都是⼀个Object
,⾥⾯描述了对于什么类型的⽂件( test )
,使⽤什么加载( loader )
和使⽤的参数( options )
- Plugin在 plugins 中单独配置。 类型为数组,每⼀项是⼀个 plugin 的实例,参数都通过构造函数传⼊。
PS : 这道题好多面试官都爱问,这个东西我背的已经比我个人介绍还要熟悉了,基本面试官问完就秒答了。
问:简历中的项目你怎么做的优化?
正常项目优化嘛,毕竟自我介绍中就是那么说的
PS : 因为在个人介绍时一般都会以优化方向为主,所以一般面试官比较喜欢问我关于webpack
的问题。
CSS
问 : 媒体查询有使用过吗?
媒体查询由⼀个可选的媒体类型和零个或多个使⽤媒体功能的限制了样式表范围的表达式组成,例如宽度、⾼度和颜⾊。媒体查询,添加⾃CSS3,允许内容的呈现针对⼀个特定范围的输出设备⽽进⾏裁剪,⽽不必改变内容本身,适合web⽹⻚应对不同型号的设备⽽做出对应的响应适配。
媒体查询包含⼀个可选的媒体类型和满⾜CSS3规范的条件下,包含零个或多个表达式,这些表达式描述了媒体特征,最终会被解析为true或false。如果媒体查询中指定的媒体类型匹配展示⽂档所使⽤的设备类型,并且所有的表达式的值都是true,那么该媒体查询的结果为true。那么媒体查询内的样式将会⽣效。
<!-- link元素中的CSS媒体查询 -->
<link rel="stylesheet" media="(max-width: 800px)" href="example.css" />
<!-- 样式表中的CSS媒体查询 -->
<style>
@media (max-width: 600px) {
.facet_sidebar {
display: none;
}
}
</style>
简单来说,使用 @media 查询,可以针对不同的媒体类型定义不同的样式。@media 可以针对不同的屏幕尺寸设置不同的样式,特别是需要设置设计响应式的页面,@media 是非常有用的。当重置浏览器大小的过程中,页面也会根据浏览器的宽度和高度重新渲染页面。
PS : 面试官我简历上很清楚,我大学学过UI
的,不要放海好吗,别逼我嗷。
问 : 讲一下VW ?
vw是与视图窗口有关的单位,vw表示相对于视图窗口的宽度(相对于视窗的尺寸),除了vw和vh外,还有vmin和vmax两个相关的单位。
- vw:相对于视窗的宽度,视窗宽度是100vw;
- vmin:vw和vh中的较小值;
- vmax:vw和vh中的较大值
PS : 哎呀 面试官你年轻了,真的,我还得留心你出的代码输出和手写那边的题呢,这些小问题就是洒洒水~,毛毛雨啦~,。
问 : 说一下屏幕分辨率 ?
以 iPhone XS 为例,当写 CSS 代码时,针对于单位 px,其宽度为 414px & 896px,也就是说当赋予一个 DIV元素宽度为 414px,这个 DIV 就会填满手机的宽度;
而如果有一把尺子来实际测量这部手机的物理像素,实际为 1242*2688 物理像素;经过计算可知,1242/414=3,也就是说,在单边上,一个逻辑像素=3个物理像素,就说这个屏幕的像素密度为 3,也就是常说的 3 倍屏。
对于图片来说,为了保证其不失真,1 个图片像素至少要对应一个物理像素,假如原始图片是 500300 像素,那么在 3 倍屏上就要放一个 1500900 像素的图片才能保证 1 个物理像素至少对应一个图片像素,才能不失真。
可以使用CSS 媒体查询来判断不同的像素密度
PS :到这真是没什么难度,是不是我挑那俩堂口给我练出来了(滴滴-网约车,字节-抖音),就面试官这几招我全给她破了,见招拆招,堪称完美。
代码输出题
问 : JavaScript原型链相关 ?
Function.prototype.a =()=>{
console.log(1)
}
Object.prototype.b =()=>{
console.log(2)
}
function A(){}
const a = new A()
a.a() a._proto__ == Object //这里是面试官被我绕晕后问的, 这里我说错了
a.b()
A.a()
A.b()
a.a is not a function
2
1
2
解析:
a
并不是Function
的实例,因为它本来就不是构造函数,调用的是Function
原型链上的相关属性和方法,只能访问到Object
原型链。所以a.b()
输出 a ,而a.a()
就报错了。A
是个构造函数,而A
是构造函数Function
的一个实例。因为A instanceof Object === true
,A instanceof Function === true
,由此可以得出结论:A
是Object
和Function
两个的实例,即 F 能访问到 a, 也能访问到 b。所以 A.a() 输出 1 ,A.b() 输出 2。
ps : 这里我不光把自己绕蒙了,还给面试官也带偏了,这叫什么?这叫把你拉入我的领域,然后击败你。
问 : JavaScript原型链相关 ?
let aname = 'zzh'
obj = {
aname : 'lyy',
say() {
console.log(this.aname);
return function () {
console.log(this.aname)
console.log(aname)
}
}
}
obj.say()()
lyy
undefined
zzh
解析:
say方法中的this就会指向他所在的对象,输出其中的a的值,return
之后this就指向了全局
ps : 我已记不清我当时的答案了,面试官给我问迷糊了,这招叫眩晕
,我中招了,但是莫事,本回合结束自动解开,纵观全局,估计面试已经到了中场,接下来估计要来大的了。
手写题
JS实现一个带并发限制的异步调度器Scheduler
保证同时运行的任务数最多有俩个
完善代码中Scheduler类
Class Scheduler{
constructor(){}
}
function timeout(time){
return new Promise(resolve=>{
setTimeout(resolve,time)
})
}
var scheduler = new Scheduler()
function addTask(time,order){
scheduler.add(()=>timeout(time).then(()=>console.log(order)))
}
addTask(1000,1)
addTask(500,2)
addTask(300,3)
addTask(400,4)
//要求
// ouput : 2 3 1 4
//一开始1,2俩个任务进入队列
//500ms时,2完成,输出2,任务3进入队列
//800ms时,3完成,输出3,任务4进入队列
//1000ms时,1完成,输出1
//1200ms时,4完成,输出4
解释一下原题目中的代码:
Scheduler类是需要我们实现的一个调度器类,他帮助我们管理添加的异步任务,做到并发限制。
也就是比如说,以前我们执行异步任务,是直接在当前环境直接执行Promise,而现在要把Promise任务添加到Scheduler中,让他帮助调度执行Promise,于是就有了add方法。
在add中,通过Scheduler的add方法,将异步任务扔进Scheduler中。往下看,add方法后面跟着.then,说明add应该返回一个Promise。
因为add会返回Promise且直接.then执行异步任务,说明在add方法里帮我们判断了异步任务数量是否超出限制,并告诉我们该执行哪个任务。
那么接下来的目的就很明确了,编写Scheduler类,最重要的就是add方法的实现。
答案:
Class Scheduler{
constructor(){
this.count = 2; //当前运行总数量
this.queren = [];//待运行的任务
this.run = []; //正运行的任务
}
add(task){
return new Promise((resolve,reject)=>{
task.resolve = resolve
if(this.run.length < 2) this.querenRun(task)
else this.queren.push(task)
})
}
querenRun(task){
this.run.push(task)
task.then(()=>{
task.resolve()
this.qunrendel(task)
if(this.queren.length > 0){
this.querenRun(this.queren.shift())
}
})
}
qunrendel(task){
let index = this.run.findIndex(task)
this.run.splice(index,1)
}
}
function timeout(time){
return new Promise(resolve=>{
setTimeout(resolve,time)
})
}
var scheduler = new Scheduler()
function addTask(time,order){
scheduler.add(()=>timeout(time).then(()=>console.log(order)))
}
addTask(1000,1)
addTask(500,2)
addTask(300,3)
addTask(400,4)
//要求
// ouput : 2 3 1 4
//一开始1,2俩个任务进入队列
//500ms时,2完成,输出2,任务3进入队列
//800ms时,3完成,输出3,任务4进入队列
//1000ms时,1完成,输出1
//1200ms时,4完成,输出4
道题的核心思想就是通过Promise的嵌套来控制主Promise的执行
ps : 这就是面试官的大招了吗?这块我最开始想了2分钟左右才开始动笔,牛客的ACM模式还是挺不舒服的,慢慢一边写一边说自己的思路,最后差不多了,又跟面试官讲了思路,虽然代码不咋的,但是咱们能吹啊~ 吹啊吹啊我骄傲放纵~
反问
职业规划,入职培训,新技术的看法,然后吹波字节的彩虹屁,逗逗姐姐。
总结
面得感觉还可以 但是比滴滴金融的一面效果差了点,感觉一面过的几率大一点,没过10分钟,就有人打电话了,我就参与了二面,这次面试总体而言感觉不错,这个对手不错,强劲的对手(不过如此)啊。
重铸字节荣光,我辈义不容辞
我宣布: 此次比武大会,面试者以微(ju)弱(da)优势战胜面试官。让我们恭喜这位选手晋级下场面试,未来前端的新星即将升起。