Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

函数类型(高阶函数) #23

Open
adodo0829 opened this issue Apr 5, 2020 · 0 comments
Open

函数类型(高阶函数) #23

adodo0829 opened this issue Apr 5, 2020 · 0 comments

Comments

@adodo0829
Copy link
Owner

函数类型(高阶函数)

函数操作函数 => 就是高阶函数; 函数有时候我们理解为一系列规则的集合, 指令集.

# 通常为两种情况
1.函数作为参数
2.函数作为返回值

函数作为参数的场景

  • 回调函数callback
当把函数当作参数传递时, 我们可以抽离出一部分容易变化的业务逻辑(规则), 
把这部分业务逻辑放在函数参数中, 这样一来可以分离业务代码中变化与不变的部分,
做到规则的抽象与分离.

doSomething(cb) {
  // do something...
  // 不变的业务逻辑

  // then
  // 可变的业务逻辑, 可支持自定义
  cb()
}
# 典型的有 ajax 回调, 事件操作等

函数作为返回值的场景

  • 抽离重复功能
当把函数当返回值输出时, 意味着我们可以延续上一步的运算;
这样我们可以抽离出重复的的业务逻辑(规则), 做到简化代码的作用

let isString = function(obj) {
  return Object.prototype.toString.call(obj) === '[object String]';
};
let isArray = function( obj ){
  return Object.prototype.toString.call(obj) === '[object Array]';
};
let isNumber = function( obj ){
  return Object.prototype.toString.call(obj) === '[object Number]';
};
// 这些函数都有相同的部分, 那就是最后都调用Object.prototype.toString.call( obj )
// 我们抽离一下

let isType = function(type){ 
  return function(obj){
    return Object.prototype.toString.call(obj) === '[object '+ type +']';
  }
};
// 之后你可以isType('你想要的判断类型的规则')

高阶函数应用

对于 js 这类语言来说, 我其实比较喜欢且倾向函数式编程, 奈何功底有限,还是得慢慢修炼

AOP(面向切面编程)

把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。把这些功能抽离出来之后,再通过“动态织入”的方式掺入业务逻辑模块中。这样做的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块

在js中实现AOP,都是指把一个函数“动态织入”到另外一个函数之中

Function.prototype.before = function (beforefn) {
    var _this = this;    // 保存原函数的引用
    return function () {    // 返回包含了原函数和新函数的"代理"函数 
      beforefn.apply(this, arguments);    // 先执行新函数,修正this 
      return _this.apply(this, arguments);    // 再执行原函数
    }
  };
Function.prototype.after = function (afterfn) {
    var _this = this;
    return function () {
      var ret = _this.apply(this, arguments); //先执行原函数
      afterfn.apply(this, arguments); //再执行新函数
      return ret;
    }
  };
var func = function () {
    console.log(2);
  };
func = func.before(function () {
    console.log(1);
  }).after(function () {
    console.log(3);
  });

func(); // 1, 2, 3
# 把负责打印数字1和打印数字3的两个函数通过AOP的方式动态植入func函数

一些工具函数

  • 映射函数:返回的新函数将一个数组映射到另一个使用这个函数的数组上
function mapFunc(fn) {
  return function(arr) {
    return Array.prototype.map.call(arr, fn)
  }
}
let increase = item => item + 1
let increaseMap = mapFunc(increase)
let a = [1,2,3]
increaseMap(a) // [2,3,4]

函数柯里化(currying)

currying又称部分求值函数。一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数(单个参数),刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值.

其主要用途在于参数复用。本质上是降低通用性,提高适用性。

  • 计算一周的消费,只需要在周末求和
# 理解: 每天调用该函数,保存累计值,最后一次调用时,求和输出; 我们可以判断参数
let everyWeekCost = (function() {
  // 闭包中用于保存数据的变量,本周每天的费用
  var feeArgs = []
  return function() {
    // 此函数才是最终执行的函数
    if (arguments.length === 0) {
      return feeArgs.reduce((prev, curr) => {
        return prev + curr
      }, 0)
    } else {
      Array.prototype.push.apply(feeArgs, arguments)
    }
  }
})()

everyWeekCost(1)
everyWeekCost(2)
everyWeekCost(3)
...
everyWeekCost(7)
everyWeekCost() // 1 + 2 + 3...+ 7的结果
  • 实现 add(1)(2)(3)() ==> 6
# 只要传了参数,就返回一个函数,并保存参数, 没有参数了就返回和值

function curryAdd() {
  let argsList = []
  // 返回一个闭包
  let closure = function () {
    // 本次调用传入的参数
    let args = [...arguments]
    if (args.length > 0) {
      // 保存参数
      argsList = argsList.concat(args)
      // 再次返回闭包,等待下次调用
      return closure
    }
    // 没有传递参数,执行累加
    return argsList.reduce((prev, cur) => prev + cur)
  }
  return closure
}

let add = curryAdd()
console.log(add(1)(2)(3)())
# 还有一种是利用函数对象的 toString 方法
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant