当前位置: 首页 > 工具软件 > Frisby.js > 使用案例 >

JavaScript 函数式编程思想

陆文康
2023-12-01

来自 Professor Frisby’s Mostly Adequate Guide to Functional Programming
英文版本
中文版本-版本较老

函数式编程是一种编程范式, 所谓范式, 就是一种编程规范

  • 面向对象: 将现实世界的事物抽象为类与对象, 通过封装, 继承, 多态来表示事物之间的联系
  • 函数式: 将现实世界事物与事物之间的联系抽象到程序中

    这里的函数指的是映射 形如 y = x 2 y = x^2 y=x2

代码优化

思考: 一个函数, 参数在另一个函数中调用 是否可以直接将另一个函数拿过来?

形如以下函数

args => actualCallFunction(args) ❗️ 只会徒增代码量 这种形式等价于 actualCallFunction

const log = arg => console.log(arg)

const a = arg => log(arg)
// a 和以下代码等价
const b = log

a(123)	// 123
b(456)	// 456

再看个 

const actualCallFunction = _ => console.log('qweasdzxc')
const foo = aFunction => actualCallFunction(arg => aFunction(arg))
// 优化一下
const foo2 = aFunction => actualCallFunction(aFunction)
// 接着优化
const foo3 = actualCallFunction

foo()		// qweasdzxc
foo2()		// qweasdzxc
foo3()		// qweasdzxc

一些概念

纯函数: 相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用

副作用中的“副”是滋生 bug 的温床

副作用可能包含,但不限于:

  • 更改文件系统
  • 往数据库插入记录
  • 发送一个 http 请求
  • 可变数据
  • 打印/log
  • 获取用户输入
  • DOM 查询
  • 访问系统状态

curry:只需传给函数一些参数,就能得到一个新函数

const add = x => y => x + y;
const increment = add(1);
const addTen = add(10);

console.log(increment(2)) // 3
console.log(addTen(2))    // 12

Compose (代码组合)

一个简单的 compose 函数如下

const compose = (f, g) => x => f(g(x));

可以看到, 代码执行顺序是从右向左

并且, 组合函数遵循结合律

// associativity
compose(f, compose(g, h)) === compose(compose(f, g), h);

举个 ​ 咯

const compose = (f, g) => x => f(g(x));

const toUpperCase = x => x.toUpperCase()
const excalim = x => x + '!'
const logMidRes = x => console.log(x) || x

const convertString = compose(excalim, toUpperCase)
const convertString2 = compose(compose(logMidRes, excalim), compose(logMidRes, toUpperCase))
const a = convertString('welcome')
const b = convertString2('welcome')	// WELCOME    WELCOME!
console.log(a)  // WELCOME!
console.log(b)  // WELCOME!

Pointfree style

函数不会提到其操作的数据, 如下

// not pointfree because we mention the data: name
const initials = name => name.split(' ').map(compose(toUpperCase, head)).join('. ');

// pointfree
const initials2 = compose(intercalate('. '), map(compose(toUpperCase, head)), split(' '));

const a = initials('hunter stockton thompson'); // 'H. S. T'
const b = initials2('hunter stockton thompson'); // 'H. S. T'
console.log(a)
console.log(b)

以下为工具代码

// args 为实际使用数据 fn , 此时为数组, 展开(...)之后, 被 fn 调用, 调用后将返回的结果放入一个数组中, 以便下次可展开调用, 最后, 取出数组中唯一的值 
const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];

function curry(fn) {
  const arity = fn.length;

  return function $curry(...args) {
    if (args.length < arity) {	// 参数未达到使用条件, 如 split(' '), 添加操作数并返回
      return $curry.bind(null, ...args);
    }
	// 实际调用, 传入实际使用参数
    return fn.call(null, ...args);
  };
}

// give it a sep and we get a function back	=>	waiting for its str argument.
const split = curry((sep, str) => str.split(sep));

const intercalate = curry((str, xs) => xs.join(str));

const map = curry((fn, f) => f.map(fn));


const toUpperCase = x => x.toUpperCase()

const head = x => x[0];
 类似资料: