第 1 章:为什么使用函数式编程?

置信度

  1. 你不能信任的代码是你不明白的代码。
  2. 对你不信任或不明白的代码,你将不能确定这些代码是否符合你的业务场景。代码运行时也只能祈求好运。

信任是什么意思?信任是指你通过读代码,不仅是跑代码,就能理解这段代码能干什么事,而不只是停留在它可能是干什么的层面。

交流渠道

  1. 代码的主要作用是方便人与人交流。应该更多的关注一下代码的可读性。

书籍推荐

一些你务必要阅读的函数式编程 / JavaScript 书籍:

Professor Frisby’s Mostly Adequate Guide to Functional Programming by Brian Lonsdorf
JavaScript Allongé by Reg Braithwaite
Functional JavaScript by Michael Fogus

博客和站点

一些其他作者和相关内容供查阅:

Fun Fun Function Videos by Mattias P Johansson
Awesome函数式编程JS
Kris Jenkins
Eric Elliott
James A Forbes
James Longster
André Staltz
Functional Programming Jargon
Functional Programming Exercises

第 2 章:函数基础

  1. 函数式编程就是使用在数学意义上的方程作为函数, function 应该接收输入值,并且返回输出值。
  2. arguments 是你输入的值(实参), parameters 是函数中的命名变量(形参),用于接收函数的输入值
  3. 注意: 在 JavaScript 中,实参的个数没必要完全符合形参的个数。如果你传入许多个实参,而且多过你所声明的形参,这些值仍然会原封不动地被传入。你可以通过不同的方式去访问,包含了你以前可能听过的老办法 —— arguments 对象。反之,你传入少于声明形参个数的实参,所有缺少的参数将会被赋予 undefined 变量,意味着你仍然可以在函数作用域中使用它,但值是 undefined。
  4. Arity: 指的是一个函数声明的形参数量。 使用函数的 length 属性即可
  5. 由于 ES5(特别是严格模式下)的 arguments 不被一些人认同,很多人尽可能地避免使用。尽管如此,它永远不会被移除,这是因为在 JS 中我们“永远不会”因为便利性而去牺牲向后的兼容性,但我还是强烈建议不要去使用它。
    然而,当你需要知道参数个数的时候,arguments.length 还是可以用的。在未来版本的 JS 或许会新增特性来替代 arguments.length,如果成真,那么我们可以完全把 arguments 抛诸脑后。
    请注意:不要通过 arguments[1] 访问参数的位置。只要记住 arguments.length。
  6. 带有可变数量参数的函数被称为 variadic
    7.
    1
    2
    3
    4
    5
    function foo( [x,y,...args] = [] ) {
    // ..
    }

    foo( [1,2,3] );

8.

1
2
3
4
5
6
7
function foo( {x,y} = {} ) {
console.log( x, y );
}

foo( {
y: 3
} );

  1. 通过不同的输入值让一个函数重载拥有不同的行为的技巧叫做特定多态(ad hoc polymorphism)
  2. 如果你没有 return 值,或者你使用 return;,那么则会隐式地返回 undefined 值。
    如果想要尽可能靠近函数式编程的定义:使用函数而非程序,那么我们的函数必须永远有返回值。这也意味着他们必须明确地 return 一个值,通常这个值也不是 undefined
  3. 我认为在许多可读性的问题上,是因为我们不仅使用 return 返回不同的值,更把它作为一个流控制结构——在某些情况下可以提前退出一个函数的执行。我们显然有更好的方法来编写流控制( if 逻辑等),也有办法使输出路径更加明显。
    我不是说,你只能有一个 return,或你不应该提早 return,我只是认为在定义函数时,最好不要用 return 来实现流控制,这样会创造更多的隐含意义。尝试找出最明确的表达逻辑的方式,这往往是最好的办法。
  4. 这个隐式函数输出在函数式编程中有一个特殊的名称:副作用。当然,没有副作用的函数也有一个特殊的名称:纯函数。我们将在以后的章节讨论这些,但关键是我们应该喜欢纯函数,并且要尽可能地避免副作用。
  5. 一个函数如果可以接受或返回一个甚至多个函数,它被叫做高阶函数。
  6. 闭包是它可以记录并且访问它作用域外的变量,甚至当这个函数在不同的作用域被执行
    处于 foo(..) 函数作用域中的 msg 参数变量是可以在内部函数中被引用的。当 foo(..) 执行时,并且内部函数被创建,函数可以获取 msg 变量,即使 return 后仍可被访问。
    虽然我们有函数内部引用 helloFn,现在 foo(..) 执行后,作用域应该回收,这也意味着 msg 也不存在了。不过这个情况并不会发生,函数内部会因为闭包的关系,将 msg 保留下来。只要内部函数(现在被处在不同作用域的 helloFn 引用)存在, msg 就会一直被保留。
    15.

    1
    2
    var x = function(){};
    x.name;
  7. 你几乎从没看到为 IIFE 函数来命名,但他们应该命名

  8. 命名所有单个函数
  9. 函数式编程中你不应当使用 this。

第 3 章:管理函数的输入(Inputs)

  1. 用一句话来说明发生的事情:getOrder(data,cb) 是 ajax(url,data,cb) 函数的偏函数(partially-applied functions)。该术语代表的概念是:在函数调用现场(function call-site),将实参应用(apply) 于形参。

  2. 偏函数

    1
    2
    3
    4
    5
    function partial(fn,...presetArgs) {
    return function partiallyApplied(...laterArgs){
    return fn( ...presetArgs, ...laterArgs );
    };
    }

partial(..) 函数接收 fn 参数,来表示被我们偏应用实参(partially apply)的函数。
接着,fn 形参之后,presetArgs 数组收集了后面传入的实参,保存起来稍后使用。
我们创建并 return 了一个新的内部函数(为了清晰明了,我们把它命名为partiallyApplied(..)),
该函数中,laterArgs 数组收集了全部实参。
你注意到在内部函数中的 fn 和 presetArgs 引用了吗?他们是怎么如何工作的?在函数 partial(..) 结束运行后,内部函数为何还能访问 fn 和 presetArgs 引用?你答对了,就是因为闭包!内部函数 partiallyApplied(..) 封闭(closes over)了 fn 和 presetArgs 变量,所以无论该函数在哪里运行,在 partial(..) 函数运行后我们仍然可以访问这些变量。所以理解闭包是多么的重要!
当 partiallyApplied(..) 函数稍后在某处执行时,该函数使用被闭包作用(closed over)的 fn 引用来执行原函数,首先传入(被闭包作用的)presetArgs 数组中所有的偏应用(partial application)实参,然后再进一步传入 laterArgs 数组中的实参。