谈谈前端面试中遇到的问题(一)
前言
歇了一个多月,终于是拿了驾照,也算是完成了人生计划中的其中一个,没有过去2019年。
2019年的全部计划估计是完不成了,想要完成多少,还是要看接下来的努力。
歇息了一个月,最近刚刚开始面试,前两个面试是一点准备都没有,去面试也仅仅是考虑一下自己现在的情况,有目的的去准备面试。
一个多月,感觉忘记了很多,在此记下面试中遇到的问题,以来自勉。
题目
1、React context是如何工作的?
更多的时候,我们在组件层级间进行数据传递,都会用到props,但是如果需要传递的子属性太多,我的组件属性就有可能会写得很长。而这时候,context或许是个解决方案。
context作用是为了避免在组件间层层传递变量。我们可以通过createContext(null)来创建一个新的context,新创建的context包含一个Provider以及一个Consumer。
如果我们想要在组件间层层传递变量,则需要用Provider来包裹父组件,在Provider包裹下的层层组件,都可以通过Consumer包裹子组件来读取传递的变量。
2、react兄弟组件间通信方式
第一种笨方法,子组件1传递给父组件,接着传递给子组件2.
第二种利用事件的发布订阅。
第三种大家都知道,用redux管理数据。原理和第一种+第二种差不多,不过是更规范的数据管理和数据流传递和事件发布订阅方式。
3、setState原理
先说明,setState既可以同步也可以异步。比如在setTimeout的包裹中setState会立即执行。我们正常方式下的setState是异步状态,因为React中的状态合并,使得多次操作,在最终的一次时间点做数据更新,避免每次都执行DOM操作耗费性能。
那么setState的原理大致可以描述为:设置新的state会将这个新的state存储在一个状态队列,如果达到批量更新的节点,则进行状态合并,更新成组件最终的state或者props。
而setState就是将新的状态放置在状态队列中的操作函数,由此也可以知道,如果直接更改state,如this.state = 1这样的操作,并不会直接更新状态队列的,所以这个操作是无效的。
这个只是简单的回答,如果要从源码上回答,还得知道setState的事务机制。
4、PureComponent和Component的区别
PureComponent会帮助我们在shouldComponentUpdate生命周期中进行一次浅比较。
浅比较只检查值类型的值和引用类型的引用是否相等,如果要做比较深入的判断,还是应该在shouldComponentUpdate生命周期自行判断。
5、如何避免重复渲染。
这个问题,其实也相当于是React性能问题的一个。
首先,不要在render中为函数绑定参数,每次参数更改都会创建新的函数,则会刷新组件或者子组件。
其次,尽量不要使用派生数据绑定到组件属性,每次传递props都会生成新的数据,即使数据一样,也会导致重复渲染。
还有,组件设定的key值要和里和固定,这样当数据一致和key一致的时候,组件也不会重新渲染。
最终,你还可以修改shouldComponentUpdate生命周期返回值来手动达到不重复渲染的目的。
6、getDerivedStateFromProps干了什么?
getDerivedStateFromProps是React16.3中新增的一个静态的生命周期函数,未来即将移除的神明周期有三个: `,componentWillMountcomponentWillReceiveProps ,componentWillUpdate
这个生命周期是映射props到state上,相当于是执行setState操作。所以,每次父组件发生props变化,都会执行setState操作。这也可能导致重复渲染,感觉尽量也减少使用吧。
getDerivedStateFromProps 是一个静态方法, 是一个和组件自身"不相关"的角色. 在这个静态方法中, 除了两个默认的位置参数 nextProps 和 currentState 以外, 你无法访问任何组件上的数据.
7、解析HTTP 304 状态码
数据请求的时候,如果服务器发现客户端有缓存,并且时间没过期,则返回304状态。
请求头中的Last Modified和f Modified Since来进行比较判断是否需要返回304,或者更新数据返回200.
8、什么是高阶组件?
我们知道函数的参数是函数,返回的是新的函数,则称为高阶函数。
高阶组件是返回组件的组件,也就是传递参数是组件,对这个组件进行加工成为一个新的组件并返回。
他用来干什么?一般用来属性代理,也就是把原本的props和新的props组合成一个新的组件的属性。所以,我们如果要创建一个loading效果组件,或者是成功失败等状态组件,可以用高阶组件来实现。
9、for...of能遍历什么?
可迭代对象。如Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等。
10、如何避免遍历到原型上的属性?
第一、for..in + hasOwnProperty方法
第二、Object.keys()
11、事件循环。
JavaScript是单线程执行模型,执行的时候将会区分为主线程和任务队列。主线程执行完毕,会从任务队列中读取新的任务放入主线程进行执行,这个读取过程是循环读取,所以也叫事件循环。
任务队列分为宏任务和微任务,同层次,先执行微任务,再执行宏任务。
微任务:promise.then()、process.nextTick()
宏任务:setTimeOut()、setInterval()
12、浏览器的渲染流程
这个问题应该就是输入url到页面呈现问题的变种,只不过此时的侧重点是获取完数据之后进行的渲染流程。
根据李兵老师的浏览器工作原理一节做如下回答:
第一步,HTML转换成DOM
第二步,CSS转换成浏览器可理解的styleSheets,然后计算DOM节点的样式
第三步,创建布居树,计算元素的布局信息
第四步,对布居树进行分层,构建分层树
第五步,为每个图层生产绘制列表,并将其提交到合成线程
第六步,合成线程将图层转化为图块,进而将图块转化成位图
第七步,合成线程发送绘制命令给浏览器
第八步,浏览器根据绘制命令生成页面,并显示到显示器上。
13、deffer和async的区别
浏览器脚本,在普通的情况下,是会依次执行。但是我们可以用deffer和async关键字来让脚本异步执行。
但是,deffer是按照加载顺序执行DOMContentLoaded之前执行,但是async则是脚本加载完毕之后立即执行(不考虑依赖以及DOM的加载状态),一般来说,deffer要比async好一点。
14、DOM选择器中,querySelectorAll和getElementByClass的区别?
querySelectorAll 返回的是一个 Static Node List,而 getElementsBy 系列的返回的是一个 Live Node List。
所以静态节点列表相当于是快照,不影响文档操作,所以我们可以遍历循环querySelectorAll生产的数据。
但是动态节点列表,如果我们的一些查询和操作就会变成死循环。
从性能上来讲,getElementByClass要更快。
15、map和forEach的区别
forEach返回undefined,map会返回新的数组。
forEach没办法中止循环,但是map可以通过返回false或者出错来中止。
16、call、apply以及bind的区别。
三者都是改变this执行,不同的是,call和apply是直接生成了函数调用,而bind则是返回了一个函数,你需要再次执行才会达到相同的效果。
call和apply又是因为参数的传递方式不一样,apply传递的是数组,call传递的单个参数的陈列。
bind则是以函数调用参数的方式传递参数。
17、css中的box-sizing
懵逼了,不知道是啥,之前根本没用过。
查了下,box-sizing 属性允许你以某种方式定义某些元素,以适应指定区域。例如,假如您需要并排放置两个带边框的框,可通过将 box-sizing 设置为 "border-box"。这可令浏览器呈现出带有指定宽度和高度的框,并把边框和内边距放入框中。
box-sizing的值有三个,content-box:只有内容、border-box:包括边框、inherit:继承自父元素。
总结
刚开始面试,有些生疏,并且有时候明明记得,却死活想不起来,大概是太长时间没有思考技术的原因。事后仔细思考,觉得这些问题都是很基础,很简单的题目,只要好好准备一下,应该还是慢慢会好起来的。