[字节青训营]前端方向Day7-前端入门 - 基础语言篇 - 如何写好JavaScript-过程抽象 | 豆包MarsCode AI刷题

46 阅读4分钟

过程抽象

[源码]:code.juejin.cn/pen/7108192…

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

把function本身看作是一个封装好的过程,关注输入和输出。

image.png

  • eg : 多次调用但是只执行一次

    • 创建一个按钮,按钮的功能是移除一条li。setTimeout为2秒。

    • 当点击按钮的时候,li已经被移除了,但是由于还有2s的渐退时间,会导致按钮在这两秒之内还存在,li不存在的情况。

    • 我们点击按钮多次,但是删除只能执行一次,一次之后就没有能删除的东西了。(所以就会报错)

image.png

const list = document.querySelector('ul');
const buttons = list.querySelectorAll('button');
buttons.forEach((button) => {
  button.addEventListener('click', (evt) => {
    const target = evt.target;
    target.parentNode.className = 'completed';
    setTimeout(() => {
      list.removeChild(target.parentNode);
    }, 2000);
  });
});
  • 第一种解决办法:

    我们可以在监听按钮click事件的时候,加入{once : true},这样多次点击按钮之后就不会报错了。

    button.addEventListener('click', (evt) => {
        const target = evt.target;
        target.parentNode.className = 'completed';
        setTimeout(() => {
          list.removeChild(target.parentNode);
        }, 2000);
      },{once:true});
    
  • 第二种解决办法:

    我们可以把过程封装成once过程抽象(高阶函数)

once高阶函数保证内部函数只执行一次。

const foo = once(() => {
  console.log('bar');
});
​
foo();
foo();
foo(); // 调用三次但是只在控制台输出一个bar

once返回一个function,如果一个函数返回另一个函数,那么这个函数叫做高阶函数

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

once 函数的目的是返回一个新函数,这个新函数只能被调用一次。

  1. 输入:

    • once(fn) 接受一个函数 fn 作为参数。
  2. 返回值:

    • once(fn) 返回一个新的函数,这个新函数会在第一次调用时执行 fn,并且之后不再执行 fn
  3. 内部逻辑:

    • 新函数使用 fn.apply(this, args) 调用原函数 fn,并将参数 args 传递给它。
    • this 在这一行是指原函数 fn 被调用时的上下文。
    • 调用后,fn 被设置为 null,使得之后的调用没有任何作用,因为没有函数可以被执行。
  4. 执行过程:

    • 第一次调用新函数时,fn 会被执行,并且执行后会将 fn 置为 null
    • 第二次及之后的调用将不做任何事情。

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

比如说开门,开冰箱,都是开,我们就能抽象出来 open。

在JS中,我们可以使用高阶函数来实现过程抽象。

  • 高阶函数(HOF)

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

image.png

  • 常用高阶函数

    • Once

    • Throttle 节流函数 鼠标移动,页面滑动的时候会触发很多监听事件,我们可以使用节流函数,限制多长时间监听一次。

      [源码] code.juejin.cn/pen/7108192…

image.png

  • Debounce 防抖函数 要实现一个自动保存的功能,如果每次都保存,会对服务器造成一个很大的负担,当我编辑完成之后,键盘不敲的时候,再帮我进行自动保存。超过多少毫秒没有动,再进行自动保存

    [源码] code.juejin.cn/pen/7108193…

    注:等待 dur 毫秒后调用 fn 函数。setTimeout 的作用是设置一个定时器,等待指定的时间 dur 毫秒后再执行一个函数。

image.png

image.png

[源码] code.juejin.cn/pen/7108194…

  • Iterative 迭代器

[源码] code.juejin.cn/pen/7108194…

image.png

  • 为什么要使用高阶函数?

    一般来说,高阶函数都是纯函数。

    • 纯函数:没有副作用,并且结果是可预测的。

      // add是一个纯函数,我们可以知道结果是否符合预期
      function add(x,y){
          return x + y;
      }
      ​
      let idx = 0;
      // inpure function with side effect 测试成本高,越多系统可维护性越差
      function count(){
          return ++idx;
      }
      count()
      count()
      const result = count()
      // 如果我们进行对count函数的测试,会受到其上文的影响,
      // 所以在对该函数进行测试时,
      // 需要先 init/setup()前面环境初始化,
      // 然后teardown()结束的时候也需要把环境销毁掉,因为要测试别的内容。
      console.assert(result === 3,'failed ${result}');
      ​
      // 显然是一个非纯函数,因为会改变el的状态
      function setColor(el,color){
          el.style.color = color;
      }
      ​
      setColor(app,'red');
      ​
      // 也是一个非纯函数
      function setColors(els,color){
          els.forEach(el) =>{
              setColor(el,color);   
          }
      }
      ​
      // 如果对上述两个代码都进行测试的话,都需要写test case。如果用Iterative 
      
    • 可以大大减少使用非纯函数的可能性。

  • 编程范式:

    • 命令式:面向过程,面向对象,怎么做
    • 声明式:描述的是想要的结果,而不需要指定具体的步骤或执行过程,做什么

image.png

image.png

命令式code.juejin.cn/pen/7108195…

用到了 if else

image.png

声明式code.juejin.cn/pen/7108195…

shift() 是从actions里面取出来,push是放到最后一个。也就是取出来现在的,放在最后一个。

写成声明式的话,添加或者减少都很简单。

image.png

声明式比命令式语言天然的具有更强的可扩展性。