发布于 

高阶函数、声明式和命令式编程

高阶函数、命令式编程和声明式编程

什么是高阶函数?

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于函数装饰器
1
2
3
4
5
let HOFO = function (fn) {
return function (...args) {
return fn.apply(this, args);
};
};

高阶函数的好处

  • 高阶函数的魅力在于它的可重复利用性,如果不是高阶函数,mapfilterreduce 等强大的数组函数就不可能存在。
  • 使 JavaScript 适合函数式编程的原因是它接受高阶函数。

常用高阶函数示例

防抖和节流

节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

  • once

    • 让函数只执行一次,常用于节流

    • ```js
      let once = function (fn) {
      return function (…args) {

      if (fn) {
        setTimeout(() => {
          fn = null;
        });
        return fn.apply(this, args);
      }
      

      };
      };

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17

      - throttle

      - 让函数在执行后的一段时间不能被再次触发

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

    • 将多次触发的任务加入任务队列,依次间隔执行

    • ```js
      function consumer(fn, time){
      let tasks = [],

        timer;
      

      return function(…args){

      tasks.push(fn.bind(this, ...args));
      if(timer == null){
        timer = setInterval(() => {
          tasks.shift().call(this)
          if(tasks.length <= 0){
            clearInterval(timer);
            timer = null;
          }
        }, time)
      }
      

      }
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15

      #### 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

      - debounce

      - ```js
      let debounce = function (fn, time) {
      let timer;
      return function (...args) {
      clearTimeout(timer);
      timer = setTimeout(() => {
      return fn.apply(this, args);
      }, time);
      };
      };

非纯函数和纯函数(pure function)

什么是纯函数和非纯函数

  • 纯函数是指在给定输入的参数下,输出总是确定的,不依赖于外界环境

    • 例如:

      1
      2
      let add = (a, b) => a + b;
      //add返回的值只与输入参数有关
  • 非纯函数依赖上下文

    • 例如:

      1
      2
      3
      let count = 0;
      let add = () => count++;
      //add返回的值依赖于count的值

如果代码中非纯函数过多,代码会变得难以维护,所以我们要减少写出非纯函数

将非纯函数转化为纯函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const isIterable = obj => obj != null 
&& typeof obj[Symbol.iterator] === 'function';

function iterative(fn) {
return function(subject, ...rest) {
if(isIterable(subject)) {
const ret = [];
for(let obj of subject) {
ret.push(fn.apply(this, [obj, ...rest]));
}
return ret;
}
return fn.apply(this, [subject, ...rest]);
}
}

这里的iterative和isIterable都是纯函数,它们能方便地对可迭代对象批量执行函数

例如假如现在有一个setColor函数能改变元素的颜色,如果这时我们要对元素批量执行,再定义一个setColors的话就产生了两个非纯函数,而如果我们利用上面的iterative函数就可以直接将setColor函数传入其中,实现批量操作,减少了非纯函数的产生。

命令式和声明式编程

以改变一个按钮的状态为例

  • 命令式编程关注怎么做

    • ```js
      switcher.onclick = function(evt){
      if(evt.target.className === ‘on’){
      evt.target.className = 'off';
      
      }else{
      evt.target.className = 'on';
      
      }
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16

      - 声明式编程关注做什么

      - ```js
      function toggle(...actions){
      return function(...args){
      let action = actions.shift();
      actions.push(action);
      return action.apply(this, args);
      }
      }

      switcher.onclick = toggle(
      evt => evt.target.className = 'off',
      evt => evt.target.className = 'on'
      );

看起来区别不大但当这个按钮有三种状态时,在命令式编程下我们需要重写if-else的逻辑,而声明式编程下我们只需要在toggle里再多传入evt => evt.target.className = 'warn'就可以实现。


本站由 @Eureka 使用 Stellar 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。