拖延症了好久,第二篇终于写出来了。
上一篇在这里:
上一篇文章里我们提到了纯函数的概念,所谓的纯函数就是,对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态(我偷懒复制过来的)。
但是实际的编程中,特别是前端的编程范畴里,“不依赖外部环境”这个条件是根本不可能的,我们总是不可避免地接触到 DOM、AJAX 这些状态随时都在变化的东西。所以我们需要用更强大的技术来干这些脏活。
一、容器、Functor
如果你熟悉 jQuery 的话,应该还记得,$(…) 返回的对象并不是一个原生的 DOM 对象,而是对于原生对象的一种封装:
1 2 3 4 5 6 |
var foo = $('#foo'); foo == document.getElementById('foo'); //=> false foo[0] == document.getElementById('foo'); //=> true |
这在某种意义上就是一个“容器”(但它并不函数式)。
接下类我们会看到,容器为函数式编程里普通的变量、对象、函数提供了一层极其强大的外衣,赋予了它们一些很惊艳的特性,就好像 Tony Stark 的钢铁外衣,Dva 的机甲,明日香的2号机一样。
下面我们就来写一个最简单的容器吧:
1 2 3 4 5 6 7 8 9 10 11 |
var Container = function(x) { this.__value = x; } Container.of = x => new Container(x); //试试看 Container.of(1); //=> Container(1) Container.of('abcd'); //=> Container('abcd') |
我们调用 Container.of 把东西装进容器里之后,由于这一层外壳的阻挡,普通的函数就对他们不再起作用了,所以我们需要加一个接口来让外部的函数也能作用到容器里面的值:
1 2 3 |
Container.prototype.map = function(f){ return Container.of(f(this.__value)) } |
我们可以这样使用它:
1 2 3 |
Container.of(3) .map(x => x + 1) //=> Container(4) .map(x => 'Result is ' + x); //=> Container('Result is 4') |
没错!我们仅花了 7 行代码就实现了很炫的『链式调用』,这也是我们的第一个 Functor。
Functor(函子)是实现了 map 并遵守一些特定规则的容器类型。
也就是说,如果我们要将普通函数应用到一个被容器包裹的值,那么我们首先需要定义一个叫 Functor 的数据类型,在这个数据类型中需要定义如何使用 map 来应用这个普通函数。
把东西装进一个容器,只留出一个接口 map 给容器外的函数,这么做有什么好处呢?
本质上,Functor 是一个对于函数调用的抽象,我们赋予容器自己去调用函数的能力。当 map 一个函数时,我们让容器自己来运行这个函数,这样容器就可以自由地选择何时何地如何操作这个函数,以致于拥有惰性求值、错误处理、异步调用等等非常牛掰的特性。
举个例子,我们现在为 map 函数添加一个检查空值的特性,这个新的容器我们称之为 Maybe(原型来自于Haskell):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
var Maybe = function(x) { this.__value = x; } Maybe.of = function(x) { return new Maybe(x); } Maybe.prototype.map = function(f) { return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value)); } Maybe.prototype.isNothing = function() { return (this.__value === null || this.__value === undefined); } //试试看 import _ from 'lodash'; var add = _.curry(_.add); Maybe.of({name: an class="crayon-sy">; Maybe.of({name: >JavaScript函数式编程(一)
上一篇文章里我们提到了纯函数的概念,所谓的纯函数就是,对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态(我偷懒复制过来的)。 但是实际的编程中,特别是前端的编程范畴里,“不依赖外部环境”这个条件是根本不可能的,我们总是不可避免地接触到 DOM、AJAX 这些状态随时都在变化的东西。所以我们需要用更强大的技术来干这些脏活。
一、容器、Functor如果你熟悉 jQuery 的话,应该还记得,$(…) 返回的对象并不是一个原生的 DOM 对象,而是对于原生对象的一种封装:
这在某种意义上就是一个“容器”(但它并不函数式)。 接下类我们会看到,容器为函数式编程里普通的变量、对象、函数提供了一层极其强大的外衣,赋予了它们一些很惊艳的特性,就好像 Tony Stark 的钢铁外衣,Dva 的机甲,明日香的2号机一样。 下面我们就来写一个最简单的容器吧:
我们调用 Container.of 把东西装进容器里之后,由于这一层外壳的阻挡,普通的函数就对他们不再起作用了,所以我们需要加一个接口来让外部的函数也能作用到容器里面的值:
我们可以这样使用它:
没错!我们仅花了 7 行代码就实现了很炫的『链式调用』,这也是我们的第一个 Functor。 Functor(函子)是实现了 map 并遵守一些特定规则的容器类型。 也就是说,如果我们要将普通函数应用到一个被容器包裹的值,那么我们首先需要定义一个叫 Functor 的数据类型,在这个数据类型中需要定义如何使用 map 来应用这个普通函数。 把东西装进一个容器,只留出一个接口 map 给容器外的函数,这么做有什么好处呢? 本质上,Functor 是一个对于函数调用的抽象,我们赋予容器自己去调用函数的能力。当 map 一个函数时,我们让容器自己来运行这个函数,这样容器就可以自由地选择何时何地如何操作这个函数,以致于拥有惰性求值、错误处理、异步调用等等非常牛掰的特性。 举个例子,我们现在为 map 函数添加一个检查空值的特性,这个新的容器我们称之为 Maybe(原型来自于Haskell):
|