{"componentChunkName":"component---src-templates-blog-post-js","path":"/lodash-fp/","result":{"data":{"site":{"siteMetadata":{"title":"😏","author":"thoamsy"}},"markdownRemark":{"id":"e81e5b24-9758-534a-86bf-c65e9247ffe6","html":"<p>NOTE: 2019-08-25 调整一些描述和格式更新，并添加 pipeline 相关。</p>\n<p>这篇文章主要是介绍 lodash 的 <a href=\"https://github.com/lodash/lodash/wiki/FP-Guide\">fp</a> 模块, 通过它的特点来让 React 的 <code class=\"language-text\">setState</code> 少写很多代码. 同样的原理也能放在 redux 的 <code class=\"language-text\">reducer</code> 上, 但是因为 lodash/fp 的缺陷, 这篇文章不会深入.</p>\n<h2 id=\"删除数组的某个值\" style=\"position:relative;\"><a href=\"#%E5%88%A0%E9%99%A4%E6%95%B0%E7%BB%84%E7%9A%84%E6%9F%90%E4%B8%AA%E5%80%BC\" aria-label=\"删除数组的某个值 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>删除数组的某个值</h2>\n<p>开门先来一个例子.\n假设我们的 state 的结构是这样</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">state <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  posts<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> id<span class=\"token operator\">:</span> <span class=\"token string\">'1234567'</span><span class=\"token punctuation\">,</span> content<span class=\"token operator\">:</span> <span class=\"token string\">'abc'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> id<span class=\"token operator\">:</span> <span class=\"token string\">'4343434'</span><span class=\"token punctuation\">,</span> content<span class=\"token operator\">:</span> <span class=\"token string\">'def'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>我们想删除特定的 id 的 post 话, 最原生的写法会是</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> posts <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n\tposts<span class=\"token operator\">:</span> posts<span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">post</span> <span class=\"token operator\">=></span> post<span class=\"token punctuation\">.</span>id <span class=\"token operator\">!==</span> postId<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>使用普通的 lodash 会是</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> posts <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  posts<span class=\"token operator\">:</span> <span class=\"token function\">reject</span><span class=\"token punctuation\">(</span>posts<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> postId<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>这样看起来, 普通的 lodash 并没有做多少事情, 只不过在 <code class=\"language-text\">filter</code> 的时候, 通过内置的 <code class=\"language-text\">reject</code> 少写了一些代码.\n那么, 我们再看下 lodash/fp 的写法</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token string\">'posts'</span><span class=\"token punctuation\">,</span> <span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> postId<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// or</span>\n<span class=\"token keyword\">const</span> rejectWithPostId <span class=\"token operator\">=</span> <span class=\"token function\">compose</span><span class=\"token punctuation\">(</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token string\">'posts'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> reject<span class=\"token punctuation\">,</span> <span class=\"token function\">propEq</span><span class=\"token punctuation\">(</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token function\">rejectWithPostId</span><span class=\"token punctuation\">(</span>postId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>可以很直观的看到, 我们已经少写了很多重复的代码. 比如之前两个代码中都会出现三次的 <code class=\"language-text\">posts</code>, 在这里只出现了一次, 而且也没有嵌套和大括号的出现, 这明显让代码看上去更<del>简洁</del>优雅不繁琐。</p>\n<p>而下面 compose 的使用, 虽然看上去总代码多了, 但是却进一步提高了代码的维护性，让开发者明确知道这个函数代表了什么意思。</p>\n<h2 id=\"具体的解释在下文说明\" style=\"position:relative;\"><a href=\"#%E5%85%B7%E4%BD%93%E7%9A%84%E8%A7%A3%E9%87%8A%E5%9C%A8%E4%B8%8B%E6%96%87%E8%AF%B4%E6%98%8E\" aria-label=\"具体的解释在下文说明 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>具体的解释在下文说明</h2>\n<h2 id=\"lodashfp-的变化\" style=\"position:relative;\"><a href=\"#lodashfp-%E7%9A%84%E5%8F%98%E5%8C%96\" aria-label=\"lodashfp 的变化 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>lodash/fp 的变化</h2>\n<p>那么这个模块和普通的 lodash 区别在哪呢？分为以下几点</p>\n<ol>\n<li>都是纯函数</li>\n<li>参数的顺序调整</li>\n<li>iteratee 参数受限</li>\n<li>完全柯里化</li>\n<li>没有可选参数下面一一解释这几点</li>\n</ol>\n<h3 id=\"pure\" style=\"position:relative;\"><a href=\"#pure\" aria-label=\"pure permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Pure</h3>\n<p><em>都是纯函数</em>这句话, 如果你稍微了解过函数式编程, 就知道它的意义何在.\n我们看这个例子.\n如果我们使用 lodash 的 <code class=\"language-text\">set</code>, 执行</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">prevState</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">set</span><span class=\"token punctuation\">(</span>prevState<span class=\"token punctuation\">,</span> <span class=\"token string\">'posts[0]'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>这样的操作会修改 prevState 的, 肯定会导致的 bug 的出现. 当然这种用 set 的方式确实有点怪, 有点像强行捏造的出来的代码 🌝\n但是如果用 lodash/fp 下的 <code class=\"language-text\">set</code></p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">prevState</span> <span class=\"token operator\">=></span> <span class=\"token function\">set</span><span class=\"token punctuation\">(</span><span class=\"token string\">'posts[0]'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">,</span> prevState<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>它相比 lodash 的区别是，参数顺序调整和 <em>immutable</em>\n另外, 你可以用 Jest 运行下面代码, 可以确定它是纯的, 这样也就保证了 setState 的正确性</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">test</span><span class=\"token punctuation\">(</span><span class=\"token string\">'The set function should be pure'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token function\">set</span><span class=\"token punctuation\">(</span><span class=\"token string\">'posts[0]'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">,</span> state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>posts<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>not<span class=\"token punctuation\">.</span><span class=\"token function\">toBe</span><span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">.</span>posts<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>not<span class=\"token punctuation\">.</span><span class=\"token function\">toBe</span><span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>posts<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toBe</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<h3 id=\"rearranged-arguments\" style=\"position:relative;\"><a href=\"#rearranged-arguments\" aria-label=\"rearranged arguments permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Rearranged Arguments</h3>\n<p>最显著的一点就是, 由 <em>data-first/</em> 变成了 <em>data-last</em>.\n除了上面的 set, 还有 <code class=\"language-text\">map, reduce, filter</code> 都是这样.</p>\n<p><code class=\"language-text\">map([1, 2, 3], a =&gt; a * 2)</code> 调整成了 <code class=\"language-text\">map(a =&gt; a * 2, [1 ,2 ,3])</code>\n这个调整目前看上去似乎很鸡肋，甚至有点反人类之嫌, 等描述完柯里化后我们会继续对比</p>\n<h3 id=\"capped-iteratee-arguments\" style=\"position:relative;\"><a href=\"#capped-iteratee-arguments\" aria-label=\"capped iteratee arguments permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Capped Iteratee Arguments</h3>\n<p>将迭代器函数做限制, 主要是为了规避一些常见 bug</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// The `lodash/map` iteratee receives three arguments:</span>\n<span class=\"token comment\">// (value, index|key, collection)</span>\n_<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'6'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'8'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'10'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> parseInt<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// ➜ [6, NaN, 2]</span>\n\n<span class=\"token comment\">// The `lodash/fp/map` iteratee is capped at one argument:</span>\n<span class=\"token comment\">// (value)</span>\nfp<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span>parseInt<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'6'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'8'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'10'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// ➜ [6, 8, 10]</span></code></pre></div>\n<p>Capped 的方式虽然让我们不会再遭遇意外的传参导致的 bug，但是这也导致 map 的能力被大大削弱。这种一刀切的方式似乎不是那么的优雅。好在 fp 中还提供了一个 <code class=\"language-text\">convert</code> 方法来用去除这个限制</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> mapWithIndex <span class=\"token operator\">=</span> map<span class=\"token punctuation\">.</span><span class=\"token function\">convert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> cap<span class=\"token operator\">:</span> <span class=\"token boolean\">false</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> posts <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n\tposts<span class=\"token operator\">:</span> <span class=\"token function\">mapWithIndex</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">post<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> i <span class=\"token operator\">===</span> index <span class=\"token operator\">?</span> <span class=\"token punctuation\">{</span> <span class=\"token operator\">...</span>post<span class=\"token punctuation\">,</span> content<span class=\"token operator\">:</span> <span class=\"token string\">'good'</span> <span class=\"token punctuation\">}</span> <span class=\"token operator\">:</span> post<span class=\"token punctuation\">,</span> posts<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>可是我们真的还需要依赖 map 的 index 来做这种事情么? 仔细想一下, cap 的存在不仅是为了减少意外的 bug 出现，还有一个可能就是它在鼓励开发者使用更加 <em>functional</em> 的方式来完成这个任务。我们可以到 fp 中的 <code class=\"language-text\">update</code> 方法</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">posts[</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>index<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">]</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token function\">set</span><span class=\"token punctuation\">(</span><span class=\"token string\">'content'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'good'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>那么除了 set 之外，fp 中还存在一个 assign 方法，它的作用和 <code class=\"language-text\">Object.assign</code> 类似，那么我们能把代码替换成下面这样么：</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">posts[</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>index<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">]</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token function\">assign</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> content<span class=\"token operator\">:</span> <span class=\"token string\">'good'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>事实上是不可行的，具体原因后面再展示。</p>\n<p>另外, lodash/fp 其实还提供了很多同名函数, <code class=\"language-text\">set</code> 的同名函数有 <code class=\"language-text\">assoc</code> 和 <code class=\"language-text\">assocPath</code>. 用法都一样, 不过在其他库中, 后面两个叫法更为普遍.</p>\n<p><code class=\"language-text\">assoc</code> 用于给非嵌套的对象赋值。比如 <code class=\"language-text\">assoc(&#39;path&#39;, 1), assoc(&#39;hh&#39;, 2)</code>。对于 <code class=\"language-text\">posts[2]</code> 这种方式就应该交给\nassocPath, <code class=\"language-text\">assocPath([&#39;posts&#39;, 2], 2)</code></p>\n<p>但是呢，Lodash 是偷了懒, 没有在实现上区别它们. 只不过它提供了一个 <code class=\"language-text\">eslint-plugin-lodash-fp</code> 来提供 lint 的支持, 帮助使用者加以限制.</p>\n<h3 id=\"currying\" style=\"position:relative;\"><a href=\"#currying\" aria-label=\"currying permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Currying</h3>\n<p>终于到了重点咯. 先来解释下柯里化吧.\n英文 <em>Currying</em>, 之所以这么叫是因为有一个数学家叫, <a href=\"https://en.wikipedia.org/wiki/Haskell_Curry\">Haskell Curry</a> , 他提出了一种化简高阶函数的方法, 所有后人就给这种方法命名为 Currying 咯. 我初学的时候, 一直没搞懂这是什么意思, 因为它的直译就是<em>咖喱</em>(那个时候, 篮球巨星库里还没那么火)</p>\n<p>假设我们有一个函数, <code class=\"language-text\">add</code>. 通常的写法应该是 <code class=\"language-text\">add(a, 10)</code>, 说明我们给变量 a 加 10.\n下一次我们要给变量 b 加 10, 就 <code class=\"language-text\">add(b, 10)</code>, 如果还有变量 C……\n如果一个被柯里化过得 add, 就可以这样写 <code class=\"language-text\">add(a, 10) add(a)(10), add(__, 10)(a), add(__)(a)(10)</code></p>\n<p>你可能会问这样有什么用 0.0, 而且那个 <code class=\"language-text\">__</code> 是什么意思? 这大的好处其实就是能够函数复用，并作为输入传给下一个函数</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> addTen <span class=\"token operator\">=</span> <span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>这样的话, 要计算 a, b, c 只需要调用 <code class=\"language-text\">addTen(a)</code> 这样就行咯.\n那么 <code class=\"language-text\">__</code> 呢? 官方的解释是:</p>\n<blockquote>\n<p>The placeholder argument, which defaults to <code class=\"language-text\">__</code>, may be used to fill in method arguments in a different order. Placeholders are filled by the first available arguments of the curried returned function.</p>\n</blockquote>\n<p>什么意思, 我们举过一个例子. 上面的加法改成减法, 但是目的还是一样, 将一个数减 10.\n这里我们还能用 <code class=\"language-text\">sub(10)</code> 吗? 显然不行, 因为减法不是加法, 是不<strong>满足交换律</strong>的. 所以 sub(10) 会变成 10 - a 而不是 a - 10\n这个时候就可以用到 <code class=\"language-text\">__</code>, <code class=\"language-text\">const subTen = sub(__, 10)</code>, 它就相当于一个占位符的效果，让我们忽略这个值，去看后面的。也类似于 <code class=\"language-text\">flip(sub)(10)</code> 的效果</p>\n<p>同时, 使用占位符, 上面的代码</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">posts[</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>index<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">]</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token function\">set</span><span class=\"token punctuation\">(</span><span class=\"token string\">'content'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'good'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>其实可以改成</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">posts[</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>index<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">]</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token function\">assign</span><span class=\"token punctuation\">(</span>__<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> content<span class=\"token operator\">:</span> <span class=\"token string\">'good'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>所以之前的 <code class=\"language-text\">assign</code> 不行的原因是因为, lodash/fp 并没有调整它的顺序. 本来是后面的参数覆盖前面的参数, 这种很符合人类思考方式的习惯, 但是如果硬要交换的话, 反而就会看上去很奇怪.</p>\n<p>现在我们大概了解了柯里化的作用, 但是看上去效果还不是那么明显. 但是如果结合参数顺序的对调呢?\n再看一个例子. 这个代码可能有点抽象, 有点长 :）<a href=\"http://fr.umio.us/favoring-curry/\">例子来自一个 ramda 的教程</a></p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getIncompleteTaskSummaries</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">memberName</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">fetchData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">data</span> <span class=\"token operator\">=></span> <span class=\"token function\">get</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">,</span> <span class=\"token string\">'tasks'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">tasks</span> <span class=\"token operator\">=></span> <span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>tasks<span class=\"token punctuation\">,</span> <span class=\"token string\">'username'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">tasks</span> <span class=\"token operator\">=></span> <span class=\"token function\">reject</span><span class=\"token punctuation\">(</span>tasks<span class=\"token punctuation\">,</span> <span class=\"token string\">'complete'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">tasks</span> <span class=\"token operator\">=></span> <span class=\"token function\">map</span><span class=\"token punctuation\">(</span>tasks<span class=\"token punctuation\">,</span> <span class=\"token parameter\">task</span> <span class=\"token operator\">=></span> <span class=\"token function\">pick</span><span class=\"token punctuation\">(</span>\n\t\t\t\ttask<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'title'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'priority'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">abbreviatedTasks</span> <span class=\"token operator\">=></span> <span class=\"token function\">sortBy</span><span class=\"token punctuation\">(</span>abbreviatedTask<span class=\"token punctuation\">,</span> <span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>我们先不管它的应用场景, 光看代码的话, 可以意识到, 这里写了太多重复的 tasks 了, 而这也是我们日常编程中的常见写法. tasks 作为中间变量, 需要不断地的传递.\n我们剥离这里的 Promise, 改成这个样子</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">sortBy</span><span class=\"token punctuation\">(</span>\n  <span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">,</span> <span class=\"token string\">'tasks'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'username'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'complete'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token parameter\">task</span> <span class=\"token operator\">=></span>\n    <span class=\"token function\">pick</span><span class=\"token punctuation\">(</span>task<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'title'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'priority'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string\">'dueDate'</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>我稍微做了一点格式的调整, 但是这些整个思路就很明显了, 这不就是函数嵌套吗? 只不过可读性不是那么好 :-(, 我们根本不可能一眼看出来它是对哪个变量进行操作, 最先运行的函数又是哪个</p>\n<p>大学数学的离散数学里应该提高一个概念, <em>函数组合</em>. <em>f(g(x)) = f • g</em> 所以上面的写法如果可以改为 <em>sortBy • map • reject • filter • get</em> 这种形式的话, 看上去应该会简单很多啦, 那么我们可以做到吗? 尝试下 <code class=\"language-text\">compose</code> 函数.</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">compose</span><span class=\"token punctuation\">(</span>\n  <span class=\"token function\">sortBy</span><span class=\"token punctuation\">(</span>__<span class=\"token punctuation\">,</span> <span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">map</span><span class=\"token punctuation\">(</span>__<span class=\"token punctuation\">,</span> <span class=\"token function\">pick</span><span class=\"token punctuation\">(</span>__<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'title'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'priority'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">reject</span><span class=\"token punctuation\">(</span>__<span class=\"token punctuation\">,</span> <span class=\"token string\">'complete'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>__<span class=\"token punctuation\">,</span> <span class=\"token string\">'username'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">get</span><span class=\"token punctuation\">(</span>__<span class=\"token punctuation\">,</span> <span class=\"token string\">'tasks'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// 不要运行这段函数</span></code></pre></div>\n<p>其实这样看上去已经好多了不是吗? 至少我们一眼就能看出来, 我们的逻辑顺序了.\n至于为什么这么多 <code class=\"language-text\">__</code>, 那是因为它们都是 data 被处理后的结果, 也就是上面提到的中间变量. 所以我们仅仅只需要调整函数参数的顺序, 就可以省去这些占位符.\n变成</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">compose</span><span class=\"token punctuation\">(</span>\n  <span class=\"token function\">sortBy</span><span class=\"token punctuation\">(</span><span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token function\">pick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'title'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'priority'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token string\">'complete'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">filter</span><span class=\"token punctuation\">(</span><span class=\"token string\">'username'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'tasks'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>这就是参数需要调整顺序的原因啦.</p>\n<p>再解释一下柯里化在这里的重要性. 如果函数没有柯里化的话, 从第一步 <code class=\"language-text\">get(&#39;tasks&#39;)</code> 开始, 这个函数只会被解释成 <code class=\"language-text\">get(&#39;tasks&#39;, undefined)</code>, 那么很显然, 后面的过程就没有进行下去的意义咯. 而通过柯里化, 这里返回的是一个函数, 一个需要收集它另外一个参数的函数.</p>\n<p>同样的 <code class=\"language-text\">filter</code> 也需要一个数组来填充进来, <code class=\"language-text\">reject</code> 和后面的函数都一样. 因为它们现在都是函数, 所以 compose 不会抱怨, 它将这些函数<em>组合在了一起</em>, 变成了一个新的函数.\n当我们将 data 传入这个新函数的时候, 先运行 <code class=\"language-text\">get(&#39;tasks&#39;)(data)</code>, 得到一个新的数组, 这个数组传给 <code class=\"language-text\">filter(&#39;usename&#39;)</code> 又得到一个新的数组. 就这样反复的执行最后得到我们的结果</p>\n<p>顺便一提, 这种 <code class=\"language-text\">a(b(c(d(e, f)))))</code> 的形式其实很像 <code class=\"language-text\">reduce</code> 的操作, 所以再看看 compose 的实现吧</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">compose</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token operator\">...</span>fns</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> fns<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token operator\">...</span>args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">a</span><span class=\"token punctuation\">(</span><span class=\"token function\">b</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>args<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>在新的 Babel 提案中，有一个 <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator\">pipeline</a> 运算符 <code class=\"language-text\">|&gt;</code>, 利用它可以让代码改成下面的样子而不需要另外的函数。而且相比 compose 的从右往左的顺序，可能从左往右会更加直观</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'tasks'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token operator\">|</span><span class=\"token operator\">></span> <span class=\"token function\">filter</span><span class=\"token punctuation\">(</span><span class=\"token string\">'username'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token operator\">|</span><span class=\"token operator\">></span> <span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token string\">'complete'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token operator\">|</span><span class=\"token operator\">></span> <span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token function\">pick</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'title'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'priority'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token operator\">|</span><span class=\"token operator\">></span> <span class=\"token function\">sortBy</span><span class=\"token punctuation\">(</span><span class=\"token string\">'dueDate'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<h3 id=\"no-optional-arguments\" style=\"position:relative;\"><a href=\"#no-optional-arguments\" aria-label=\"no optional arguments permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>No Optional Arguments</h3>\n<p>最后再解释一下为什么需要固定参数? lodash 的那些可选参数的函数全部都挂了, 为什么呢?\n其实这是因为 <code class=\"language-text\">curry</code> 不支持可选参数的. JS 原生不支持柯里化, 那么我们无非就是使用一个函数来模拟这个过程.\n一个简单的 curry 方法可以写成</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">curry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> length <span class=\"token operator\">=</span> fn<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  args<span class=\"token punctuation\">.</span>length <span class=\"token operator\">>=</span> length <span class=\"token operator\">?</span> <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>args<span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> <span class=\"token function\">curry</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> length<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>args<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>注意看实现就可以发现, 我们必须了解到函数的长度, 才能决定是返回函数的运行结果还是一个新的函数.\n看完这些再回过头看之前的例子的话, 应该就很容易理解了.</p>\n<h2 id=\"更多例子\" style=\"position:relative;\"><a href=\"#%E6%9B%B4%E5%A4%9A%E4%BE%8B%E5%AD%90\" aria-label=\"更多例子 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>更多例子</h2>\n<p>上面已经举了使用 lodash/fp 配合 setState 更新和删除的例子, 再看看在末尾添加的例子吧.</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// 末尾添加</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">addPost</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">post</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token string\">'posts'</span><span class=\"token punctuation\">,</span> <span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>post<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// 更新数组特定下标的值, 这个类似于井字棋的游戏中很有用.</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">setValue</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">value<span class=\"token punctuation\">,</span> index</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token function\">set</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">posts[</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>index<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">]</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"不足\" style=\"position:relative;\"><a href=\"#%E4%B8%8D%E8%B6%B3\" aria-label=\"不足 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>不足</h2>\n<p>lodash/fp 其实还有很多不足, 它不过是将 lodash 里的函数改成了 fp 的形式, 所以它相比 lodash 没有任何新函数.\n比如, 它不能同时修改一个对象的多个值. 同样的也不能使用多个函数来修改一个对象的多个值.\n简单的说就是, <code class=\"language-text\">set</code> 和 <code class=\"language-text\">update</code> 都仅仅只支持修改一个参数.\n同时, 针对数组的方法也不是很多, 比如没有类似于 <code class=\"language-text\">insert</code> 的方法. 这让某些时候，fp 变得非常鸡肋</p>\n<h2 id=\"ramda\" style=\"position:relative;\"><a href=\"#ramda\" aria-label=\"ramda permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Ramda</h2>\n<p>最后, 如果真的尝试使用这种风格的话, lodash/fp 仅仅是入门, 建议还是使用 <a href=\"http://ramdajs.com\">Ramda</a> 来获得更加纯粹的体验和享受.\n关于 Ramda 的使用可以参考这些<a href=\"http://fr.umio.us\">博客</a></p>","frontmatter":{"title":"使用 lodash/fp","spoiler":"感受 fp 的强大","image":null,"date":"2018/04/02"}}},"pageContext":{"slug":"/lodash-fp/","previous":{"fields":{"slug":"/object-assign/"},"frontmatter":{"title":"Object.assign 的一些问题","image":null,"spoiler":null}},"next":{"fields":{"slug":"/purecomponent-is-harmful/purecomponent-is-harmful/"},"frontmatter":{"title":"PureComponent Considered Harmful","image":null,"spoiler":"不要让所有的组件都是 PureComponent 或者 memo"}}}}}