百度前端面试题(一)

百度 WEB前端工程师 连续五面 全程3约个小时

一面

实现一个函数,判断输入是不是回文字符串。

function run(input) {
  if (typeof input !== 'string') return false;
  return input.split('').reverse().join('') === input;
}

两种以上方式实现已知或者未知宽度的垂直水平居中

// 1
.wrapper {
  position: relative;
  .box {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100px;
    height: 100px;
    margin: -50px 0 0 -50px;
  }
}

// 2
.wrapper {
  position: relative;
  .box {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
}

// 3
.wrapper {
  .box {
    display: flex;
    justify-content:center;
    align-items: center;
    height: 100px;
  }
}

// 4
.wrapper {
  display: table;
  .box {
    display: table-cell;
    vertical-align: middle;
  }
}

实现效果,点击容器内的图标,图标边框变成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 = 0; i < children.length; i++) {
    if (isIcon(children[i])) {
      children[i].style.border = 'none';
    }
  }
}

请简单实现双向数据绑定mvvm。

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;
}

实现Storage,使得该对象为单例,并对localStorage进行封装设置值setItem(key,value)和getItem(key)

var instance = null;
class Storage {
  static getInstance() {
    if (!instance) {
      instance = new Storage();
    }
    return this.instance;
  }
  setItem = (key, value) => localStorage.setItem(key, value),
  getItem = key => localStorage.getItem(key)
}

Q1 你的技术栈主要是react,那你说说你用react有什么坑点?

  • JSX做表达式判断时候,需要强转为boolean类型,如:

    render() {
    const b = 0;
    return <div>
      {
        !!b && <div>这是一段文本</div>
      }
    </div>
    }

    如果不使用 !!b 进行强转数据类型,会在页面里面输出 0。

  • 2、尽量不要在 componentWillReviceProps 里使用 setState,如果一定要使用,那么需要判断结束条件,不然会出现无限重渲染,导致页面崩溃。(实际不是componentWillReviceProps会无限重渲染,而是componentDidUpdate)

  • 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 说说事件流吧

事件流分为两种,捕获事件流和冒泡事件流。 捕获事件流从根节点开始执行,一直往子节点查找执行,直到查找执行到目标节点。 冒泡事件流从目标节点开始执行,一直往父节点冒泡查找执行,直到查到到根节点。

DOM事件流分为三个阶段,一个是捕获节点,一个是处于目标节点阶段,一个是冒泡阶段。

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对项目进行优化。

构建优化

  • 1、减少编译体积 ContextReplacementPugin、IgnorePlugin、babel-plugin-import、babel-plugin-transform-runtime。
  • 2、并行编译 happypack、thread-loader、uglifyjsWebpackPlugin开启并行
  • 3、缓存 cache-loader、hard-source-webpack-plugin、uglifyjsWebpackPlugin开启缓存、babel-loader开启缓存
  • 4、预编译 dllWebpackPlugin && DllReferencePlugin、auto-dll-webapck-plugin

性能优化

  • 1、减少编译体积 Tree-shaking、Scope Hositing。
  • 2、hash缓存 webpack-md5-plugin
  • 3、拆包 splitChunksPlugin、import()、require.ensure

Q3 es6 class 的new实例和es5的new实例有什么区别

这个我觉得是一样的(当时因为很少看babel编译之后的结果),面试官说不一样。。。后来我看了一下babel的编译结果,发现只是类的方法声明的过程不一样而已,最后new的结果是一样的。。。具体答案现在我也不知道。。。

Q4 看你简历上写了canvas,你说一下为什么canvas的图片为什么过有跨域问题。

canvas图片为什么跨域我不知道,至今没查出来,也差不多,大概跨域原因和浏览器跨域的原因是一样的吧。

Q5 我现在有一个canvas,上面随机布着一些黑块,请实现方法,计算canvas上有多少个黑块。

使用getImageData获取像素数组,然后遍历数组,把在遍历节点的过程中,查看节点上下左右的像素颜色是否相同,如果相同,然后设置标识,最后groupBy一下所有像素。(这是我当时的方案)

Q6 请手写实现一个promise

五面

Q1 你说一下你的技术有什么特点

Q2 说一下你觉得你最得意的一个项目?你这个项目有什么缺陷,弊端吗?

Q3 现在有那么一个团队,假如让你来做技术架构,你会怎么做?

考虑到团队每一个前端的技术栈可能不一致,这个时候我可能选择微前端架构,让每个人负责的模块可以单独开发,单独部署,单独回滚,不依赖于其他项目模块,在尽可能的情况下节约团队成员之间的学习成本,当然这肯定也有缺点,那就是每个模块都需要一个前端项目,单独部署,单独回滚无疑也加大了运维成本。

Q4 说一下你上一家公司的主要业务流程,你参与到其中了吗?