闭包、偏函数、高阶函数、函数柯里化、反柯里化

145 阅读3分钟

高阶函数

接收函数作为参数或将函数作为输出返回的函数(高阶函数是对其他函数进行操作的函数)

柯里化和反柯里化是高阶函数的一种应用,通俗的说,函数可以作为参数传递到函数中,这个作为参数的函数叫回调函数,而拥有这个参数的函数就是高阶函数,在高阶函数执行时,由于回调函数的内部逻辑不同,高阶函数的执行结果也不同,非常灵活,也被叫做函数式编程

// 比如:数组中的map方法,接收一个函数作为参数
let arr = [1, 2, 3];
let res = arr.map((item, index) => {
  return item + 1;
});
console.log(res); // [ 2, 3, 4 ]

函数柯里化

把一个n参数的函数,转化为n个单参数函数,即每一个函数只有一个参数(阮一峰对于柯里化和函数的合成的理解

// 普通函数
function getAddress(country, province, city){
  console.log(country + '-' + province + '-' + city);
}
getAddress('中国','湖北','武汉') // 中国-湖北-武汉
getAddress('中国','浙江','杭州') // 中国-浙江-杭州

// 柯里化函数
function getAddress(country) {
  return function (province) {
    return function (city) {
      console.log(country + "-" + province + "-" + city);
    };
  };
}
let test = getAddress('中国')
test('湖北')('武汉') // 中国-湖北-武汉
test('浙江')('杭州') // 中国-浙江-杭州

反柯里化

柯里化的过程是将函数拆分成功能更具体化的函数,反柯里化在于扩大函数的适用性,使本来作为特定对象所拥有的功能函数可以被任意对象所使用(一篇关于函数式编程的文章)

//反柯里化通用式应用
function uncurring(fn) {
  return function (...args) {
    console.log(args); // [ { name: 'Panda', age: 16 }, 'name', 'age' ]
    return fn.call(...args);
  };
}
// 构造函数 F
function F() {}

// 拼接属性值的方法
F.prototype.concatProps = function () {
  let args = Array.from(arguments);
  console.log(args); // [ 'name', 'age' ]
  return args.reduce((prev, next) => `${this[prev]}&${this[next]}`);
};

// 使用 concatProps 的对象
const obj = {
  name: "Panda",
  age: 16,
};/*  */

// 使用反柯里化进行转化
const concatProps = uncurring(F.prototype.concatProps);

let res = concatProps(obj, "name", "age"); // Panda&16
console.log(res); // Panda&16

图解:

image.png

偏应用函数

一个函数有多个参数,预先填充原始函数的部分参数,返回的函数接收剩余的参数

function getAddress(country) {
  return function (province,city) { // 余下的所以参数
    console.log(country + "-" + province + "-" + city);
  };
}

let test2 = getAddress("中国")
test2('湖北','武汉') // 中国-湖北-武汉
test2('浙江','杭州') // 中国-浙江-杭州

闭包

一个函数里面创建另一个函数,指有权访问另一个函数作用域中的变量的函数(MDN中闭包的解释

文章有过记录,请移步

递归函数

一个函数在内部调用自身的操作(MDN中递归的解释

求和

// 递归求和 复杂度:O(n)
function f(x) {
  if (x == 1) return 1;
  return x + f(x-1);
}
console.log(f(5)); // 15

// 尾递归求和 复杂度:O(1)
function f(n, total = 1) {
  if (n == 1) return total;
  return f(n - 1, n + total);
}
console.log(f(5)); // 15

求数组和

// 尾递归求数组和
function sum(arr, total) {
  if (arr.length == 0) {
    return total;
  }
  return sum(arr, total + arr.shift());
}
console.log(sum([10, 20, 30], 0)); // 60

// 递归求数组和
function sum(arr) {
  function f(i) {
    if (i < arr.length) {
      return f(i + 1) + arr[i]; // 递归调用
    } else {
      console.log(i, "终止条件"); // 3 终止条件
      return 0;
    }
  }
  return f(0);
}
console.log(sum([10, 20, 30])); //60

数组扁平化

const flatten = (arr) => {
  let newArr = [];
  arr.forEach((item) => {
    if (Array.isArray(item)) {
      newArr = newArr.concat(flatten(item));
      // 最小单元想象成这种数据结构:[3, [4, 5]]去走里面的if-else条件判断
    } else {
      newArr.push(item);
    }
  });
  return newArr;
};
const arr = [1, [2, [3, [4, 5]]], 6];
console.log(flatten(arr)); // [ 1, 2, 3, 4, 5, 6 ]

如果不好理解,通过debugger可以发现如下规律:
// arr的变化
[1, [2, [3, [4, 5]]], 6]
[2, [3, [4, 5]]]
[3, [4, 5]]
[4, 5]

// newArr的变化
item push成 [4, 5]
item concat成 [3].concat([4, 5]) => [3, 4, 5]
item concat成 [2].concat([3, 4, 5]) => [2, 3, 4, 5]
item concat成 [1].concat([2, 3, 4, 5]) => [1, 2, 3, 4, 5]