共计 3212 个字符,预计需要花费 9 分钟才能阅读完成。
昨天分享了深入浅出 setState 原理篇,其中讲到 setState 是同步还是异步的问题?这不,引起了古老的回顾,打开笔记,想起已经有一个体验良好的面试,面试官从一道面试题登程,循序渐进,引出了各种知识点,这些知识点能检测出面试者的 React 知识点、ES6 知识点、JS 根底等。我在此基础上,加上本人的了解,整顿一个集体认为考点较短缺的面试分享
单方客套,面试正式开始,面试官正手来一个面试题
如下的代码,a 的值是多少
class A extends React.Component {constructor(props) {super(props)
this.state = {a: 0}
}
componentDidMount() {this.setState({ a: 1});
setTimeout(() => {this.setState({ a: 2});
}, 0);
new Promise((resolve) => {resolve(this.setState({ a: 3}));
}).then(() => {this.setState({ a: 4});
});
}
render() {console.log("state", this.state.a);
return <div>{this.state.a}</div>;
}
}
这题考查到“React 渲染生命周期”以及“setState 是同步还是异步”知识点
答案是:0、3、4、2
首先是 React 渲染生命周期,当挂载时,其生命周期调用程序为:
- constructor
- static getDerivedStateFromProps()
- render
- componentDidMount
所以先 render 一次 state.a
,值为 0,接着进入 componentDidMount 生命周期,this.setState({a: 1})
和 this.setState({a: 3})
为同步操作,setTimeout 会将其中的回调函数(即 () => { this.setState({ a: 2})}
)放入宏工作中,then 之后的回调函数(即 () =>{this.setState({ a: 4})}
)会放入微工作中
因为(legacy 模式下)setState 的同周期内的 setState 会批处理合成为一个 setState,并当前者为主,所以 this.setState({a: 1})
会被笼罩。因为调用了 setState,触发了更新,意味着又 render 一次,此时的 state.a
就显示为 3。当此宏工作调用完后去查看微工作队列,发现有未执行的回调函数,执行它 this.setState({a: 4})
,又一次调用 setState,触发更新,state.a
显示 4。微工作队列为空后,查看宏工作队列,发现回调函数 this.setState({a: 2})
,执行,触发更新,state.a
显示 2
所以其后果为:0、3、4、2
不晓得你对否~~
咱们接着革新一个这个题,变成
class A extends React.Component {constructor(props) {super(props)
this.state = {a: 0}
}
componentDidMount() {this.setState({ a: 1});
console.log("a", this.state.a);
setTimeout(() => {this.setState({ a: 2});
console.log("a", this.state.a);
}, 0);
new Promise((resolve) => {resolve(this.setState({ a: 3}));
console.log("a", this.state.a);
}).then(() => {this.setState({ a: 4});
console.log("a", this.state.a);
});
}
render() {return <div>{this.state.a}</div>;
}
}
不在 render 展现 state.a
,而是在调用完 setState 后查看 state.a
的值,后果会如何呢?
改编后的题次要考查组件的数据更新和视图的更新是两码事
答案是:0、0、3、2
首先都是在 componentDidMount 中,其次,与上个案例一样,调用顺次是
- this.setState({a: 1});
- this.setState({a: 3});
- this.setState({a: 4});
- this.setState({a: 2});
其区别在于调用 this.setState({a: 1}) 和 this.setState({a: 3}) 后,数据不会马上更新,调用 setState 后,会将调用压入队列中,到最初一并执行(批处理),所以此时查看 state.a
的值,会看到还是 0,因为它还没触发批处理。而 Promise、setTimeout 之类原生事件会同步执行,值就显示为你 setState 什么,我就显示什么
咱们在上述两个例子中谈到了 Event Loop,在 React 中会因为性能优化而对 setState 做解决,如果在浏览器环境中,上述的例子会怎么展现呢?
console.log('0')
setTimeout(() => {console.log("1");
}, 0);
new Promise((resolve) => {resolve()
console.log("2");
}).then(() => {console.log("3");
});
console.log('4')
这题次要考验了浏览器的 Event Loop 机制
答案:0、2、4、3、1
第一个值为 0 没有疑难
遇到 setTimeout,所以 console.log(“1”) 排入 宏工作队列
因为 new Promise 中的执行函数会同步执行,而 then 中的 ”console.log(“3”)” 会进入微工作,所以第二个值为 2,
接着就是第三个值 4,再因为 Event Loop 机制(宏工作 - 执行全副微工作 - 再去找宏工作队列第一个),所以先执行微工作,第四个值为 3
最初执行宏工作(setTimeout),第五个值为 5
既然说到了 Promise,无妨考考 Promise,手写一个太麻烦,没必要考课本。那来说说为什么 promise 中能 .then,它的链式调用的原理是什么?
每次 new Promise() 后能 .then().then().then(),因为它每次调用完 then 后,返回了 Promise 实例,所以能力始终调用上来
这样了解下来,链式调用的外围,就是调用完办法后返回对象自身(return this)
那咱们出一道题,题目是
class Operator {...}
var op = new Operator(1)
op.add(3).minus(2).multi(2).divide(1)
写出 Operator 构造函数中的 add、minus、multi、divide
我的答案是:
class Operator {constructor(initNum) {this.num = initNum;}
add(value) {
this.num = this.num + value
return this
}
minus(value) {
this.num = this.num - value;
return this
}
multi(value) {
this.num = this.num * value;
return this;
}
divide(value) {
this.num = this.num / value;
return this;
}
}
从这题能够引申出 class、ES6 还有有哪些个性和柯里化等等
总结
从 this.setState 的一道面试题延申出各种问题,即考查了面试者对 this.setState 的了解,又考了浏览器的 Event Loop,并引申出 Promise 的链式调用,并用一道题目考查面试者的 JS 根底能力,再之后,还能够问 ES6 的个性和柯里化,常识广度就变大了,也能更好的考查面试者