闭包 + 回调 => JS 高阶函数 High-order function

高阶函数可以比普通函数处理更多问题,有更好的集成性和封装性。

闭包

MDN 是目前可以找到非常专业且总结性很强的针对 ECMA262 文件,针对开发者最全,也是最专业的一个技术文档集合。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

当一个函数被系统创建时,闭包就会被一同创建出来。一个函数统一与外界环境(作用域)捆绑在一起的这种现象叫做闭包。

理论上 test 就是一个闭包。

  1. function test () { }

global scope => 闭包,闭包函数。和外部环境以及应用捆绑在一起就形成闭包,并不是一定访问作用域中的内容。

  1. function test () {
  2. // fn test1 + test scope => 闭包,test1 就叫做闭包函数
  3. function test1 () {
  4. // closure
  5. }
  6. }
  1. function test () {
  2. var a = 1;
  3. // fn test1 + test scope => 闭包,test1 就叫做闭包函数
  4. function test1 () {
  5. // closure
  6. console.log(a);
  7. }
  8. }

没有形成闭包,function b 并没有与 test 环境捆绑在一起,并不是闭包函数。

  1. function b () {
  2. console.log(a);
  3. }
  4. function test () {
  5. var a = 1;
  6. b(a);
  7. }
  8. test();

函数内部声明函数,这其实就是一种封装性。

  1. function compute () {
  2. function plus (a, b) {
  3. return a + b;
  4. }
  5. function minus (a, b) {
  6. return a - b;
  7. }
  8. function mul (a, b) {
  9. return a * b;
  10. }
  11. function div (a,b ) {
  12. return a / b;
  13. }
  14. return function (type, a, b) {
  15. switch (type) {
  16. case 'plus':
  17. return plus(a, b);
  18. case 'minus':
  19. return minus(a, b);
  20. case 'mul':
  21. return mul(a, b);
  22. case 'div':
  23. return div(a, b);
  24. default:
  25. break;
  26. }
  27. }
  28. }
  29. console.log(compute()('mul', 100, 200));

=>

  1. function compute () {
  2. compute.plus = function (a, b) {
  3. return a + b;
  4. }
  5. compute.minus = function (a, b) {
  6. return a - b;
  7. }
  8. compute.mul = function (a, b) {
  9. return a * b;
  10. }
  11. compute.div = function (a, b) {
  12. return a / b;
  13. }
  14. return function (type, a, b) {
  15. return compute[type](a, b);
  16. }
  17. }
  18. console.log(compute()('mul', 100, 200));

=>

  1. function compute (type, a, b) {
  2. compute.plus = function (a, b) {
  3. return a + b;
  4. }
  5. compute.minus = function (a, b) {
  6. return a - b;
  7. }
  8. compute.mul = function (a, b) {
  9. return a * b;
  10. }
  11. compute.div = function (a, b) {
  12. return a / b;
  13. }
  14. return compute[type](a, b);
  15. }
  16. console.log(compute('mul', 100, 200));

闭包可以来做数据结构化,比如请求一组数组,在函数中声明其他函数进行数据处理,再返回处理后数据。

  1. class Compute {
  2. constructor () {
  3. this.a = 100;
  4. }
  5. add (b) {
  6. return this.a + b;
  7. }
  8. minus (b) {
  9. return this.a - b;
  10. }
  11. }
  12. const compute = new Compute();
  13. console.log(compute.add(10));

上述代码存在一个问题,a 是内部变量,我们仍然可以访问。我们可以使用闭包解决此问题。

  1. const Compute = (function () {
  2. const a = 100;
  3. return class Compute {
  4. add (b) {
  5. return a + b;
  6. }
  7. minus (b) {
  8. return a - b;
  9. }
  10. }
  11. })();
  12. const compute = new Compute();
  13. console.log(compute.add(10));

闭包缓存内部变量。不会污染全局,每个实例都存在自己私有属性。集成性、封装性。

  1. function test () {
  2. let a = 1;
  3. function b () {
  4. return ++a;
  5. }
  6. return b;
  7. }
  8. const b = test();
  9. console.log(b()); // 2
  10. console.log(b()); // 3
  11. console.log(b()); // 4

闭包应用案例,JS function API。

  1. function compute () {
  2. const base = 1000;
  3. return {
  4. plus: function (a, b) {
  5. return base + a + b;
  6. },
  7. minus: function (a, b) {
  8. return base - a - b;
  9. },
  10. mul: function (a, b) {
  11. return base * a * b;
  12. },
  13. div: function (a, b) {
  14. return base / a / b;
  15. }
  16. }
  17. }
  18. const comp = compute();
  19. console.log(comp.plus(1, 2));

回调

回调,callback。回应,通讯的回应。

call 调用、行为 function

trigger 触发,触发事件 event

event 通过一个程序或者交互触发,并且执行相应的处理程序。

box => user => click => box (点击事件 trigger)
event and function => bind
event => trigger => function => call

box.addEventListener('click', handleClick, false);

// 浏览器 => event => defined
// box[click, mouse] => click => handler =>
// click => trigger => handler(function)

// 绑定的是一个事件处理函数,而不是一个事件。这叫做 function call,event trigger。
function a () {
  // a call => task1
  // other function

  // todo task 1;

  function b () {
    // continue task 1
    // todo...
    // task 1 is finished
  }

  b();
}

a();
// task 1 finished

=> 回调

function a (callback) {
  // todo task 1;

  // var res = task 1;

  callback && callback(res);
}

a(function (res) {
  // get res
  // go on doing task1
})

// a 函数是一个封装,通过回调拿到封装后的结果,继续完成业务

// task 1 finished

vue onMounted,就是使用回调的方式。

onMounted(() => {
  // todo
});

function onMounted (callback) {
  // todo ...

  callback && callback();
}

react useEffect,hooks API,也是使用回调的方式。

useEffect(() => {}, []);

回调函数可以在不影响代码执行顺序的前提下,处理异步 API。

下面函数完成两个任务,返回值,执行回调。

function test (count, callback) {
  const newCount = count + 1;

  const callbackCount = count + 100;

  callback && callback(callbackCount);

  return newCount;
}


const count = test(123, function (newCount) {
  console.log(newCount);
});

console.log(count);

闭包 + 回调函数 打印日志。

function Compute (callback) {
  return function (a, b, type) {
    let ans = 0;

    switch (type) {
      case '+':
        ans = a + b;
        break;
      case '-':
        ans = a - b;
        break;
      default:
        break;
    }


    callback && callback({ a, b, type, ans });
  }
}

const compute = Compute(({ a, b, type, ans }) => {
  console.log(`
    ${ a } ${ type } ${ b } = ${ ans }
   `);
});

compute(2, 1, '+');

// 2 + 1 = 3

回调 + 闭包,验证数据规范。

function compute (validtor) {
  return function (a, b, type) {
    const { isError, errorMsg } = validtor(a, b);

    if (isError) {
      throw new Error(errorMsg);
    }

    switch (type) {
      case '+':
        return a + b;
        break;
      case '-':
        return a - b;
        break;
      default:
        break;
    }
  }
}

const comp = compute(validtor);

console.log(comp(1, 31, '+'));


function validtor (a, b) {
  if (a > 50 || b < 30) {
    return {
      isError: true,
      errorMsg: 'a 必须小于等于 50,并且 b 大于等于 30'
    }
  }

  return {
    isError: false,
    errorMsg: 'ok'
  }
}