导入
它们是什么?
函数对象的简要介绍,以及它们将如何与本课程内容相结合。(4分钟阅读)
FP 定义了自己的类型,而函子(functor)是最基本的一种。可以将其理解为一个容器,用于存放任意类型的值,比如字符串、日期、布尔值、数组、对象等。
这个容器必须拥有一个 map 方法,才能被称为函子。
下面是一个简单的例子:
1const Identity = (x) => ({ 2 value: x, 3 map: (fn) => Identity(fn(x)) 4});
函数 Identity 接收一个值并返回一个函子。
1const name = Identity('Bobo'); 2 3console.log(name);
打印 name 会得到如下结构的函子:
1{ value: 'Bobo', map: [Function: map] }
那么,如何改变这个函子内部的值 'Bobo' 呢?
在 JavaScript 中,我们可以直接修改它:
1const name = Identity('Bobo'); 2 3name.value = name.value.toUpperCase(); 4 5console.log(name);
但根据函数式编程的原则,我们应尽量避免这种可变操作。那如果改用 map 呢?
1const name = Identity('Bobo'); 2const newName = name 3 .map(value => value.toUpperCase()); 4 5console.log({ name, newName });
这样,我们没有修改原有的函子,而是创建了一个全新的函子,包含了我们期望的新值!
由于 map 必须返回相同类型的函子,因此我们可以像数组的 map 一样进行链式调用。
1const name = Identity('Bobo'); 2const newName = name 3 .map(value => value.slice(0, 3)) 4 .map(value => `My name is not ${value}!`) 5 .map(value => value.toUpperCase()); 6 7console.log({ name, newName });
这种方式是不可变的,代码更易读,也更易于组合。
当你需要取出值时,只需访问其 .value 属性即可。
总结
- 函子是你可以对其应用
map的容器。 - 函子可以容纳任何类型的值。
map是一种可组合、不可变的方式来改变函子的值。
为什么关心这个?
透镜(Lenses)使用了函子。我们很快就会深入探讨它们。