1.手写函数柯里化(function currying)

201 阅读2分钟

基本概念

函数柯里化(function currying):创建已经设置好一个或多个参数的函数。

函数的柯里化的基本方法和函数绑定是一样的:使用一个闭包 并 return一个函数。

两者的区别:当函数被调用时,返回的函数还需要设置一些传入的参数。

function add(num1, num2) {
    return num1 + num2;
}

function curryAdd(num2) {
    return add(5, num2);
}

console.log(add(2, 3)); // 5
console.log(curryAdd(3)); // 8

上面代码定义了两个函数,add()和curryAdd()。后者本质上是在任何情况下第一个参数为5的add()版本。尽管技术上来讲,curryAdd()并非柯里化的函数,却很好的展示了柯里化的概念。

手写代码

原生:

function curry(fn) {
    //1.一个闭包
    //截取内置对象arguments第二位以后的参数, 第一位参数是fn
    var args = Array.prototype.slice.call(arguments, 1);
    //2.return一个函数
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    }
}

//使用实例

function add(num1, num2) {
    return num1 + num2;
}

/**
curry(add, 5): arguments[0] = fn = add, argumens[1] = 5
args = [5];
curryAdd: return function() { ... }
*/
var curryAdd = curry(add, 5);

/**
curryAdd(2, 3): innerArgs = [2 ,3]
finalArgs = [5, 2, 3]
*/
console.log(curryAdd(2, 3)); //7, 因为add只接收2个参数,所以5,2入参了,3没有入参

//或者一次性给出所有参数
var curryAddAll = curry(add, 5, 12);
console.log(curryAddAll()); // 17

ES6:

//使用了destructuring代替了箭头函数没有arguments内置对象
function currying(fn, ...args) {
  //一个闭包
  let outerArgs = [...args];
  const returnFn = (...newArgs) => {
    const finalArgs = [...outerArgs, ...newArgs];    return fn(...finalArgs);  };
  //返回一个函数
  return returnFn;}

// 用法如下:
const add3 = (a, b, c) => a + b + c;
const curryAdd3 = currying(add3, 1);
console.log(curryAdd3(2,3)); // 6

使用柯里化手写其他函数

防抖函数

原理:触发事件,但是在事件触发 n 秒后才执行,如果在一个事件触发的 n 秒内又触发了这个事件,就以新的事件的时间为准,n 秒后才执行,总之,就是要等触发完事件 n 秒内不再触发事件,才执行实际的函数。

触发时间以最后一次触发为准。

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>防抖</title>
    <meta charset="utf-8" />
  </head>
  <body>
    <button id="submit-btn">提交</button>
  </body>
  <script>
    function submit() {
      console.log("submit");
    }
    
    /**
    1. 创建一个setTimeout()的闭包变量timer
    2. return 一个函数
    3. return函数内,如果setTimeout()未清空,就clearTimeout()
    4. timer = setTimeout(() => {...})
    */
    const debounce = (fn, delay) => {
      let timer = null; //一个闭包
      return (...args) => { //return 一个函数
        if (timerId) {
          window.clearTimeout(timer);
        }
        timer = setTimeout(() => {
          fn.apply(this, args);
          timer = null;
        }, delay);
      };
    };

    const btnDebounce = debounce(submit, 1000);

    const btn = document.getElementById("submit-btn");
    btn.addEventListener("click", btnDebounce);
  </script>
</html>

节流函数

原理:连续触发事件,在时间间隔内,只执行一次事件。触发时间以第一次触发为准。

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>节流</title>
    <meta charset="utf-8" />
  </head>
  <body>
    <button id="submit-btn">提交</button>
  </body>
  <script>
    function submit() {
      console.log("submit");
    }
    
    /**
    
    */
    const throttle = (fn, delay) => {
      let canUse = true; //设置一个是否可用的闭包flag
      return (...args) => {
        if (canUse) {
          fn.apply(this, args); //立即执行这个函数
          canUse = false;
          setTimeout(() => (canUse = true), delay); //在delay毫秒后,再次可用
        }
      };
    };

    // 间隔设为3秒,效果比较明显
    const btnThrottle = throttle(submit, 3000);

    const btn = document.getElementById("submit-btn");
    btn.addEventListener("click", btnThrottle);
  </script>
</html>