前言
人家都说,前端需要每年定期出来面面试,衡量一下自己当前的技术水平以及价值,本人17年7月份,毕业到现在都没出来试过,也没很想换工作,就出来试试,看看自己水平咋样。
以下为我现场面试时候的一些回答,部分因人而异的问题我就不回答了,回答的都为参考答案,也有部分错误的地方或者不好的地方,有更好的答案的可以在评论区评论。
百度 WEB前端工程师 连续五面 全程3约个小时
一面
先完成笔试题:
1、实现一个函数,判断输入是不是回文字符串。
function run(input) {
if (typeof input !== 'string') return false;
return input.split('').reverse().join('') === input;
}
2、两种以上方式实现已知或者未知宽度的垂直水平居中。
// 1
.wraper {
position: relative;
.box {
position: absolute;
top: 50
%;
left: 50%;
width: 100px;
height: 100px;
margin: -50px 0 0 -50px;
}
}
// 2
.wraper {
position: relative;
.box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-
50%, -50%);
}
}
// 3
.wraper {
.box {
display: flex;
justify-content:center;
align-items: center;
height: 100px;
}
}
// 4
.wraper {
display: table;
.box {
display
: table-cell;
vertical-align: middle;
}
}
3、实现效果,点击容器内的图标,图标边框变成border 1px solid red,点击空白处重置。
const box = document.getElementById('box');
function isIcon(target) {
return target.className.includes('icon');
}
box.onClick = function(e) {
e.stopPropagation();
const target = e.target;
if (isIcon(target)) {
target.style.border = '1px solid red';
}
}
const doc = document;
doc.onclick = function(e) {
const children = box.children;
for(let i; i < children.length; i++) {
if (isIcon(children[i])) {
children[i].style.border = 'none';
}
}
}
4、请简单实现双向数据绑定mvvm。
id="input"/>
const data = {};
const input = document.getElementById('input');
Object.defineProperty(data, 'text', {
set(value) {
input.value = value;
this.value = value;
}
});
input.onChange =
function(e) {
data.text = e.target.value;
}
5、实现Storage,使得该对象为单例,并对localStorage进行封装设置值setItem(key,value)和getItem(key)
var instance = null;
class Storage {
static getInstance() {
if (!instance) {
instance = new Storage();
}
return instance;
}
setItem = (key, value) => localStorage.setItem(key
, value),
getItem = key => localStorage.getItem(key)
}
Q1 你的技术栈主要是react,那你说说你用react有什么坑点?
1、JSX做表达式判断时候,需要强转为boolean类型,如:
render() {
const b = 0;
return <div>
{
!!b && <div>这是一段文本div>
}
div>
}
如果不使用 !!b 进行强转数据类型,会在页面里面输出 0。
2、尽量不要在 componentWillReviceProps 里使用 setState,如果一定要使用,那么需要判断结束条件,不然会出现无限重渲染,导致页面崩溃。
3、给组件添加ref时候,尽量不要使用匿名函数,因为当组件更新的时候,匿名函数会被当做新的prop处理,让ref属性接受到新函数的时候,react内部会先清空ref,也就是会以null为回调参数先执行一次ref这个props,然后在以该组件的实例执行一次ref,所以用匿名函数做ref的时候,有的时候去ref赋值后的属性会取到null。详情见
4、遍历子节点的时候,不要用 index 作为组件的 key 进行传入。
Q2 我现在有一个button,要用react在上面绑定点击事件,要怎么做?
class Demo {
render
() {
return <button onClick={(e) => {
alert('我点击了按钮')
}}>
按钮
button>
}
}
Q3 接上一个问题,你觉得你这样设置点击事件会有什么问题吗?
由于onClick使用的是匿名函数,所有每次重渲染的时候,会把该onClick当做一个新的prop来处理,会将内部缓存的onClick事件进行重新赋值,所以相对直接使用函数来说,可能有一点的性能下降(个人认为)。
修改:
class Demo {
onClick = (e) => {
alert('我点击了按钮')
}
render() {
return <button onClick={this.onClick}>
按钮
button>
}
}
当然你在内部声明的不是箭头函数,然后你可能需要在设置onClick的时候使用bind绑定上下文,这样的效果和先前的使用匿名函数差不多,因为bind会返回新的函数,也会被react认为是一个新的prop。
Q4 你说说event loop吧
首先,js是单线程的,主要的任务是处理用户的交互,而用户的交互无非就是响应DOM的增删改,使用事件队列的形式,一次事件循环只处理一个事件响应,使得脚本执行相对连续,所以有了事件队列,用来储存待执行的事件,那么事件队列的事件从哪里被push进来的呢。那就是另外一个线程叫事件触发线程做的事情了,他的作用主要是在定时触发器线程、异步HTTP请求线程满足特定条件下的回调函数push到事件队列中,等待js引擎空闲的时候去执行,当然js引擎执行过程中有优先级之分,首先js引擎在一次事件循环中,会先执行js线程的主任务,然后会去查找是否有微任务microtask(promise),如果有那就优先执行微任务,如果没有,在去查找宏任务macrotask(setTimeout、setInterval)进行执行。
Q5 说说事件流吧
事件流分为两种,捕获事件流和冒泡事件流。
事件流分为三个阶段,一个是捕获节点,一个是处于目标节点阶段,一个是冒泡阶段。
Q6 我现在有一个进度条,进度条中间有一串文字,当我的进度条覆盖了文字之后,文字要与进度条反色,怎么实现?
。。。当时我给的是js的方案,在进度条宽度变化的时候,计算盖过每一个文字的50%,如果超过,设置文字相反颜色。
当然css也有对应的方案,也就是 mix-blend-mode,我并没有接触过。
对应html也有对应方案,也就设置两个相同位置但是颜色相反的dom结构在重叠在一起,顶层覆盖底层,最顶层的进度条取overflow为hidden,其宽度就为进度。
二面
Q1 你为什么要离开上一家公司?
-
Q2 你觉得理想的前端地位是什么?
-
Q3 那你意识到问题所在,你又尝试过解决问题吗?
-
三面
Q1 说一下你上一家公司的一个整体开发流程吧
-
Q2 react 的虚拟dom是怎么实现的
首先说说为什么要使用Virturl DOM,因为操作真实DOM的耗费的性能代价太高,所以react内部使用js实现了一套dom结构,在每次操作在和真实dom之前,使用实现好的diff算法,对虚拟dom进行比较,递归找出有变化的dom节点,然后对其进行更新操作。为了实现虚拟DOM,我们需要把每一种节点类型抽象成对象,每一种节点类型有自己的属性,也就是prop,每次进行diff的时候,react会先比较该节点类型,假如节点类型不一样,那么react会直接删除该节点,然后直接创建新的节点插入到其中,假如节点类型一样,那么会比较prop是否有更新,假如有prop不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点。
Q3 react 的渲染过程中,兄弟节点之间是怎么处理的?也就是key值不一样的时候。
通常我们输出节点的时候都是map一个数组然后返回一个ReactNode,为了方便react内部进行优化,我们必须给每一个reactNode添加key,这个key prop在设计值处不是给开发者用的,而是给react用的,大概的作用就是给每一个reactNode添加一个身份标识,方便react进行识别,在重渲染过程中,如果key一样,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新,如果key不一样,则react先销毁该组件,然后重新创建该组件。
Q4 我现在有一个数组[1,2,3,4],请实现算法,得到这个数组的全排列的数组,如[2,1,3,4],[2,1,4,3]。。。。你这个算法的时间复杂度是多少
这个我没写出来,大概给了个思路,将每一个数组拆除俩个小数组进行求它的全排列,然后得到的结果互相之间又进行全排列,然后把最后的结果连接起来。。。
感兴趣的同学见数组全排列
Q5 我现在有一个背包,容量为m,然后有n个货物,重量分别为w1,w2,w3...wn,每个货物的价值是v1,v2,v3...vn,w和v没有任何关系,请求背包能装下的最大价值。
这个我也没写出来,也给了个思路,首先使用Q4的方法得到货物重量数组的全组合(包括拆分成小数组的全组合),然后计算每一个组合的价值,并进行排序,然后遍历数组,找到价值较高切刚好能装进背包m的组合。
本题
动态规划面试题
,感兴趣的同学请自行百度或者谷歌。
四面
Q1 请说一下你的上一家公司的研发发布流程。
-
Q2 你说一下webpack的一些plugin,怎么使用webpack对项目进行优化。
正好最近在做webpack构建优化和性能优化的事儿,当时吹了大概15~20分钟吧,插件请见webpack插件归纳总结。
构建优化:
-
减少编译体积 ContextReplacementPugin、IgnorePlugin、babel-plugin-import、babel-plugin-transform-runtime
-
并行编译 happypack、thread-loader、uglifyjsWebpackPlugin开启并行
-
缓存 cache-loader、hard-source-webpack-plugin、uglifyjsWebpackPlugin开启缓存、babel-loader开启缓存
-
预编译 dllWebpackPlugin && DllReferencePlugin、auto-dll-webapck-plugin
性能优化:
-
减少编译体积 Tree-shaking、Scope Hositing
-
hash缓存 webpack-md5-plugin
-
拆包 splitChunksPlugin、import()、require.ensure
Q3 es6 class 的new实例和es5的new实例有什么区别
这个我觉得是一样的(当时因为很少看babel编译之后的结果),面试官说不一样。。。后来我看了一下babel的编译结果,发现只是类的方法声明的过程不一样而已,最后new的结果是一样的。。。具体答案现在我也不知道。。。
Q4 看你简历上写了canvas,你说一下为什么canvas的图片为什么过有跨域问题
canvas图片为什么跨域我不知道,至今没查出来,也差不多,大概跨域原因和浏览器跨域的原因是一样的吧。
Q5 我现在有一个canvas,上面随机布着一些黑块,请实现方法,计算canvas上有多少个黑块
使用getImageData获取像素数组,然后遍历数组,把在遍历节点的过程中,查看节点上下左右的像素颜色是否相同,如果相同,然后设置标识,最后groupBy一下所有像素。(这是我当时的方案)
其他更好的答案见地址
Q6 请手写实现一个promise
这个就不写了,详情见promise实现原理
注:四面是一个超级可爱的小姐姐,电脑给我让我写完之后,我说我写得差不多了,然后电脑给她,然后她竟然默默的在看我的代码,尝试寻找我的思路,也没有问我实现思路是啥,然后我就问她,你不应该是让我给你解释我的代码思路吗。。。你竟然在尝试寻找我的思路,我自己都不知道我自己是思路是啥。。。然后我两都笑了,哈哈哈。最后结束的时候我说我午饭还没吃,她还叫了另外一个小哥哥先带了下去吃饭,真是一个善良的小姐姐,非常感谢。
五面
Q1 你说一下你的技术有什么特点