[JS性能优化]函数去抖(debounce)与函数节流(throttle)

前言

这是个老生常谈的话题了,之所以还搬出来讲讲,原因之一是之前根本就没在意,近期面临的一些问题需要用到这两个小技巧;原因之二,这两个技巧带来的优化不小;原因之三,顺便复习一下闭包。

开发中你可能会遇到下面的情况:

  • 监听Window对象的resizescroll事件
  • 拖拽时监听mousemove
  • 文字输入时,对输入字符串进行处理,比如要把markdwon转换成html
  • 监听文件变化,重启服务

第一种和第三种情况,事件短时间内被频繁出发,如果在事件中有大量的计算,频繁操作DOM,资源加载等重行为,可能会导致UI卡顿,严重点甚至让浏览器挂掉。对于第四种情况,有的开发者保存编辑好的文件喜欢按多次Ctrl+S,若是快速的重启服务还能Hold住,但是要是重启一个应用,就可能多次不必要的重启。

针对上面这一系列的需求,于是有了debouncethrottle两种解决办法。

函数节流

函数按照一个周期执行,例如给window绑定一个resize事件之后,只要窗口改变大小改变就打印1,如果不采用函数节流,当我们将窗口调节的时候发现控制台一直打印1,但是使用了函数节流后我们会发现调节的过程中,每隔一段时间才打印1

一个函数节流的简单实现:

/**
*
* @param func    {Function}   实际要执行的函数
* @param wait    {Number}     执行间隔,单位是毫秒(ms),默认100ms
*
* @return        {Function}   返回一个“节流”函数
*/

function throttle(func, wait = 100) {
   // 利用闭包保存定时器和上次执行时间
   let timer = null;
   let previous; // 上次执行时间
   return function() {
       // 保存函数调用时的上下文和参数,传递给 fn
       const context = this;
       const args = arguments;
       const now = +new Date();
       if (previous && now < previous + wait) { // 周期之中
           clearTimeout(timer);
   	    timer = setTimeout(function() {
   	        previous = now;
   	        func.apply(context, args);
   	    }, wait);
       } else {
           previous = now;
           func.apply(context, args);
       }
   };
}
复制代码

使用的方法也很简单:

const btn = document.getElementById('btn');

function demo() {
   console.log('click');
}
btn.addEventListener('click', throttle(demo, 1000));
复制代码

看看React中怎么使用的,下面监听窗口的resize和输入框的onChange事件:

import React, { Component } from 'react';
import { throttle } from '../../utils/utils';

export default class Demo extends Component {
 constructor() {
   super();
   this.change = throttle((e) => {
       console.log(e.target.value);
       console.log('throttle');
   }, 100);
 }
 
 componentDidMount() {
   window.addEventListener('resize', throttle(this.onWindowResize, 60));
 }
 
  componentWillUnmount() {
   window.removeEventListener('resize', throttle(this.onWindowResize, 60));
 }
 
 onWindowResize = () => {
 	console.log('resize');
 }

 handleChange = (e) => {
   e.persist();
   this.change(e);
 }

 render() {
   return (
       <input
         onChange={this.handleChange}
       />
   );
 }
}
复制代码

函数去抖

当事件触发之后,必须等待某一个时间(N)之后,回调函数才会执行,假若再等待的时间内,事件又触发了则重新再等待时间N,直到事件N内事件不被触发,那么最后一次触发过了事件N后,执行函数。

还是窗口resize,如果一直改变窗口大小,则不会打印1,只有停止改变窗口大小并等待一段时间后,才会打印1。

函数去抖简单实现:

/**
 * @param     func     {Function}   实际要执行的函数
 * @param     delay    {Number}     延迟时间,单位是毫秒(ms)
 * @return    {Function}
 */

function debounce(fn, delay = 1000) {
  let timer;

  // 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 func 函数
  return function () { 

    // 保存函数调用时的上下文和参数,传递给func
    var context = this
    var args = arguments

    // 函数被调用,清除定时器
    clearTimeout(timer)

    // 当返回的函数被最后一次调用后(也就是用户停止了某个连续的操作),
    // 再过 delay 毫秒就执行 func
    timer = setTimeout(function () {
      fn.apply(context, args);
    }, delay);
  }
}
复制代码

应用场景,监听文件变化,重启应用:

const debounce = require('./debounce');

watcher.on('change', debounce(() => {
    const child = spawn('npm', ['run', 'dev:electron'], {
      cwd,
      detached: true,
      stdio: 'inherit'
    })
    child.unref();
    electron.app.quit();
  }, delay));
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
节流是前端开发中常用的两种性能优化方式,它们可以减少事件的触发次数,提高页面的性能。下面是防节流的封装函数与使用详解。 ## 防是指在事件触发后等待一段时间再执行回调函数,如果这段时间内事件又被触发了,则重新计时。这种方式可以用来解决一些频繁触发的事件,比如窗口大小改变、输入框输入等。 ### 封装防函数 ```javascript function debounce(func, delay) { let timer; return function (...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, delay); }; } ``` ### 使用防函数 ```javascript const button = document.querySelector('button'); function handleClick() { console.log('clicked!'); } button.addEventListener('click', debounce(handleClick, 500)); ``` ## 节流 节流是指在一段时间内只执行一次回调函数,如果这段时间内事件被触发多次,则只有第一次会执行回调函数。这种方式可以用来解决一些频繁触发的事件,比如滚动事件、鼠标移动事件等。 ### 封装节流函数 ```javascript function throttle(func, delay) { let lastTime = 0; return function (...args) { const nowTime = Date.now(); if (nowTime - lastTime > delay) { lastTime = nowTime; func.apply(this, args); } }; } ``` ### 使用节流函数 ```javascript window.addEventListener('scroll', throttle(handleScroll, 500)); function handleScroll() { console.log('scrolling!'); } ``` 以上就是防节流的封装函数与使用详解,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值