【JavaScript】函数防抖(debounce)、函数节流(throttle)在面向切片编程(AOP)中的应用

什么是面向切面编程

面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,是在面向对象编程(OOP)的基础上的一种补充。AOP提供了一种灵活的、易于维护和跨多个对象和组件的切面(Aspect)的方式,可以在不修改原有代码的情况下实现代码的特定功能。

下面是一个简单的JS示例代码,实现了AOP的日志记录功能:

js 复制代码
//定义一个用于包装函数的高阶函数
function Logging(fn) {
  return function() {
    console.log(`entering ${fn.name}`);
    const result = fn.apply(this, arguments);
    console.log(`exiting ${fn.name}`);
    return result;
  }
}

//定义一个需要包装的函数
function add(x, y) {
  return x + y;
}

//将函数用Logging包装
const loggedAdd = Logging(add);

//调用经过包装的函数
loggedAdd(2, 3);

在上面的代码中,Logging是一个高阶函数(Higher Order Function),它返回的函数可以包装一个函数,并能够记录该函数的输入和输出。add是一个需要包装的函数,它的功能是相加两个数。用Logging包装add得到的loggedAdd函数,可以在add函数执行前输出"entering add",在add函数执行后输出"exiting add",并返回add函数的执行结果。

只不过这样的使用方式不太友好,怎么才能更加简单呢,在VUE、Recat、Angular等框架中常见的装饰是个比较好的方式。

装饰器

JavaScript中的装饰器是一种特殊的函数,它可以用来修改类或对象的行为。装饰器可以在不修改原始类或对象定义的情况下,动态地添加、删除或修改它们的属性和方法。

装饰器可以被用来解决很多问题,比如增加验证、日志记录、性能分析等功能,这些功能可以通过在类或对象的定义上应用不同的装饰器来实现。使用装饰器可以让代码具有更好的可维护性和可扩展性。

通常情况下,装饰器可以在函数、类、类成员上应用。

我们准备对防抖和节流这两个最常用的函数进行升级。升级前先看看最基本的使用方式。

函数防抖(debounce)

概念: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

生活中的实例: 如果有人进电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这时如果又有人进电梯了(在10秒内再次触发该事件),我们又得等10秒再出发(重新计时)。

JS函数防抖:

js 复制代码
function debounce(fn, wait) {
  var timer = null;
  return function () {
      var context = this
      var args = arguments
      if (timer) {
          clearTimeout(timer);
          timer = null;
      }
      timer = setTimeout(function () {
          fn.apply(context, args)
      }, wait)
  }
}

var fn = function () {
  console.log('boom')
}

setInterval(debounce(fn,500),1000) // 第一次在1500ms后触发,之后每1000ms触发一次

setInterval(debounce(fn,2000),1000) // 不会触发一次(我把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)

函数节流(throttle)

概念: 规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

生活中的实例: 我们知道目前的一种说法是当 1 秒内连续播放 24 张以上的图片时,在人眼的视觉中就会形成一个连贯的动画,所以在电影的播放(以前是,现在不知道)中基本是以每秒 24 张的速度播放的,为什么不 100 张或更多是因为 24 张就可以满足人类视觉需求的时候,100 张就会显得很浪费资源。

JS函数节流:

js 复制代码
function throttle(fn, gapTime) {
  let _lastTime = null;

  return function () {
    let _nowTime = + new Date()
    if (_nowTime - _lastTime > gapTime || !_lastTime) {
      fn();
      _lastTime = _nowTime
    }
  }
}

let fn = ()=>{
  console.log('boom')
}

setInterval(throttle(fn,1000),10)

在使用上面的函数时我们要对原有的函数进行调整,增加业务逻辑,改动起来比较麻烦。

使用装饰器封装函数防抖和函数节流,代码如下:

装饰器-函数防抖

js 复制代码
/**
 * 函数防抖
 * @export
 * @param {*} delay
 * @returns {(target, name, descriptor) => any}
 */
export function debounce(delay) {
  let timer: any = null;
  return function (target, name, descriptor) {
    const originFn = descriptor.value;
    descriptor.value = function (...args) {
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(() => {
        originFn.apply(this, args);
      }, delay);
    }
    return descriptor;
  }
}

装饰器-函数节流

js 复制代码
/**
 * 函数节流
 * @param {function} fn 函数
 * @param {number} delay 延迟执行毫秒数
 * @returns {(target: any, name: string, descriptor: PropertyDescriptor) => any}
 */
export function throttle(delay) {
    let timer: any = null;
    return function (target, name, descriptor) {
      const originFn = descriptor.value;
      descriptor.value = function (...args) {
        if (timer) {
            return;
        }
        timer = setTimeout(() => {
          originFn.apply(this, args);
          timer = undefined;
        }, delay);
      }
      return descriptor;
    }
}

使用方式比较简单,在需要处理的函数上添加上装饰器就行。

js 复制代码
// 函数防抖
@debounce(100)
init(){
    ......
}

// 函数节流
@throttle(100)
init(){
    ......
}

这样在使用时不会影响到原始的函数,代码也不需要进行调整。完美!!

加个关注,不迷路

相关推荐
JNU freshman9 分钟前
vue 技巧与易错
前端·javascript·vue.js
Asort17 分钟前
JavaScript设计模式(十六)——迭代器模式:优雅遍历数据的艺术
前端·javascript·设计模式
我是日安26 分钟前
从零到一打造 Vue3 响应式系统 Day 28 - shallowRef、shallowReactive
前端·javascript·vue.js
开源之眼27 分钟前
深入理解 JavaScript 报错:TypeError: undefined is not a function
前端·javascript
却尘32 分钟前
当你敲下 `pnpm run dev`,这台机器到底在背后干了什么?
前端·javascript·面试
歪歪10032 分钟前
React Native开发有哪些优势和劣势?
服务器·前端·javascript·react native·react.js·前端框架
一个很帅的帅哥42 分钟前
伪类选择器和伪元素选择器
javascript
Glommer1 小时前
某红书 Js 逆向思路
javascript·逆向
Mintopia1 小时前
🧠 可解释性AIGC:Web场景下模型决策透明化的技术路径
前端·javascript·aigc
Mintopia1 小时前
⚙️ Next.js 事务与批量操作:让异步的世界井然有序
前端·javascript·全栈