高阶函数 + 柯里化【JS深入知识汇点4】
系列文章:
什么式函数式编程
函数是一种描述集合和集合之间的转换关系,输入通过函数都会返回有且只有一个输出值。
函数式编程(Functional Programming)就是强调在编程过程中,把更多的关注点放在如何去构建关系。函数式编程大多时候都是在声明我需要什么,而非怎么做。
函数式编程的特点:
- 函数是“一等公民”:这是函数式编程得以实现的前提,因为我们基本都在操作函数。函数和其他变量一样,可以作为其他函数的输入和输出。
- 惰性执行:函数只在需要的时候执行,即不产生无意义的中间变量
- 无状态:主要是强调对于一个函数,不管何时运行,它都像第一次运行一样,给定相同的输入,给出相同的输出,完全不依赖外部状态的变化
- 数据不可变:所有的数据都是不可变的,意味着如果想改一个对象,就应该先创建一个新的对象用来修改。
- 纯函数:就是没有副作用的函数
函数式编程的优劣势
优势:
- 更好的状态管理,能最大化的减少未知,优化代码,减少出错的情况
- 更简单的复用:把过程逻辑以纯函数来实现,这样代码复用起来,完全不考虑它的内部实现和外部影响
- 更优雅的组合
劣势:
- 提高了代码编写成本
- 跟过程式编程相比,它没有提高性能,反而可能会降低性能
- 代码不易读
- 学习成本高
高阶函数
What‘s Higher-order function(高阶函数)
至少满足下列一个条件的函数:
- 接受一个或多个函数作为输入
- 输出一个函数
tip:为什么js中,函数是一等公民呢?这是因为:js中,函数是一种特殊的对象,可以将他们作为参数传递给另外的函数,所以称为一等公民
内置高阶函数 => 不使用高阶函数的方案
Array.prototype.map
const arr = [1, 3, 5]
const arr1 = arr.map(item => item * 2)
//不使用高阶函数
let arr2 = [];
for(let i = 0; i < arr.length; i++) {
arr2.push(arr[i] * 2)
}
Array.prototype.filter
const arr = [1, 3, 5]
const arr1 = arr.filter(item => item > 2)
//不使用高阶函数
let arr2 = [];
for(let i = 0; i < arr.length; i++) {
(arr[i] > 2) && arr2.push(arr[i])
}
柯里化
What‘s Currying(柯里化)
柯里化,又称部分求值,只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。把接收多个的参数的函数变换成接受一个单一参数的函数,并返回接受余下的参数并且返回结果的新函数。
柯里化作用
- 参数复用:缓存参数到闭包内部参数,然后在函数内部将缓存
- 提前返回
- 延迟计算/运行:不断的柯里化,累积传入的参数,最后执行。
偏函数
偏函数就是固定函数的某一个或几个参数,返回一个新函数,接收剩下的参数,
function partial(fn) {
var args = [].slice.call(arguments, 1)
return function() {
var newArgs = args.concat([].slice.call(arguments))
return fn.apply(this, newArgs)
}
}
难题解析:
Q1:写一个函数实现以下功能:
add(1); // 1
add(1)(2); // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10
function add(x) {
var sum = x;
function temp(y) {
sum = sum + y;
return temp;
}
temp.toString = function() {
return sum
}
return temp
}
Q2:写一个函数实现以下功能:
add(1)(); // 1
add(1)(2)(); // 3
add(1)(2)(3)(); // 6
add(1)(2)(3)(4)(); // 10
function add(x) {
var sum = x;
return function temp(y) {
if(arguments.length === 0) {
return sum
} else {
sum += y;
return temp
}
}
}
Q3:写一个函数实现以下功能:
add(1, 2, 3) // 6
add(1, 2)(3) // 6
add(1)(2)(3) // 6
add(1)(2, 3) // 6
//思路就是:判断当前参数长度够不够,
function currying(fn, length) {
// 第一次等于 fn 的参数长度,后续等于 fn 剩余参数的长度
length = length || fn.length;
console.log(length)
return function(...args) {
// 新函数接受长度如果大于剩余参数的长度,执行 fn 函数,传入 新函数的参数
// 否则,递归 currying 函数,新的 fn 为绑定了新函数参数的新函数,length为fn剩余的参数
return args.length >= length ? fn.apply(this, args) : currying(fn.bind(this, ...args), length - args.length)
}
}
let add = currying(function(a, b, c) {
return a + b + c
});
Q4: 编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 10, [14] ] ] ], 10];
// 方法1
function flatArr(data) {
let result = [];
function iterate(arr) {
if (Array.isArray(arr)) {
arr.forEach(item => iterate(item))
} else {
(!result.includes(arr)) && result.push(arr)
}
}
iterate(data);
return result.sort((x, y) => x-y)
}
// 方法 2:
data.flat(Infinity).sort((x, y) => x - y).reduce((acc, cur) => {
if (!acc.includes(cur)) {
acc.push(cur)
}
return acc
},[])
// 方法 3:
Array.from(new Set(data.flat(Infinity))).sort((x, y) => x - y)
console.log(flatArr(arr))
转载自:https://juejin.cn/post/6844904150342303752