零 · 引言
此次面试记录分享,对于react面试者更为实用,因为本人常用的技术栈为react,感觉面试官很nice,面试也是面试你擅长的部分,所以你要在简历里突出你的擅长。准备好小本本开始面试之旅吧!
在进行简单的自我介绍后,开始讨论react的问题,涉及比较多,本次主要记录了一下几个问题
壹 · setState 异步同步机制
以下的setState是分几批次执行?
saveState =() =>{
this.setState({ num: 1 })
this.setState({ num2: 1 })
this.setState({ num3: 1 }, () => {
this.setState({ num: 3 })
console.log('callback', this.state)
})
this.setState({ num4: 1 })
console.log('num4', this.state)
}
复制代码
想象不如实操
- 在回调函数打印输出结果中没有对num的更新
- componentDidUpdate 生命周期中
如果存在多个回调函数呢?
import React, { Component } from 'react'
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {};
}
saveState = () => {
this.setState({ num: 1 })
this.setState({ num2: 1 })
this.setState({ num3: 1 }, () => {
this.setState({ num: 3 })
console.log('callback', this.state)
})
this.setState({ num6: 1 }, () => {
this.setState({ num: 7 })
console.log('callback6', this.state)
})
this.setState({ num4: 1 })
console.log('num4', this.state)
}
componentDidUpdate = (prevProps, prevState, snapshot) => {
console.count('componentDidUpdate')
console.log("🚀 ~ componentDidUpdatePrev", prevProps, prevState, snapshot)
console.log("🚀 ~ componentDidUpdate", this.state)
}
render() {
console.count('render')
return (
<div onClick={this.saveState}>测试</div>
);
}
}
export default App;
复制代码
- 点击测试,多个回调函数仅触发一次回调
- 直接从1变成了7
贰 · fieber 算法解决的问题
React Fiber的目标是提高其对动画,布局和手势等区域的适用性。它的标题功能是增量渲染:将渲染工作分成多个块并将其分布到多个帧中的能力。
其他关键功能包括随着新更新的出现而暂停,中止或重用工作的功能;为不同类型的更新分配优先级的能力;和新的并发原语。 React->Fieber
叁 · 实现 promise.then()
完整的promise实现
- 状态和值的变化处理
- 指定默认返回函数
- 使用setTimeout实现异步执行
(function () {
function MyPromise(executor) {
//参数校验
if (typeof executor !== 'function') {
throw new TypeError("MyPromise resolve" + executor + "is not a function")
}
//设置默认值
var _this = this
this.PromiseStatus = 'pending'
this.PromiseValue = undefined
this.resolveFunc = function () { }//匿名空函数,未使用传递then,Function.prototype
this.rejectFunc = function () { }
//修改实例状态value方法,只有当前状态为pending才能修改状态
function change(status, value) {
if (_this.PromiseStatus !== 'pending') return;
_this.PromiseStatus = status
_this.PromiseValue = value
//通知基于then注入的某个方法执行(异步的)
var delayTimer = setTimeout(function () {
clearTimeout(delayTimer)
delayTimer = null
//
var status = _this.PromiseStatus,
value = _this.PromiseValue;
status === 'fulfilled' ?
_this.resolveFunc.call(_this, value) :
_this.rejectFunc.call(_this, value)
}, 0)
}
//处理设定传递给executor,并且执行可以需要修改实例状态和value值resolve/reject函数
//new 立即执行传入函数executor
//executor函数执行出现错误,也会把实例的状态改为失败,且value的失败原因
try {
executor(function resolve(value) {
change('fulfilled', value)
}, function reject(reason) {
change('rejected', reason)
})
} catch (err) {
change('rejected', err.message)
}
}
MyPromise.prototype.then = function (resolveFunc, rejectFunc) {
//参数不传默认值的处理:目的是为了实现状态顺延
if (typeof resolveFunc !== 'function') {
//给不穿的时候默认返回一个函数
resolveFunc = function (value) {
return MyPromise.resolve(value)
}
}
if (typeof rejectFunc !== 'function') {
rejectFunc = function (value) {
return MyPromise.reject(value)
}
}
var _this = this
// this.resolveFunc = resolveFunc
// this.rejectFunc = rejectFunc
//每次执行.then都会返回一个新的promise实例
return new MyPromise(function (resolve, reject) {
//返回的新实例的成功和失败(执行resolve/ reject):
//由于函数是否报错决定(或返回值是否为新的Promise实例来决定)
_this.resolveFunc = function (value) {
try {
var x = resolveFunc.call(_this, value)
//执行结果如果是Promise实例,x.then判断执行成功或失败,否则返回值
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (err) {
reject(err.message)
}
}
_this.rejectFunc = function (reason) {
try {
var x = rejectFunc.call(_this, reason)
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (err) {
reject(err.message)
}
}
})
}
MyPromise.prototype.catch = function (rejectFunc) {
return this.then(null, rejectFunc)
}
MyPromise.resolve = function (value) {
return new MyPromise(function (resolve) {
resolve(value)
})
}
MyPromise.reject = function (reason) {
return new MyPromise(function (_, reject) {
reject(reason)
})
}
MyPromise.all = function (promiseArr) {
return new MyPromise(function (resolve, reject) {
var index = 0,
values = []
for (var i = 0; i < promiseArr.length; i++) {
//利用闭包的方式保存循环的每一项的索引
(function (i) {
var item = promiseArr[i];
//如果当前项不是promise:直接算当前项成功
!(item instanceof MyPromise) ?
item = new MyPromise(item) :
null
//如果是当前项是promise
item.then(function (value) {
index++;
values[i] = value
if (index >= promiseArr.length) {
resolve(values)
}
}).catch(function (reason) {
//只有有一个失败,整体失败
reject(reason)
})
return;
})(i)
}
})
}
window.MyPromise = MyPromise
})()
复制代码
肆 · 实现 splice
方案一:使用新数组,简单实现
- 获取原数组
- 判断临界值
- 返回新数组
- 给原始的Array添加属性
const arr = [1, 2, 3]
/**
*
* @param {*} 路径
* @param {*} removeLength 删除个数
* @param {*} arguments 替换元素
*/
function spliceTest(index, removeLength, ...arguments) {
let res = []
let array = Object(this);//获取原数组
let otherParams = arguments
const endIndex = index + removeLength
for (let i = 0; i < array.length; i++) {
if (i == index) {//插入数组位置
res.push(...otherParams)
} else if (i > index && i < endIndex) {//删除
continue
} else {
res.push(arr[i])//添加
}
}
return res
}
Array.prototype.spliceTest = spliceTest //给原始的Array添加属性
console.log("输出:", arr.spliceTest(1, 1, 4, 5))
复制代码
方案二:基于原数组实现
在对比后感觉这个文章比较详细,大家可以参考看下 如何手动实现数组的splice方法 ? (V8源码级别)
收获
感觉通过面试,不仅是为了找工作,也可以给你的未来的技术学习指路,面试官可遇而不可求,一个好的面试过程,可以给你带来好奇的想象,明确或是加深对未来技术追求的目标。
思维是开发的源泉:优化思维、反向思维、对比思维、打破思维