如何写好JavaScript(3) —— 过程抽象 | 青训营笔记

116 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第6天

本文主要介绍写好JS的第三个重要方面——过程抽象。过程抽象可以提升系统的可维护性,同时简化代码的额外处理逻辑,减少逻辑陷阱。

概念解析

什么是过程抽象

为了能够让“只执行一次“的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象

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

什么是高阶函数

高阶函数(HOF)是对其他函数进行操作的函数,操作可以是将它们作为参数,或者是返回它们。 简单来说,高阶函数是一个接收函数作为参数或将函数作为输出返回的函数。

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于作为函数装饰器

高阶函数的返回函数恰好可以对其进行包装,承载抽象出来的过程及连接原来函数的参数和作用域,帮助实现过程抽象

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

常见的高阶函数

Once

单次执行(once),我们有时会遇到事件处理函数只能执行一次的情况,此时就是once函数发挥作用的时候了。

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

Throttle

节流函数(throttle),就是用于防止频繁的执行操作,限制执行的频率,(比如向服务器发送数据)。在某段时间内,不管你触发了多少次回调,都只认第一次,并在计时结束时给予响应。

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

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

节流应用场景

  1. 按钮点击事件
  2. 拖拽事件
  3. onScoll
  4. 计算鼠标移动的距离(mousemove)

debounce

防抖函数(debounce),实现的是取消(防止)多余的执行操作,只保留最后一次的执行。也就是如果事件(极短的时间内)连续触发多次,防抖函数可以防止多次(重复无用的)执行,只保留最后一次的操作。

比如下面代码示例,我们想让屏幕上的小鸟跟随我们鼠标移动的方向进行移动,但是又不希望在鼠标乱晃移动的过程中同样跟随,而是希望它在鼠标移动结束处于稳定状态时,向着鼠标最终所在位置进行移动,这时防抖函数(debounce)就可以派上用场了。

var i = 0;
setInterval(function(){
  bird.className = "sprite " + 'bird' + ((i++) % 3);
}, 1000/10);

function debounce(fn, dur){
  dur = dur || 100;
  var timer;
  return function(){
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, dur);
  }
}

document.addEventListener('mousemove', debounce(function(evt){
  var x = evt.clientX,
      y = evt.clientY,
      x0 = bird.offsetLeft,
      y0 = bird.offsetTop;
  
  console.log(x, y);
  
  var a1 = new Animator(1000, function(ep){
    bird.style.top = y0 + ep * (y - y0) + 'px';
    bird.style.left = x0 + ep * (x - x0) + 'px';
  }, p => p * p);
  
  a1.animate();
}, 100));

防抖应用场景

  1. 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
  2. 表单验证
  3. 按钮提交事件。
  4. 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。

Thinking

image.png

为什么要使用高阶函数?

使用函数式编程思想中的高阶函数能够设计出简单可靠的API,这些高阶的API根据确定参数返回确定的函数,它们依然是纯函数,使得它们对简化系统,提升可扩展性和可维护性都有着非常大的帮助,同时高阶函数可以任意组合,形成强大的功能。

编程范式

命令式与声明式

image.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 / 装饰器
  • 命令式 / 声明式
image.png