一年半经验,百度、有赞、阿里前端面试总结

前言

人家都说,前端须要每一年按期出来面面试,衡量一下本身当前的技术水平以及价值,本人17年7月份,毕业到如今都没出来试过,也没很想换工做,就出来试试,看看本身水平咋样。css

如下为我现场面试时候的一些回答,部分因人而异的问题我就不回答了,回答的都为参考答案,也有部分错误的地方或者很差的地方,有更好的答案的能够在评论区评论。html

原文地址前端

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

一面

先完成笔试题java

  1. 实现一个函数,判断输入是否是回文字符串。
function run(input) {
  if (typeof input !== 'string') return false;
  return input.split('').reverse().join('') === input;
}
复制代码
  1. 两种以上方式实现已知或者未知宽度的垂直水平居中。
// 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;
  }
}

复制代码
  1. 实现效果,点击容器内的图标,图标边框变成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';
    }
  }
}
复制代码
  1. 请简单实现双向数据绑定mvvm。
<input 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;
}
复制代码
  1. 实现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有什么坑点?node

一、JSX作表达式判断时候,须要强转为boolean类型,如:react

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

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

二、尽可能不要在 componentWillReviceProps 里使用 setState,若是必定要使用,那么须要判断结束条件,否则会出现无限重渲染,致使页面崩溃。(实际不是componentWillReviceProps会无限重渲染,而是componentDidUpdate)webpack

三、给组件添加ref时候,尽可能不要使用匿名函数,由于当组件更新的时候,匿名函数会被当作新的prop处理,让ref属性接受到新函数的时候,react内部会先清空ref,也就是会以null为回调参数先执行一次ref这个props,而后在以该组件的实例执行一次ref,因此用匿名函数作ref的时候,有的时候去ref赋值后的属性会取到null。 详情见es6

四、遍历子节点的时候,不要用 index 做为组件的 key 进行传入。web

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

正好最近在作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 你说一下你的技术有什么特色

-

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

-

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

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

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

-

杭州有赞

一面 WEB前端工程师 电话面 全程43分钟

Q1 自我介绍

-

Q2 说说从输入URL到看到页面发生的全过程,越详细越好。

  1. 首先浏览器主进程接管,开了一个下载线程。
  2. 而后进行HTTP请求(DNS查询、IP寻址等等),中间会有三次捂手,等待响应,开始下载响应报文。
  3. 将下载完的内容转交给Renderer进程管理。
  4. Renderer进程开始解析css rule tree和dom tree,这两个过程是并行的,因此通常我会把link标签放在页面顶部。
  5. 解析绘制过程当中,当浏览器遇到link标签或者script、img等标签,浏览器会去下载这些内容,遇到时候缓存的使用缓存,不适用缓存的从新下载资源。
  6. css rule tree和dom tree生成完了以后,开始合成render tree,这个时候浏览器会进行layout,开始计算每个节点的位置,而后进行绘制。
  7. 绘制结束后,关闭TCP链接,过程有四次挥手。

Q3 你刚刚说了三次握手,四次挥手,那你描述一下?

本人对计算机网络的这些概念一直不是很熟悉,因此这个问题回答不会,这里mark下文章,感兴趣的同窗查看地址

Q4 刚刚Q2中说的CSS和JS的位置会影响页面效率,为何?

css在加载过程当中不会影响到DOM树的生成,可是会影响到Render树的生成,进而影响到layout,因此通常来讲,style的link标签须要尽可能放在head里面,由于在解析DOM树的时候是自上而下的,而css样式又是经过异步加载的,这样的话,解析DOM树下的body节点和加载css样式能尽量的并行,加快Render树的生成的速度。

js脚本应该放在底部,缘由在于js线程与GUI渲染线程是互斥的关系,若是js放在首部,当下载执行js的时候,会影响渲染行程绘制页面,js的做用主要是处理交互,而交互必须得先让页面呈现才能进行,因此为了保证用户体验,尽可能让页面先绘制出来。

Q5 如今有一个函数A和函数B,请你实现B继承A

// 方式1
function B(){}
function A(){}
B.prototype = new A();

// 方式2
function A(){}
function B(){
  A.call(this);
}

// 方式3
function B(){}
function A(){}
B.prototype = new A();

function B(){
  A.call(this);
}
复制代码

Q6 刚刚你在Q5中说的几种继承的方式,分别说说他们的优缺点

方式1:简单易懂,可是没法实现多继承,父类新增原型方法/原型属性,子类都能访问到

方式2:能够实现多继承,可是只能继承父类的实例属性和方法,不能继承原型属性/方法

方式3:能够继承实例属性/方法,也能够继承原型属性/方法,可是示例了两个A的构造函数

Q7 说说CSS中几种垂直水平居中的方式

参考前面百度一面笔试题Q2

Q8 Q7中说的flex布局,垂直水平居中必须知道宽度吗?

是的,必须知道高度(脑子进水了回答了必须知道,其实答案是不须要知道高度的)

Q9 描述一下this

this,函数执行的上下文,能够经过apply,call,bind改变this的指向。对于匿名函数或者直接调用的函数来讲,this指向全局上下文(浏览器为window,nodejs为global),剩下的函数调用,那就是谁调用它,this就指向谁。固然还有es6的箭头函数,箭头函数的指向取决于该箭头函数声明的位置,在哪里声明,this就指向哪里。

Q10 说一下浏览器的缓存机制

浏览器缓存机制有两种,一种为强缓存,一种为协商缓存。

对于强缓存,浏览器在第一次请求的时候,会直接下载资源,而后缓存在本地,第二次请求的时候,直接使用缓存。

对于协商缓存,第一次请求缓存且保存缓存标识与时间,重复请求向服务器发送缓存标识和最后缓存时间,服务端进行校验,若是失效则使用缓存。

强缓存方案

Exprires:服务端的响应头,第一次请求的时候,告诉客户端,该资源何时会过时。Exprires的缺陷是必须保证服务端时间和客户端时间严格同步。

Cache-control:max-age,表示该资源多少时间后过时,解决了客户端和服务端时间必须同步的问题,

协商缓存方案

If-None-Match/ETag:缓存标识,对比缓存时使用它来标识一个缓存,第一次请求的时候,服务端会返回该标识给客户端,客户端在第二次请求的时候会带上该标识与服务端进行对比并返回If-None-Match标识是否表示匹配。

Last-modified/If-Modified-Since:第一次请求的时候服务端返回Last-modified代表请求的资源上次的修改时间,第二次请求的时候客户端带上请求头If-Modified-Since,表示资源上次的修改时间,服务端拿到这两个字段进行对比。

Q11 ETag是这个字符串是怎么生成的?

没答出来,我当时猜是根据文件内容或者最后修改时间进行的加密算法。其实官方没有明确指定生成ETag值的方法。

一般,使用内容的散列,最后修改时间戳的哈希值,或简单地使用版本号。

Q12 如今要你完成一个Dialog组件,说说你设计的思路?它应该有什么功能?

  1. 该组件须要提供hook指定渲染位置,默认渲染在body下面。
  2. 而后改组件能够指定外层样式,如宽度等
  3. 组件外层还须要一层mask来遮住底层内容,点击mask能够执行传进来的onCancel函数关闭Dialog。
  4. 另外组件是可控的,须要外层传入visible表示是否可见。
  5. 而后Dialog可能须要自定义头head和底部footer,默认有头部和底部,底部有一个确认按钮和取消按钮,确认按钮会执行外部传进来的onOk事件,而后取消按钮会执行外部传进来的onCancel事件。
  6. 当组件的visible为true时候,设置body的overflow为hidden,隐藏body的滚动条,反之显示滚动条。
  7. 组件高度可能大于页面高度,组件内部须要滚动条。
  8. 只有组件的visible有变化且为ture时候,才重渲染组件内的全部内容。

Q13 你以为你作过的你以为最值得炫耀的项目?

蚂蚁金服-体验技术部 资深数据可视化研发工程师

一面 电话面 全程1小时24分钟

Q1 描述一下你最近作的可视化的项目

Q2 刚刚说的java调用js离线生成数据报告?java调用js的promise异步返回结果怎么实现的?

使用java的js引擎Nashorn,Nashorn不支持事件队列,是要引进polyfill,而后java调用js方法得到java的promise对象,而后在调用该对象的then方法,回调函数为java中的某各种的某个方法,而后while一个表示是否已执行回调的变量,若是未执行,则让java主线程sleep,若是已经执行,则跳出循环,表示是否已执行回调的变量在传入promise的回调函数中设置更改。详情代码见地址

Q3 说说svg和canvas各自的优缺点?

共同点:都是有效的图形工具,对于数据较小的状况下,都很又高的性能,它们都使用 JavaScript 和 HTML;它们都遵照万维网联合会 (W3C) 标准。

svg优势:

矢量图,不依赖于像素,无限放大后不会失真。

以dom的形式表示,事件绑定由浏览器直接分发到节点上。

svg缺点:

dom形式,涉及到动画时候须要更新dom,性能较低。

canvas优势:

定制型更强,能够绘制绘制本身想要的东西。

非dom结构形式,用JavaScript进行绘制,涉及到动画性能较高。

canvas缺点:

事件分发由canvas处理,绘制的内容的事件须要本身作处理。

依赖于像素,没法高效保真,画布较大时候性能较低。

Q4 你刚刚说的canvas渲染较大画布的时候性能会较低?为何?

由于canvas依赖于像素,在绘制过程当中是一个一个像素去绘制的,当画布足够大,像素点也就会足够多,那么想能就会足够低。

Q6 假设我如今有5000个圆,彻底绘制出来,点击某一个圆,该圆高亮,另外4999个圆设为半透明,分别说说用svg和canvas怎么实现?

首先,从数据出发,咱们的每一个圆是一个数据,这个数据有圆的x、y、radius、isHighlight若是是svg,直接渲染节点便可,而后往节点上边绑定点击事件,点击改变全部数据的高亮属性(必须同步执行完成),而后让浏览器进行绘制。若是是canvas,咱们须要本身绑定事件到canvans标签上,而后点击的时候判断点击的位置是否在圆内,若是在某个圆内,则更新全部数据的高亮属性,以后在进行一次性绘制。

Q7 刚刚说的canvas的点击事件,怎么样实现?假如不是圆,这些图形是正方形、长方形、规则图形、不规则图形呢。

针对于每个形状,将其抽象成shape类,每个类有本身的方法isPointInSide来判断节点是否在图形内,对于不规则图形,当作矩形处理,点击的时候执行该方法判断点击位置是否在图形内。

Q8 那假如个人图形可能有变形、放大、偏移、旋转的需求呢?你的这个isPointInSide怎么处理?

这个我答不出来,据面试官提示,好像有相应的API处理变形、旋转、放大等等以后的位置映射关系。

Q9 那个这个canvas的点击事件,点击的时候怎么样快速的从这5000个圆中找到你点击的那个圆(不彻底遍历5000个节点)?

能够经过预查找的形式,当鼠标划过的时候预先查找到鼠标附近的一些节点,当点击的时候在从这些预先筛选好的节点里查找点击下来的节点,固然这个方法的前提是不能影响js主线程的执行,必须是异步的形式。

Q10 那你用过@antv/g6,里面有一个tree,说说你大学时候接触到的tree的数据结构是怎么实现的?

毕业一年多,tree的结构大概忘记了,我当时是这么回答的:

大学使用的是C++学的数据结构,是用指针的形式,首先有一个根节点,根节点里有一个指针数组指向它的全部子节点,而后每个子节点也是,拥有着子节点的指针数组,一层一层往下,直到为叶子节点,指针数组指向为空。

Q11 还记得二叉树吗?描述二叉树的几种遍历方式?

先序遍历:若二叉树非空,访问根结点,遍历左子树,遍历右子树。

中序遍历:若二叉树非空,遍历左子树;访问根结点;遍历右子树。

后序遍历:若二叉树非空,遍历左子树;遍历右子树;访问根结点。

全部遍历是以递归的形似,直到没有子节点。

Q12 说说你记得的全部的排序,他们的原理是什么?

冒泡排序:双层遍历,对比先后两个节点,若是知足条件,位置互换,直到遍历结束。

快速排序:去数组中间的那一个数,而后遍历全部数,小于该数的push到一个数组,大于该数的push到另一个数组,而后递归去排序这两个数组,最后将全部结果链接起来。

选择排序:声明一个数组,每次去输入数组里面找数组中的最大值或者最小值,取出来后push到声明的数组中,直到输入数组为空。

Q13 说一下你以为你作过的最复杂的项目?中间遇到的困难,以及你是怎么解决的?

-


面试官:我这边问题差很少问完了,你还有什么问题?

我:很惊讶今天全都是问可视化相关的,没怎么问js,css,html。

面试官:那咱们继续吧

我:。。。


Q14 那给我介绍一下react吧(面试官是作可视化开发的,根本不懂react)

之前咱们没有jquery的时候,咱们大概的流程是从后端经过ajax获取到数据而后使用jquery生成dom结果真后更新到页面当中,可是随着业务发展,咱们的项目可能会愈来愈复杂,咱们每次请求到数据,或则数据有更改的时候,咱们又须要从新组装一次dom结构,而后更新页面,这样咱们手动同步dom和数据的成本就愈来愈高,并且频繁的操做dom,也使我咱们页面的性能慢慢的下降。

这个时候mvvm出现了,mvvm的双向数据绑定可让咱们在数据修改的同时同步dom的更新,dom的更新也能够直接同步咱们数据的更改,这个特定能够大大下降咱们手动去维护dom更新的成本,mvvm为react的特性之一,虽然react属于单项数据流,须要咱们手动实现双向数据绑定。

有了mvvm还不够,由于若是每次有数据作了更改,而后咱们都全量更新dom结构的话,也没办法解决咱们频繁操做dom结构(下降了页面性能)的问题,为了解决这个问题,react内部实现了一套虚拟dom结构,也就是用js实现的一套dom结构,他的做用是讲真实dom在js中作一套缓存,每次有数据更改的时候,react内部先使用算法,也就是鼎鼎有名的diff算法对dom结构进行对比,找到那些咱们须要新增、更新、删除的dom节点,而后一次性对真实DOM进行更新,这样就大大下降了操做dom的次数。

那么diff算法是怎么运做的呢,首先,diff针对类型不一样的节点,会直接断定原来节点须要卸载而且用新的节点来装载卸载的节点的位置;针对于节点类型相同的节点,会对比这个节点的全部属性,若是节点的全部属性相同,那么断定这个节点不须要更新,若是节点属性不相同,那么会断定这个节点须要更新,react会更新并重渲染这个节点。

react设计之初是主要负责UI层的渲染,虽然每一个组件有本身的state,state表示组件的状态,当状态须要变化的时候,须要使用setState更新咱们的组件,可是,咱们想经过一个组件重渲染它的兄弟组件,咱们就须要将组件的状态提高到父组件当中,让父组件的状态来控制这两个组件的重渲染,当咱们组件的层次愈来愈深的时候,状态须要一直往下传,无疑加大了咱们代码的复杂度,咱们须要一个状态管理中心,来帮咱们管理咱们状态state。

这个时候,redux出现了,咱们能够将全部的state交给redux去管理,当咱们的某一个state有变化的时候,依赖到这个state的组件就会进行一次重渲染,这样就解决了咱们的咱们须要一直把state往下传的问题。redux有action、reducer的概念,action为惟一修改state的来源,reducer为惟一肯定state如何变化的入口,这使得redux的数据流很是规范,同时也暴露出了redux代码的复杂,原本那么简单的功能,却须要完成那么多的代码。

后来,社区就出现了另一套解决方案,也就是mobx,它推崇代码简约易懂,只须要定义一个可观测的对象,而后哪一个组价使用到这个可观测的对象,而且这个对象的数据有更改,那么这个组件就会重渲染,并且mobx内部也作好了是否重渲染组件的生命周期shouldUpdateComponent,不建议开发者进行更改,这使得咱们使用mobx开发项目的时候能够简单快速的完成不少功能,连redux的做者也推荐使用mobx进行项目开发。可是,随着项目的不断变大,mobx也不断暴露出了它的缺点,就是数据流太随意,出了bug以后很差追溯数据的流向,这个缺点正好体现出了redux的优势所在,因此针对于小项目来讲,社区推荐使用mobx,对大项目推荐使用redux。

Q15 假如我一个组件有一个状态count为1,而后我在componentDidMount()里面执行执行了两次this.setState({count: ++this.state.count}),而后又执行了两次setTimeout(() => { this.setState({count: ++this.state.count}) }, 0),最后count为多少?为何?

count为4,由于第二次执行setState的时候,取不到第一次this.state.count++的结果,react在一轮生命周期结束后才会更新内部的state,若是在一轮生命周期内屡次使用了setState,react内部会有一个字段isBatchUpdate标识本次更新为批量更新,而后在最后render的时候将全部setState的结果提交到state中,一次性进行更新,而且把isBatchUpdate这个字段设置为false。

针对于两次setTimeout,js引擎会把这两个setState丢到事件队列中,等待js空闲了去执行,而咱们的渲染函数render是同步执行的(react16版本默认没有开启异步渲染),因此等咱们render执行彻底,也就是咱们的state被同步完后,在取事件队列里面的setState进行执行,setTimeout的第二个setState也是同样的,因此最后结果是4。

备注:这个count的答案彷佛有疑问,写了个demo,答案并非4,Demo地址 jsfiddle.net/yacan8/5gsp…

Q16 说一下你以为你作过的最值得你说的吧

-

最后

这几轮面试的面试官都很是和善好交流,百度的五轮面试不知道过了没有,只记得五面的面试官说,你稍等一下,我去问一下其余人对你还有什么其余要求,而后过了一下子HR就喊我先回去了,叫我等HR面的消息,若是没经过,也不会在联系我了,已通过了四天了,希望后面有消息吧。而后有赞、蚂蚁金服的两个一面都过了,由于每次面完试面试官问我还有什么问题吗?我都会询问一下本次面试面试官对个人评论是啥。

后续传送门 -> 记一次前端面试的全过程

相关文章
相关标签/搜索