JavaScript 编码原则之过程抽象|青训营

69 阅读4分钟

JavaScript 编码原则之过程抽象

过程抽象

  • 用来处理局部细节控制的一些方法
  • 函数式编程思想的基础应用

例子

这段代码是一个用于限制函数只能执行一次的工具函数 once,以及该函数的使用示例。

首先是 once 函数的定义:

function once(fn) {
  return function(...args) {
    if(fn) {
      const ret = fn.apply(this, args);
      fn = null;
      return ret;
    }
  }
}

该函数接受一个函数作为参数 fn,然后返回一个新的函数。这个新函数可以接受任意数量的参数 ...args。当调用这个新函数时,它会执行传入的 fn 函数,并将 fn 设置为 null,以确保 fn 只能被执行一次。

接下来是示例的代码:

const list = document.querySelector('ul');
const buttons = list.querySelectorAll('button');
buttons.forEach((button) => {
  button.addEventListener('click', once((evt) => {
    const target = evt.target;
    target.parentNode.className = 'completed';
    setTimeout(() => {
      list.removeChild(target.parentNode);
    }, 2000);
  }));
});

这部分代码首先使用 querySelector 获取页面中的 <ul> 元素,并使用 querySelectorAll 获取所有的 <button> 元素。然后通过 forEach 函数遍历每一个按钮,并为每个按钮添加一个 click 事件监听器。

事件监听器使用了 once 函数来包装一个回调函数 (evt) => { ... },该回调函数会在按钮被点击时执行。在回调函数中,首先获取到被点击的按钮的父元素,将它的 className 设置为 "completed",然后使用 setTimeout 函数延迟2秒后移除该父元素。

最后注释掉的部分是另一个使用 once 函数的示例,它定义了一个函数 foo,并尝试多次调用 foo 函数。由于 once 函数的限制,foo 实际上只会执行一次,后续的调用都不会有任何效果。

总结起来,这段代码实现了一个功能,通过 once 函数可以限制一个函数只能执行一次,而在示例中,它被用于在点击按钮后添加一些样式并延迟删除对应的元素。

操作次数限制

    - 一些异步交互
    - 一次性的HTTP请求
    

image.png

image (2).png

image (1).png

高阶函数

    - 以函数作为参数
    - 以函数作为返回值
    - 常用于作为函数装饰器 
    
为了能够让“只执行一次“的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为**过程抽象**。

HOF

     function HOF0(fn) {
            return function(...args) {
              return fn.apply(this, args);
            }
          }

常用高阶函数

HOF

首先是 throttle 函数的定义:

function throttle(fn, time = 500) {
  let timer;
  return function(...args) {
    if (timer == null) {
      fn.apply(this, args);
      timer = setTimeout(() => {
        timer = null;
      }, time);
    }
  };
}

该函数接受两个参数:fn 为需要节流的函数,time 为节流的时间间隔,默认为 500 毫秒。函数内部定义了一个定时器变量 timer,初始值为 undefined

返回的是一个新的函数,该函数可以接受任意数量的参数 ...args。在每次调用这个新函数时,会判断 timer 的值。如果 timernull 或者 undefined,则执行传入的 fn 函数并将 timer 设置为一个新的定时器。这样,在一段时间内重复调用这个新函数时,fn 函数只会执行一次。

接下来是示例的代码:

btn.onclick = throttle(function(e) {
  circle.innerHTML = parseInt(circle.innerHTML) + 1;
  circle.className = 'fade';
  setTimeout(() => (circle.className = ''), 250);
});

这部分代码给一个按钮 btn 添加了 click 事件的监听器,并且将 throttle 函数作为回调函数传入。

当按钮被点击时,回调函数会执行。在回调函数中,首先将 circle 元素的内部文本内容解析为数字,加1后重新赋值给 circle.innerHTML,这样每次点击按钮时数字会自增。

然后将 circleclassName 设置为 'fade',为其添加一个淡入的过渡效果。使用 setTimeout 函数延迟 250 毫秒后,将 circle.className 设置为空字符串,以移除淡入效果。

整个示例的作用是,当按钮被点击时,通过节流函数将点击事件限制在一个时间间隔内执行,以避免短时间内多次点击导致的过快响应。在回调函数中,对 circle 元素做了一些操作,包括数字增加、添加过渡效果等。

思考和讨论

    - 为什么要使用高阶函数?
    
  了解纯函数和非纯函数:纯函数,是指一个函数的返回值结果只依赖于它的参数,并且在执行过程里面没有副作用。
        - 纯函数方便进行单元测试;
        - 减少系统中非纯函数的数量,从而使得系统可维护性增加。

编程范式

命令式与声明式

image (3).png

//命令式(怎么做?)
          let list = [1, 2, 3, 4];
          let mapl = [];
          for(let i = 0; i < list.length; i++) {
            mapl.push(list[i] * 2);
          }
//声明式(做什么?) 
          let list = [1, 2, 3, 4];
          const double = x => x * 2;
          list.map(double);

例子

总结

    - 过程抽象 / HOF高阶函数 / 装饰器
    - 命令式 / 声明式