【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(){
    ......
}

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

加个关注,不迷路

相关推荐
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与1 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
前端郭德纲1 小时前
浏览器是加载ES6模块的?
javascript·算法
JerryXZR1 小时前
JavaScript核心编程 - 原型链 作用域 与 执行上下文
开发语言·javascript·原型模式
帅帅哥的兜兜1 小时前
CSS:导航栏三角箭头
javascript·css3
渗透测试老鸟-九青2 小时前
通过投毒Bingbot索引挖掘必应中的存储型XSS
服务器·前端·javascript·安全·web安全·缓存·xss
龙猫蓝图2 小时前
vue el-date-picker 日期选择器禁用失效问题
前端·javascript·vue.js
夜色呦2 小时前
掌握ECMAScript模块化:构建高效JavaScript应用
前端·javascript·ecmascript
peachSoda72 小时前
随手记:简单实现纯前端文件导出(XLSX)
前端·javascript·vue.js
大叔是90后大叔2 小时前
vue3中查找字典列表中某个元素的值对应的列表索引值
开发语言·前端·javascript