一些JavaScript高阶函数

  • 函数柯里化

    'use strict';
    
    function currying(fn) {
      const args = []; // 收集传入的参数
      const curred = function () {
        if (!arguments.length) return fn.apply(this, args); // 没有参数再指向
        Array.prototype.push.apply(args, arguments);
        return curred;
      };
      return curred;
    }
    
    const plus = (...args) => args.reduce((pre, cur) => pre + cur, 0);
    const plusCurred = currying(plus);
    
    plusCurred(1)(2)(3);
    plusCurred(4);
    
    console.log(plusCurred()); // 10
  • 将函数附加在 Function 上, 实现一步转为柯里化(骚操作)

    'use strict';
    
    Function.prototype.currying = function () {
      const fn = this;
      const args = []; // 收集传入的参数
      const curred = function () {
        if (!arguments.length) return fn.apply(this, args); // 没有参数再指向
        Array.prototype.push.apply(args, arguments);
        return curred;
      };
      return curred;
    };
    
    const plus = (...args) => args.reduce((pre, cur) => pre + cur, 0);
    const plusCurred = plus.currying();
    
    plusCurred(1)(2)(3);
    plusCurred(4);
    
    console.log(plusCurred()); // 10
  • 反柯里化

    'use strict';
    
    Function.prototype.unCurrying = function () {
      let self = this;
      return function () {
        return Function.prototype.call.apply(self, arguments);
      };
    };
    
    const push = Array.prototype.push.unCurrying();
    
    const t1 = [1, 2, 3];
    push(t1, 4);
    console.log(t1); // [ 1, 2, 3, 4 ]
    
    // 定义 length 的对象都可以用 Array.prototype.push
    const t2 = { length: 2 };
    Array.prototype.push.call(t2, 4);
    console.log(t2); // { '2': 4, length: 3 }
    
    // unCurrying 简化了调用
    const t3 = { length: 2 };
    push(t3, 4);
    console.log(t3);

  • 节流

    function throttle(fn, interval = 500) {
      let timer = null,
        first = true;
      return function () {
        if (first) return fn.apply(this, arguments); // 首次不卡
        if (timer) return false; // 在 interval 内还有其他函数在执行
        timer = setTimeout(() => {
          fn.bind(this, arguments);
          clearTimeout(timer);
          timer = null;
        }, interval);
      };
    }
  • 防抖

    function debounce(fn, wait) {
      var timer = null;
      return function () {
        var context = this,
          args = arguments;
        if (timer) {
          clearTimeout(timer);
          timer = null;
        }
        timer = setTimeout(() => {
          fn.apply(context, args);
        }, wait);
      };
    }
    // test
    var debounceRun = debounce(function () {
      console.log(123);
    }, 2000);
    window.addEventListener('mousemove', debounceRun);

    注意防抖不要被饿死(抖死), 尤其要防止与时间相关的元素打交道, 比如我设置了 500ms 的防抖, 结果有一个视频播放模块要调防抖, 这个视频播放模块每 24ms 就要执行一次, 直接抖死

  • 装饰器

    Function.prototype.before = function (fn) {
      const self = this;
      return function () {
        fn.apply(this, arguments);
        return self.apply(this, arguments);
      };
    };
    
    Function.prototype.after = function (fn) {
      const self = this;
      return function () {
        const res = self.apply(this, arguments);
        fn.apply(this, arguments);
        return res;
      };
    };
    
    const f1 = () => console.log(1);
    const f2 = () => console.log(2);
    
    const f = f1.after(f2).before(f2);
    f(); // 2 1 2