函数式编程

函数式编程FP

FP是编程范式之一,除此之外,还有面向过程编程,面向对象编程.

  • 面向对象编程的思维方式:把现实世界中的事物抽象成程序世界中的类和对象。通过封装,继承和多态来演示事务事件的联系。
  • 函数式编程的思维方式:把现实世界的事物和事物之间联系抽象倒程序世界,对运算过程进行抽象
    • 本质:根据输入通过某种计算获的相应的输出。
    • x->f (联系,映射) =>y ,即 y=>f(x)
    • 函数式编程中的函数不是指程序中的函数或者方法,而是数据中的映射关系,如y=sin(x),x和y的关系。
    • 相同的输入始终得到相同的输出(纯函数)
    • 函数式编程用来描述数据(函数)之间的映射。
例子:
//面向过程
const num1 = 1
const num2 = 2
const sum = num1 + num2

//函数式(事物和事物之间联系抽离)
function add(n1, n2){
return n1 + n2
}
add(1,2)

函数是一等公民

mdn:当一门编程语言的函数可以被当作变量一样用时,则称这门语言拥有头等函数。例如,在这门语言中,函数可以被当作参数传递给其他函数,可以作为另一个函数的返回值,还可以被赋值给一个变量。

  • 函数可以存储在变量中
  • 函数可以作为参数
  • 函数作为返回值
  • 在js中,函数就是一个普通的对象。

高阶函数

  • 可以把函数作为参数传递给另一个函数。
  • 可以把函数作为另一个函数的返回值。

如Array.prototype.forEach,map,等,都属于高阶函数。
实现

function forEach(array, fn){
	if(!Array.isArray(array) || typeof fn !== 'function'){
		throw new TypeErro('参数有误')
	}
	for(let i = 0; i< array.length; i++){
		fn(array[i])
}
}

function filter(array, fn){
	if(!Array.isArray(array) || typeof fn !== 'function'){
		throw new TypeErro('参数有误')
	}
	const arrs = []
	for(let i = 0; i< array.length; i++){
		if(fn(array[i])){
			arrs.push(array[i])
		}
	}
	return arrs
}

function check(array, fn) {
  if (!Array.isArray(array) || typeof fn !== "function") {
    throw new TypeErro("参数有误");
  }
}

function map(array, fn) {
  check(array, fn);

  const arrs = [];
  for (let i = 0; i < array.length; i++) {
    arrs.push(fn(array[i], i));
  }
  return arrs;
}

function some(array, fn) {
  check(array, fn);
  for (let i = 0; i < array.length; i++) {
    if (fn(array[i], i)) {
      return true;
    }
  }
  return false;
}

function every(array, fn) {
  check(array, fn);
  for (let i = 0; i < array.length; i++) {
    if (!fn(array[i], i)) {
      return false;
    }
  }
  return true;
}

function find(array, fn) {
  check(array, fn);
  for (let i = 0; i < array.length; i++) {
    if (fn(array[i], i)) {
      return array[i];
    }
  }
  return undefined;
}


实现loadsh的once

function once(fn){
	let i = false;
	return function(...args){
	if(i === false){
		i = true
		fn.apply(this, args)
	}
	}
 }

只执行一次。

高阶函数的意义
  • 抽象可以帮助我们屏蔽细节,我们只需要关注目标。
  • 高阶函数是用来抽象通用的问题

闭包

  • 闭包是指:函数执行的时候,产生一个不被释放的私有上下文,该上下文的变量可以被下一级上下文访问,这样一种可以保存+保护的机制,就是闭包。
  • 闭包的本质:函数在执行的时候会被放到一个执行栈上,当函数执行完毕之后,会从执行栈上移除,但是堆上的作用域,因为被外部引用不能被释放。因此内部函数依然可以访问外部函数的成员。

纯函数

  • 相同的输入永远会得到相同的输出。而且没有任何副作用
  • 纯函数就是类似数据中的函数,比如y=f(x),用来描述输入和输出之间的关系
  • 如数组的slice,就是纯函数。

优点:

  • 可缓存,因为纯函数对于相同的输入有相同的结果,所以可以缓存结果。只执行一次。
	
function memoize(f) {
  const cache = {};
  return function (...args) {
    const key = JSON.stringify(args);
    if (cache[key]) {
      return cache[key];
    }
    cache[key] = f.apply(f, args);
    return cache[key];
  };
}

副作用

  • 函数依赖于外部的状态,无法保证相同的输出,就会带来副作用。
  • 副作用来源:配置文件,数据库,获取用户输入。。。所有外部交互都可能产生副作用。虽然副作用有影响,但不可能完全禁止,尽量控制在可控范围内。

柯里化

经典柯里化loadsh的curry。

loadsh的_curry
```js
function curry(fn) {
  const count = fn.length;
  return function (...args) {
    if (args.length < count) {
      return function (...args2) {
        return fn.apply(this, [...args, ...args2]);
      };
    }
    return fn.apply(this, args);
  };
}


const test4 = function(a,v,c)  {return a+v+c}

const test4_  = curry(test4)
console.log(test4_(1,2)(4));
console.log(test4_(1,2,)(4));
console.log(test4_(1,2,4));
  • 柯里化可以让我们给一个函数传递较少的参数得到一个已经记住了某些固定参数的新函数。
  • 可以把多元函数转化为一元函数,组合使用使函数产生强大的功能。

函数组合

  • 纯函数和柯里化很容易写出洋葱代码,如f(t(s(3)))
  • 函数组合:如果要经过多个函数处理才能得到终值,这个时候可以把中间过程的函数合并成一个函数。函数就像数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果。
    如redux的compose
// compose
function compose(...fns) {
  if (fns.length === 1) {
    return fns[0];
  }

  return fns.reduce(
    (pre, item) =>
      (...args) =>
        pre(item(...args))
  );
}
const test1 = (n) => n + 1;
const test2 = (n) => n + 2;
const test3 = (n) => n + 3;
console.log(test1(test2(test3(4))));
console.log(compose(test1, test2, test3)(4));

默认从右往左处理,也就是从test4开始。

  • 函数组合需要满足结合律,如compose(f, compose(g,h)) === compose(g, compose(f,h))

Point Free

  • 把数据处理的过程定义成与数据无关的合成运算。不需要用到代表数据的那个参数,只要把简单的运算步骤合成到一起。
  • 不需要指明处理的数据
  • 只需要合成运算过程
  • 需要定义一些辅助的基本运算函数。

例子:

// 非 PointFree
function f(word){return word.toLowerCase().replace(/\s+/g, '_')}

// Point Free
const f = compose(fp.replace(/s+/g, '_'), fp.toLower)
f(word)

Functor函子

  • 容器:包含值和值的变形关系(这个变形关系就是函数)
  • 函子:是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理。

class Container {
  static of(value){
    return new Container(value)
  }
  constructor(value){
    this.value = value
  }
  map(fn){
    return Container.of(fn(this.value))
  }
}

console.log(Container.of(4).map(x=>x+1).map(x=>x*2));

MayBe函子

  • 对外部的空值情况做处理
class MayBe {
  static of(value) {
    return new Container(value);
  }
  constructor(value) {
    this.value = value;
  }
  map(fn) {
    return isNothing ? MayBe.of(null) : MayBe.of(fn(this.value));
  }
  isNothing() {
    return this.value === null || this.value === undefined;
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值