4.14

83 阅读5分钟

1. 说一下你理解的闭包,以及使用场景

闭包是指有权访问另一个函数作用域中变量的函数。

形成闭包的原因是因为函数对外部作用域存在引用,导致成闭包。

闭包的作用
  • 保存函数内的一些值,形成内部属性和方法私有化
  • 延长变量的生命周期
闭包使用场景
  • 使用闭包模拟私有方法(模块化方案)
  • 柯里化函数
  • 计数器(),延迟调用(节流防抖),回调等

2. html中defer和async

script标签的defer和async只对外部脚本有效,defer和async都会让脚本异步加载,但是defer加载完脚本并不立即执行,而是等待后续文档渲染加载完毕后执行。async则是加载脚本立刻执行,在执行js脚本时会阻塞dom的渲染。

  • defer是指脚本可以延迟到文档完全解析和显示时再执行
  • async是指脚本立即下载,不妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本。

3. react中setState是异步还是同步

react在v18之前setState是异步的,但这仅限于react事件和生命周期中。设计为异步,是为了提升性能,并且保持state和props是最新的值。(如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步;)在setTimeout和原生的Dom事件里面setState是同步的。

4. 事件循环

事件循环是js的执行机制。在 js 中,任务分为宏任务(macrotask)和微任务(microtask),这两个任务分别维护一个队列,均采用先进先出的策略进行执行。同步执行的任务都在宏任务上执行。

具体步骤

  1. 代码执行,先按顺序取出一个宏任务;
  2. 宏任务中碰到微微任务把它放入微任务队列;
  3. 等待当前宏任务执行完毕,则从微任务中执行,如果有任务采用先进先出的顺序执行微任务,直到执行完所有微任务;
  4. GUI 渲染;
  5. 回到步骤 1,直到宏任务执行完毕,进行下一个宏任务;

宏任务主要有:script(整体代码)、setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)。

微任务主要有:Promise.then、 MutationObserver、 process.nextTick(Node.js 环境)。

5. 说一下防抖,节流实现

防抖是指将多次执行变为最后一次执行。

 function debounce(fn, delay) {
     let timer = null;
     return () => {
         if (timer) clearTimeout(timer);
         timer = setTimeout(fn, delay)
     }
 }

节流是指将多次执行变成每隔一段时间执行

    function throttle(fn, delay) {
        let flag = true;
        if (!flag) return;
        return () => {
            setTimeout(() => {
                fn();
                flag = false;
            }, delay);
        }
    }

6. 如何实现图片懒加载,除了监听滚动条事件,还有什么api可以实现。

  • 通过 onscroll 事件与 getBoundingClientRect API 实现图片的懒加载方案

  • 通过 Intersection Observer(交叉观察器)实现比监听 onscroll 性能更佳的图片懒加载方案

  • 通过 content-visibility: auto 实现图片资源的延迟渲染,视觉延迟

  • 通过 loading=lazy HTML 属性实现图片懒加载

  • 通过 decoding=async HTML 属性实现图片的异步解码

7. 重排和重绘,怎么避免重排问题

重排是指页面的几何属性发生变化,需要重新计算,比如width、height改变。重绘事指外观属性发生变化,需要重新绘制,比如background发生改变,需要重新绘制的过程。重排一定会发生重绘,重绘不一定会发生重排。

8. 如何避免重排
  • 减少Dom操作,需要创建多个DOM节点时,使用DocumentFragment一次性创建;
  • 元素上下移动,使用translate代替top
  • 复杂的动画使用定位,absolute/fiexd
  • 使用className批量修改样式

9. 说说项目中遇到的难点

10. 缓存

11. 性能优化

12. hook、类组件与普通函数的区别

  • 函数式组件是一个纯函数,它是需要接受props参数并且返回一个React元素就可以了。class组件是需要继承React.Component的,而且class组件需要创建render并且返回React元素,语法上来讲更复杂。

  • 函数式组件可以直接调用,返回一个新的React元素;类组件在调用时是需要创建一个实例的,然后通过调用实例里的render方法来返回一个React元素。

  • 函数式组件没有状态管理,类组件有状态管理(生命周期和内部状态)。

  • 类组件没有具体的要求。函数式组件一般是用在大型项目中来分割大组件(函数式组件不用创建实例,所以更高效),一般情况下能用函数式组件就不用类组件,提升效率。

  • Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。也就是说在React 16.8之后函数组件就可以使用state以及其他的react特性。有了hook之后函数组件式编程使react项目性能更优,代码更加简洁。

用法区别
  • 在普通函数内部你不可以使用hook,自定义hook内部可以用其他的hook
  • 自定义hook名字必须是 useXxx,而普通函数是 xxx , 函数组件是 Xxx;
  • 在这里,我们可以观察到,函数组件 和 useXxx都是可以在里面使用hook的。
  • 那么,函数组件 和 自定义hook 不同点: return 的东西不同 ,一个是 return(),一个是 return { state,setStet,xxx,yyy }。或 [ state , setState ]
  • 自定义Hook 不是非用不可,没有也不影响业务。 使用自定义hook是为了让整体代码更简洁,更好复用。

13. Fiber

Fiber(一种核心算法)其实就是在react版本v16之后的虚拟dom。因为在v16之前采用深度优先遍历去遍历节点,这样会导致树庞大和中断了就不能恢复的问题。