文章目录
面试专题:settimeout剩余参数,数组扁平化,var实现let,性能优化Memo,useMemo,useCallback
1.settimeout剩余参数:如何用settimeout去实现setinterval的功能
- 大多数小伙伴遇到这种编程题,第一反应就是惊喜,so easy,于是乎,就嗖嗖嗖写完了
function setInterval_(callback,time) {
function settimeout (){
setTimeout(()=>{
callback()
settimeout()
},time)
}
settimeout()
};
setInterval_(()=>{
console.log(1);
},1000)
- 看起来功能都实现了,但是仅仅是这个简单吗?答案是不,setinterval不止2个参数!
var intervalID = setInterval(func, [delay, arg1, arg2, ...]);
- 看的出来,不止2个参数,甚至有无数参数
- 这些参数都会作为回调的入参传穿进去
function setInterval_(callback,time,...args) {
let callback_ = callback;
function settimeout (){
setTimeout((...args_)=>{
callback_(...args_);
settimeout()
},time,...args)
}
settimeout()
};
setInterval_((...args)=>{
console.log(1,...args);
},11,22,33)
2.数组扁平化:设计一个flat函数将如下数组arr=[1,2,[‘3’,4,‘5’,[6,[7,8],9]]]输出为1,2,‘3’,4,‘5’,6,7,8,9。写出两种方法,要求不改原初始类型
1. 常规循环和递归
function glats(a) {
let flatArr = [];
function flat(ele) {
if (Array.isArray(ele)) {
ele.forEach((item) => {
if (Array.isArray(item)) {
flat(item)
} else {
flatArr.push(item)
}
})
} else {
flatArr.push(ele)
}
}
flat(a)
return flatArr
}
2.ES6新特性flat()
- flat可以对数组进行扁平化
- 利用递归不断扁平化
- 利用try catch 实现跳出foreach循环
function myFlat(a) {
let flatArr = []
function flat(ele) {
flatArr = ele.flat(1); // 扁平化一层
try {
flatArr.forEach(item => {
if (Array.isArray(item)) {
flat(flatArr) // 递归
throw new Error() // 利用catch跳出循环
}
})
} catch (error) {}
}
flat(a);
return flatArr
}
3.常用的 ES6 语法有哪些,var 怎么实现let
- let 相对于var来说形成了块级作用域
for (let i = 0; i < 4; i++) {
setTimeout(()=>{
console.log(i); // 0 1 2 3
},100)
}
for (var c = 0; c < 4; i++) {
setTimeout(()=>{
console.log(c); // 4 4 4 4
},100)
}
- let 的块级作用域只会在当前作用域生效,超出则回收,而var并不会被回收
for (var c = 0; c < 4; c++) {}
console.log(c) // 4
for (let i = 0; i < 4; i++) {};
console.log(i) // undefined
利用函数的闭包存储当前var的值
for (var i = 0; i < 4; i++) {
function va(i){
setTimeout(()=>{
console.log(i); //0 1 2 3
},1000)
}
va(i)
}
4.useCallback和useMemo有什么区别
1.前世:class组件大一统时代
- 说起性能优化,我们一定不会陌生。一个是pureComponent,一个是shouldComponentUpdate
shouldComponentUpdate
:根据返回值来处理是否要更新pureComponent
:包裹在组件外面,根据shouldComponentUpdate进行封装的,对props进行浅层的对比,从而判断是否需要更新
import React, { Component } from 'react';
function shallowEqual(obj1, obj2) {//传入新老state和prop
if (obj1 === obj2) {//一样直接不改变
return true
}
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
return false//检测,如果有一个满足就更新
}
let keys1 = Object.keys(obj1)//拿到所有属性名组成的数组
let keys2 = Object.keys(obj2)//拿到所有属性名组成的数组
if (keys1.length !== keys2.length) {
return false//如果键值对数量不相等,那么更新
}
for (const key of keys1) {//循环进行比较
if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) {
return false//如果obj2没有key的值,或者表层的key的值不相等时,更新
}
}
return true//走到这里,说明没改变,那么不更新
}
class PureComponent extends Component {//引用shouldcomponentupdate方法
shouldComponentUpdate(nextProps, nextState) {//传入的nextprops,nextState与这次的比较,并且传入
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState)
//返回值是ture就更新组件,false就不更新组件,这里调用手写shallowEqual方法
}
render() {
return this.props.children;
}
}
export default PureComponent;
2.今生:函数式轻量化组件,hooks百花齐放
React.memo():
解决了整个组件的是否render的问题(实现了类似PureComponent)
- 第二个参数回调如果没传的话,就会进行浅对比,
function MyComponents() {};
function areEqual(prevState, nextState) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
export default React.memo(MyComponents,areEqual)
React.useMemo() :
细粒度性能优化,返回的是一个memoized值,当依赖项变化时才会重新计算
- usememo是在render期间执行的,所以
不能进行一些副操作,如网络请求
- 如果没有提供依赖值,那么每次都会重新计算
const useMomes =useMemo(()=>{
return a+b+c
},[a,b]);
React.useCallback:
用法其实和useMemo是一样的,但是他们唯一的区别是,useCallback是缓存了函数
- 父子关系组件中,父组件向子组件传递一个方法,由有函数的特殊性,每次父组件更新都会触发子组件,此时用usecallback,即可指定值变化时触发更新.
const fnB=()=>{}
const fnA = useCallback(fnB, [a])
3.速记口诀:值是useMemo,函数是useCallback,都不传Memo
- 只穿值的话,优化用useMomo,因为会对值进行浅different
- 函数则用useCallback,因为他会缓存返回值的空间地址。
- 既没有值也没有函数时,可以用Memo
- 三兄弟可以认为是不同阶段的不同分支和方向,都会用闭包去缓存值,他们反映的都是当前的状态