面试题练习第五篇

面试题第五篇
- 1. 说说React生命周期中有哪些坑?如何避免?
- 2. 说说Real diff算法是怎么运作的?
- 3. 调和阶段setState干了什么?
- 4. 说说redux的实现原理是什么,写出核心代码?
- 5. React合成事件的原理?
- 6. React组件之间如何通信?
- 7. 为什么react元素有一个$$type属性?
- 8. 说说Connect组件的原理是什么?
- 9. 说说你对fiber架构的理解?解决了什么问题?
- 10. 说说你对redux中间件的理解?常用的中间件有哪些?实现原理?
- 11. React性能优化的手段有哪些?
- 12. 说说你对事件循环event loop的理解?
- 14. 数组常用方法及作用,至少15个?
- 15. React render方法的原理,在什么时候会触发?
- 16. 说说你对vue中mixin的理解?
- 17. for...in循环和for...of循环的区别?
- 18. Js数据类型判断都有哪几种方式?至少说出5种?它们的区别是什么?
- 19. 说说你对Object.defineProperty()的理解?
- 20. 说说你对webSocket的理解?
1. 说说React生命周期中有哪些坑?如何避免?
-
getDerivedStateFromProps容易编写反模式代码,使受控组件和非受控组件区分模糊 -
componentWillMount在React中已被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它被多次调用,所以网络请求以及事件绑定应该放到componentDidMount中 -
componentWillReceiveProps同样也被标记弃用,被getDerivedStateFromProps所取代,主要原因是性能问题。 -
shouldComponentUpdate通过返回true或者false来确定是否需要触发新的渲染。主要用于性能优化。 -
componentWillUpdate同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合getSnapshotBeforeUpdate与componentDidUpdate改造使用。
如果在 componentWillUnmount 函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug。
如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。
2. 说说Real diff算法是怎么运作的?
Diff算法是虚拟DOM的一个必然结果,它是通过新旧DOM的对比,将在不更新页面的情况下,将需要内容局部更新Diff算法遵循深度优先,同层比较的原则- 可以使用
key值,可以更加准确的找到DOM节点
react中diff算法主要遵循三个层级的策略:
tree层级
conponent 层级
element 层级
tree层不会做任何修改,如果有不一样,直接删除创建
component层从父级往子集查找,如果发现不一致,直接删除创建
element层有key值做比较,如果发现key值可以复用的话,就会将位置进行移动,如果没有,则执行删除创建
3. 调和阶段setState干了什么?
(1)代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。
(2)经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面;
(3)在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
(4)在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
4. 说说redux的实现原理是什么,写出核心代码?
原理
- 将应用的状态统一放到
state中,由store来管理state。 reducer的作用是 返回一个新的state去更新store中对用的state。- 按
redux的原则,UI层每一次状态的改变都应通过action去触发,action传入对应的reducer中,reducer返回一个新的state更新store中存放的state,这样就完成了一次状态的更新 subscribe是为store订阅监听函数,这些订阅后的监听函数是在每一次dipatch发起后依次执行- 可以添加中间件对提交的
dispatch进行重写
核心API
createStore创建仓库,接受reducer作为参数bindActionCreator绑定store.dispatch和action的关系combineReducers合并多个reducersapplyMiddleware洋葱模型的中间件,介于dispatch和action之间,重写dispatchcompose整合多个中间件
5. React合成事件的原理?
React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运行。
收集的事件放在dispatchQueue数组中,而冒泡和捕获的区别在于执行时机和顺序,那么我们只需要对数组按照不同顺序循环执行即可
首先会在fiber节点进入render阶段的complete阶段时,将事件监听绑定在root上。然后调用ensureListeningTo进行事件绑定,生成事件合成对象、收集事件、触发真正的事件。
6. React组件之间如何通信?
-
父组件向子组件通讯:
父组件可以向子组件传入props的方式,向子组件进行通讯。 -
子组件向父组件通讯:
props+回调的方式,父组件向子组件传递props进行通讯,此props为作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中。 -
兄弟组件通信:
兄弟组件之间的传递,则父组件作为中间层来实现数据的互通,通过使用父组件传递
例:组件A – 传值 --> 父组件 – 传值 --> 组件B -
跨层级通讯:
Context设计⽬的是为了共享那些对于⼀个
组件树⽽⾔是“全局”的数据,
使用context提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据
例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过Context通信再适合不过。 -
发布订阅者模式:
发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊event模块进⾏通信。 -
全局状态管理工具:
借助Redux或者Mobx等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store,并根据不同的事件产⽣新的状态。
7. 为什么react元素有一个$$type属性?
目的是为了防止 XSS 攻击。因为 Synbol 无法被序列化,所以 React 可以通过有没有 $$typeof 属性来断出当前的 element 对象是从数据库来的还是自己生成的。
如果没有 $$typeof 这个属性,react 会拒绝处理该元素。
8. 说说Connect组件的原理是什么?
connect是一个高阶函数,它真正连接 Redux 和 React,包在我们的容器组件的外一层,接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
原理:
首先传入mapStateToProps、mapDispatchToProps,然后返回一个生产Component的函数(wrapWithConnect),然后再将真正的Component作为参数传入wrapWithConnect,这样就生产出一个经过包裹的Connect组件,该组件具有:
(1)通过props.store获取祖先Component的store
(2)props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component
(3)componentDidMount时,添加事件this.store.subscribe(this.handleChange),实现页面交互
(4)shouldComponentUpdate时判断是否有避免进行渲染,提升页面性能,并得到nextState
(5)componentWillUnmount时移除注册的事件this.handleChange
9. 说说你对fiber架构的理解?解决了什么问题?
解决的问题:
JavaScript引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起等待;如果 JavaScript 线程长时间地占用了主线程,那么渲染层面的更新就不得不长时间地等待,界面长时间不更新,会导致页面响应度变差,用户可能会感觉到卡顿
理解:
React Fiber 是对 React 做出的一个重大改变与优化,是对 React 核心算法的一次重新实现
主要做了:
- 为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务
- 增加了异步任务,调用
requestIdleCallback api,浏览器空闲的时候执行 dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行
10. 说说你对redux中间件的理解?常用的中间件有哪些?实现原理?
理解:
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
Redux中,中间件就是放在就是在dispatch过程,在分发action进行拦截处理
本质上一个函数,对store.dispatch方法进行了改造,在发出 Action和执行 Reducer这两步之间,添加了其他功能
常用中间件:
redux-thunk:用于异步操作
redux-logger:用于日志记录
实现原理:
所有中间件被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。可以看到,中间件内部(middlewareAPI)可以拿到getState和dispatch这两个方法内部会将dispatch进行一个判断,然后执行对应操作
11. React性能优化的手段有哪些?
- 使用纯组件;
- 使用
React.memo进行组件记忆(React.memo是一个高阶组件),对 于相同的输入,不重复执行; - 如果是类组件,使用
shouldComponentUpdate(这是在重新渲染组件之前触发的其中一个生命周期事件)生命周期事件,可以利用此事件来决定何时需要重新渲染组件; - 路由懒加载;
- 使用
React Fragments避免额外标记; - 不要使用内联函数定义(如果我们使用内联函数,则每次调用“
render”函数时都会创建一个新的函数实例); - 避免在
Willxxx系列的生命周期中进行异步请求,操作dom等; - 如果是类组件,事件函数在
Constructor中绑定bind改变this指向; - 避免使用内联样式属性;
- 优化
React中的条件渲染; - 不要在
render方法中导出数据; - 列表渲染的时候加
key; - 在函数组件中使用
useCallback和useMemo来进行组件优化,依赖没有变化的话,不重复执行; - 类组件中使用
immutable对象;
12. 说说你对事件循环event loop的理解?
任务在主线程不断进栈出栈的一个循环过程。任务会在将要执行时进入主线程,在执行完毕后会退出主线程。
13. 前端跨域的解决方案?
通过jsonp跨域 document.domain + iframe跨域 location.hash + iframe window.name + iframe跨域 postMessage跨域 跨域资源共享(CORS) nginx代理跨域 nodejs中间件代理跨域 WebSocket协议跨域
14. 数组常用方法及作用,至少15个?
Array.length:返回或设置一个数组中的元素个数Array.from() :对伪数组或可迭代对象(包括arguments,Array,Map,Set,String…)转换成数组对象Array.isArray():用于确定传递的值是否是一个Arrayconcat():方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。every(callback):方法测试数组的所有元素是否都通过了指定函数的测试filter():创建一个新数组, 其包含通过所提供函数实现的测试的所有元素find():返回数组中满足提供的测试函数的第一个元素的值forEach():方法对数组的每个元素执行一次提供的函数includes():用来判断一个数组是否包含一个指定的值,如果是,酌情返回true或falseindexOf():返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1join():将数组(或一个类数组对象)的所有元素连接到一个字符串中lastIndexOf():返回指定元素(也即有效的JavaScript值或变量)在数组中的最后一个的索引,如果 不存在则返回 -1。从数组的后面向前查找map():创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果pop():从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度push():将一个或多个元素添加到数组的末尾reduce():累加器和数组中的每个元素(从左到右)应用一个函数shift():从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度slice():返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象some():测试数组中的某些元素是否通过由提供的函数实现的测试。sort():当的位置对数组的元素进行排序,并返回数组。splice():通过删除现有元素和/或添加新元素来更改一个数组的内容toString():返回一个字符串,表示指定的数组及其元素unshift():将一个或多个元素添加到数组的开头,并返回新数组的长度toLocaleString():返回一个字符串表示数组中的元素。数组中的元素将使用各自的toLocaleString方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 “,”)隔开
15. React render方法的原理,在什么时候会触发?
原理:
在类组件中render函数指的就是render方法;而在函数组件中,指的就是整个函数组件
render函数中的jsx语句会被编译成我们熟悉的js代码,在render过程中,react将新调用的render函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff 比较,更新dom树
触发时机:
类组件调用 setState 修改状态
函数组件通过useState hook修改状态
一旦执行了setState就会执行render方法,useState 会判断当前值有无发生改变确定是否执行render方法,一旦父组件发生渲染,子组件也会渲染
16. 说说你对vue中mixin的理解?
mixin是一种类,在vue中就是js文件,主要的作用是作为功能模块引用。因为在项目中,可能不同组件会有相同的功能,比如控制元素的显示和隐藏,如果他们的变量和规则也完全相同的话,就可以把这个功能单独提取出来,放在mixin.js中,再引入,就可以实现一样的功能了。引入的方法也分为全局混入和局部混入,局部混入就是在每个组件中引入,全局混入就是在main.js中通过Vue.mixin()引入。
17. for…in循环和for…of循环的区别?
for...in 循环:只能获得对象的键名,不能获得键值
for…in循环有几个缺点
①数组的键名是数字,但是for…in循环是以字符串作为键名“0”、“1”、“2”等等。
②for…in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
③某些情况下,for…in循环会以任意顺序遍历键名。
for…in循环主要是为遍历对象而设计的,不适用于遍历数组。
for...of 循环:允许遍历获得键值
for…of循环
①有着同for…in一样的简洁语法,但是没有for…in那些缺点。
②不同于forEach方法,它可以与break、continue和return配合使用。
③提供了遍历所有数据结构的统一操作接口
18. Js数据类型判断都有哪几种方式?至少说出5种?它们的区别是什么?
1. typeof判断
typeof返回的类型都是字符串形式
2. Constructor
实例constructor属性指向构造函数本身
constructor 判断方法跟instanceof相似,但是constructor检测Object与instanceof不一样,constructor还可以处理基本数据类型的检测,不仅仅是对象类型
3. Instanceof
instanceof可以判类型是否是实例的构造函数
instanceof 后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。
4. Object.prototype.toString.call()
判断类型的原型对象是否在某个对象的原型链上
5. 通过object原型上的方法判断
比如array.isArray()来判断是不是一个数组
6. ===(严格运算符)
通常出现在我们的条件判断中,用来判断数据类型的话就会非常的有局限性,比如判断一个变量是否为空,变量是否为数据等
19. 说说你对Object.defineProperty()的理解?
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
该方法接受三个参数
- 第一个参数是
obj:要定义属性的对象, - 第二个参数是
prop:要定义或修改的属性的名称或Symbol, - 第三个参数是
descriptor:要定义或修改的属性描述符
函数的第三个参数 descriptor 所表示的属性描述符有两种形式:数据描述符和存取描述符。
- 数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。
- 存取描述符是由
getter函数和setter函数所描述的属性。
一个描述符只能是这两者其中之一;不能同时是两者。
这两种同时拥有下列两种键值:
configurable: 是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)。设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false。
enumerable: 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false。
20. 说说你对webSocket的理解?
理解
WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
Websocket是一个持久化的协议
原理
websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信
在websocket出现之前,web交互一般是基于http协议的短连接或者长连接
websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"


