Help
RSS
API
Feed
Maltego
Contact
Domain > adispring.github.io
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2018-01-31
151.101.45.147
(
ClassC
)
2018-03-31
151.101.185.147
(
ClassC
)
2025-11-12
185.199.110.153
(
ClassC
)
Port 80
HTTP/1.1 301 Moved PermanentlyConnection: keep-aliveContent-Length: 162Server: GitHub.comContent-Type: text/htmlLocation: https://adispring.github.io/X-GitHub-Request-Id: E282:5A908:84DA87:88BE36:69145B5BAccept-Ranges: bytesAge: 0Date: Wed, 12 Nov 2025 10:03:07 GMTVia: 1.1 varnishX-Served-By: cache-bfi-krnt7300073-BFIX-Cache: MISSX-Cache-Hits: 0X-Timer: S1762941788.579228,VS0,VE79Vary: Accept-EncodingX-Fastly-Request-ID: 13d666aafc4957271cb904ed5fb244eb26b5c21d html>head>title>301 Moved Permanently/title>/head>body>center>h1>301 Moved Permanently/h1>/center>hr>center>nginx/center>/body>/html>
Port 443
HTTP/1.1 200 OKConnection: keep-aliveContent-Length: 245718Server: GitHub.comContent-Type: text/html; charsetutf-8Last-Modified: Wed, 27 Apr 2022 12:21:55 GMTAccess-Control-Allow-Origin: *ETag: 62693563-3bfd6expires: Wed, 12 Nov 2025 10:13:07 GMTCache-Control: max-age600x-proxy-cache: MISSX-GitHub-Request-Id: FA89:4264C:8871DA:8C61F2:69145B56Accept-Ranges: bytesAge: 0Date: Wed, 12 Nov 2025 10:03:07 GMTVia: 1.1 varnishX-Served-By: cache-bfi-kbfi7400087-BFIX-Cache: MISSX-Cache-Hits: 0X-Timer: S1762941788.701020,VS0,VE95Vary: Accept-EncodingX-Fastly-Request-ID: 88e0186d5c48763b684abb8b2b87e9ae42dee842 !DOCTYPE html>html langen>head> meta charsetUTF-8>meta nameviewport contentwidthdevice-width, initial-scale1, maximum-scale2>meta nametheme-color content#222>meta namegenerator contentHexo 3.9.0> link relapple-touch-icon sizes180x180 href/images/apple-touch-icon-next.ico> link relicon typeimage/png sizes32x32 href/favicon.ico> link relicon typeimage/png sizes16x16 href/favicon.ico> link relmask-icon href/logo.svg color#222>link relstylesheet href/css/main.css>link relstylesheet href/lib/font-awesome/css/all.min.css>script idhexo-configurations> var NexT window.NexT || {}; var CONFIG {hostname:adispring.github.io,root:/,scheme:Muse,version:7.8.0,exturl:false,sidebar:{position:left,display:post,padding:18,offset:12,onmobile:false},copycode:{enable:false,show_result:false,style:null},back2top:{enable:true,sidebar:false,scrollpercent:false},bookmark:{enable:false,color:#222,save:auto},fancybox:false,mediumzoom:false,lazyload:false,pangu:false,comments:{style:tabs,active:null,storage:true,lazyload:false,nav:null},algolia:{hits:{per_page:10},labels:{input_placeholder:Search for Posts,hits_empty:We didnt find any results for the search: ${query},hits_stats:${hits} results found in ${time} ms}},localsearch:{enable:false,trigger:auto,top_n_per_article:1,unescape:false,preload:false},motion:{enable:true,async:false,transition:{post_block:fadeIn,post_header:slideDownIn,post_body:slideDownIn,coll_header:slideLeftIn,sidebar:slideUpIn}}}; /script> meta namedescription contentFE@Meituan.com>meta propertyog:type contentwebsite>meta propertyog:title contentwangzengdi's Blog>meta propertyog:url contenthttps://adispring.github.io/index.html>meta propertyog:site_name contentwangzengdi's Blog>meta propertyog:description contentFE@Meituan.com>meta propertyog:locale contenten>meta nametwitter:card contentsummary>meta nametwitter:title contentwangzengdi's Blog>meta nametwitter:description contentFE@Meituan.com>link relcanonical hrefhttps://adispring.github.io/>script idpage-configurations> // https://hexo.io/docs/variables.html CONFIG.page { sidebar: , isHome : true, isPost : false, lang : en };/script> title>wangzengdis Blog/title> noscript> style> .use-motion .brand, .use-motion .menu-item, .sidebar-inner, .use-motion .post-block, .use-motion .pagination, .use-motion .comments, .use-motion .post-header, .use-motion .post-body, .use-motion .collection-header { opacity: initial; } .use-motion .site-title, .use-motion .site-subtitle { opacity: initial; top: initial; } .use-motion .logo-line-before i { left: initial; } .use-motion .logo-line-after i { right: initial; } /style>/noscript>/head>body itemscope itemtypehttp://schema.org/WebPage> div classcontainer use-motion> div classheadband>/div> header classheader itemscope itemtypehttp://schema.org/WPHeader> div classheader-inner>div classsite-brand-container> div classsite-nav-toggle> div classtoggle aria-labelToggle navigation bar> span classtoggle-line toggle-line-first>/span> span classtoggle-line toggle-line-middle>/span> span classtoggle-line toggle-line-last>/span> /div> /div> div classsite-meta> a href/ classbrand relstart> span classlogo-line-before>i>/i>/span> h1 classsite-title>wangzengdis Blog/h1> span classlogo-line-after>i>/i>/span> /a> p classsite-subtitle itempropdescription>Functional Programming/p> /div> div classsite-nav-right> div classtoggle popup-trigger> /div> /div>/div>nav classsite-nav> ul idmenu classmain-menu menu> li classmenu-item menu-item-home> a href/ relsection>i classfa fa-home fa-fw>/i>Home/a> /li> li classmenu-item menu-item-archives> a href/archives/ relsection>i classfa fa-archive fa-fw>/i>Archives/a> /li> /ul>/nav>/div> /header> div classback-to-top> i classfa fa-arrow-up>/i> span>0%/span> /div> main classmain> div classmain-inner> div classcontent-wrap> div classcontent index posts-expand> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2022/04/27/Hexo-Workflow/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2022/04/27/Hexo-Workflow/ classpost-title-link itempropurl>Hexo Workflow/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2022-04-27 12:20:36 itempropdateCreated datePublished datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> p>Welcome to a hrefhttps://hexo.io/ target_blank relnoopener>Hexo/a>! This is your very first post. Check a hrefhttps://hexo.io/docs/ target_blank relnoopener>documentation/a> for more info. If you get any problems when using Hexo, you can find the answer in a hrefhttps://hexo.io/docs/troubleshooting.html target_blank relnoopener>troubleshooting/a> or you can ask me on a hrefhttps://github.com/hexojs/hexo/issues target_blank relnoopener>GitHub/a>./p>h2 idquick-start>Quick Start/h2>h3 idcreate-a-new-post>Create a new post/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>$ hexo new span classstring>My New Post/span>/span>br>/pre>/td>/tr>/table>/figure>p>More info: a hrefhttps://hexo.io/docs/writing.html target_blank relnoopener>Writing/a>/p>h3 idrun-server>Run server/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>$ hexo server/span>br>/pre>/td>/tr>/table>/figure>p>More info: a hrefhttps://hexo.io/docs/server.html target_blank relnoopener>Server/a>/p>h3 idgenerate-static-files>Generate static files/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>$ hexo generate/span>br>/pre>/td>/tr>/table>/figure>p>More info: a hrefhttps://hexo.io/docs/generating.html target_blank relnoopener>Generating/a>/p>h3 iddeploy-to-remote-sites>Deploy to remote sites/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>$ hexo deploy/span>br>/pre>/td>/tr>/table>/figure>h3 idgenerate-amp-deploy-together>Generate & Deploy together/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>$ hexo d -g/span>br>/pre>/td>/tr>/table>/figure>p>More info: a hrefhttps://hexo.io/docs/deployment.html target_blank relnoopener>Deployment/a>/p>h3 idhexo-zi-dong-hua-bu-shu>Hexo 自动化部署/h3>ul>li>官方自动化部署文档 – Hexo + Travis: a hrefhttps://hexo.io/docs/github-pages.html target_blank relnoopener>https://hexo.io/docs/github-pages.html/a>/li>li>空白页问题的解决方案:a hrefhttp://magicse7en.github.io/2016/03/27/travis-ci-auto-deploy-hexo-github/ target_blank relnoopener>http://magicse7en.github.io/2016/03/27/travis-ci-auto-deploy-hexo-github//a>/li>/ul>p>自动化部署工作流:/p>p>本地开发使用 hexo-source 分支,开发完成后,推导 github 仓库,会触发自动化部署,自动部署在 master 分支上生成最新的文档并部署。/p> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2022/03/24/Write-Vue2-with-TypeScript/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2022/03/24/Write-Vue2-with-TypeScript/ classpost-title-link itempropurl>TypeScript 开发 Vue2/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2022-03-24 15:28:37 itempropdateCreated datePublished datetime2022-03-24T15:28:37+00:00>2022-03-24/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> h2 idyuan-li-jian-jie>原理简介/h2>p>在 Vue2 中,我们编写的 单文件组件(SCF,也即 Single-File Components),其实是 vue 自创的前端领域语言(类比各互联网厂的小程序开发语言,也是各自造(抄)的领域语言),形式如下所示:/p>figure classhighlight plain>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>span classline>21/span>br>span classline>22/span>br>span classline>23/span>br>span classline>24/span>br>span classline>25/span>br>span classline>26/span>br>span classline>27/span>br>span classline>28/span>br>span classline>29/span>br>span classline>30/span>br>span classline>31/span>br>span classline>32/span>br>span classline>33/span>br>span classline>34/span>br>span classline>35/span>br>span classline>36/span>br>span classline>37/span>br>/pre>/td>td classcode>pre>span classline><template>/span>br>span classline> <div class"title" @click"onClick">{{ title }}</div>/span>br>span classline></template>/span>br>span classline>/span>br>span classline><script>/span>br>span classline>export default {/span>br>span classline> props: {/span>br>span classline> // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)/span>br>span classline> propA: Number,/span>br>span classline> // 多个可能的类型/span>br>span classline> propB: String, Number,/span>br>span classline> // 必填的字符串/span>br>span classline> propC: {/span>br>span classline> type: String,/span>br>span classline> required: true/span>br>span classline> },/span>br>span classline> },/span>br>span classline>/span>br>span classline> data() {/span>br>span classline> return {/span>br>span classline> title: 'hello, vue',/span>br>span classline> }/span>br>span classline> },/span>br>span classline>/span>br>span classline> methods: {/span>br>span classline> onClick() {/span>br>span classline> console.log('click');/span>br>span classline> }/span>br>span classline> }/span>br>span classline>}/span>br>span classline></script>/span>br>span classline>/span>br>span classline><style>/span>br>span classline>.title {/span>br>span classline> color: red;/span>br>span classline>}/span>br>span classline></style>/span>br>/pre>/td>/tr>/table>/figure>p>其中 code><script> ... </script>/code> 标签内包含了一个使用 JavaScript 编写的、 Vue 组件的配置对象,配置对象中包含 props、data、methods 等内容。/p>p>从上面的示例可以看出,原始的 Vue 配置对象,只能够对 props 进行有限的类型检查,不能对 data、methods 等其他配置项做类型声明和类型检查。并且在对 props 进行类型声明时,只能使用下列原生构造函数:String Number Boolean Array Object Date Function Symbol 来做类型校验,不能进行更具体、深入的类型声明,可以说是非常鸡肋。/p>p>对一个对象进行 TypeScript 类型声明是比较困难的,而对 code>class/code> 进行 TypeScript 类型声明是相对简单、自然的:我们可以很方便地对 class 的属性和方法添加类型,还可以通过 code>extends/code> 继承父类的类型。如下所示:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>span classline>21/span>br>span classline>22/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>class/span> Animal {/span>br>span classline> name: span classbuilt_in>string/span>;/span>br>span classline>/span>br>span classline> span classkeyword>constructor/span>(span classparams>theName: span classbuilt_in>string/span>/span>) {/span>br>span classline> span classkeyword>this/span>.name theName;/span>br>span classline> }/span>br>span classline>/span>br>span classline> move(distanceInMeters: span classbuilt_in>number/span> span classnumber>0/span>) {/span>br>span classline> span classbuilt_in>console/span>.log(span classstring>`span classsubst>${span classkeyword>this/span>.name}/span> moved span classsubst>${distanceInMeters}/span>m.`/span>);/span>br>span classline> }/span>br>span classline>}/span>br>span classline> /span>br>span classline>span classkeyword>class/span> Horse span classkeyword>extends/span> Animal {/span>br>span classline> span classkeyword>constructor/span>(span classparams>name: span classbuilt_in>string/span>/span>) {/span>br>span classline> span classkeyword>super/span>(name);/span>br>span classline> }/span>br>span classline>/span>br>span classline> move(distanceInMeters span classnumber>45/span>) {/span>br>span classline> span classbuilt_in>console/span>.log(span classstring>Galloping.../span>);/span>br>span classline> span classkeyword>super/span>.move(distanceInMeters);/span>br>span classline> }/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>p>那么我们是否可以使用 class 来写 Vue 组件呢?这就是 a hrefhttps://www.typescriptlang.org/docs/handbook/decorators.html target_blank relnoopener>Decorators (装饰器)/a>。/p>p>本质上,装饰器就是一个函数。函数能做的事情,装饰器都能做。函数可以做任何类型的数据转换,装饰器也是一样的。/p>p>有了装饰器,我们就可以使用 code>class/code> 和 TypeScript 来开发 Vue 组件;然后在运行时,使用装饰器将我们定义的 code>class/code> 转换为 Vue 引擎能够识别的原生的 Vue 配置对象,如下所示。/p>p>开发时:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>import/span> { Vue, Component, Prop } span classkeyword>from/span> span classstring>vue-property-decorator/span>/span>br>span classline>/span>br>span classline>span classmeta>@Component/span>/span>br>span classline>span classkeyword>export/span> span classkeyword>default/span> span classkeyword>class/span> YourComponent span classkeyword>extends/span> Vue {/span>br>span classline> span classmeta>@Prop/span>({ span classkeyword>default/span>: span classstring>default value/span> }) readonly title!: span classbuilt_in>string/span>/span>br>span classline>/span>br>span classline> message: span classbuilt_in>string/span> span classstring>hello, world/span>;/span>br>span classline>/span>br>span classline> onClick(): span classbuilt_in>void/span> {}/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>p>运行时,@Component 将自定义的组件 code>YourComponent/code> 转换为 Vue 配置对象:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>export/span> span classkeyword>default/span> {/span>br>span classline> props: {/span>br>span classline> title: {/span>br>span classline> span classkeyword>default/span>: span classstring>default value/span>,/span>br>span classline> },/span>br>span classline> },/span>br>span classline>/span>br>span classline> data() {/span>br>span classline> message: span classstring>hello, world/span>,/span>br>span classline> },/span>br>span classline>/span>br>span classline> methods: {/span>br>span classline> onClick() {}/span>br>span classline> }/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>p>Vue2 使用 TypeScript 开发,原理就是这么简单。下面来详细讲一下 Vue2 使用 TypeScript 开发原理。/p>h2 idyi-lai-bao-jie-shao>依赖包介绍/h2>p>Vue2 使用 TypeScript 开发,依赖三个库:/p>ul>li>vue-property-decorator: 提供所有的装饰器;开发时会直接用到的库;依赖 vue-class-component 和 vue;/li>li>vue-class-component: 提供 @Component 装饰器 和 基本的装饰器工厂函数 code>createDecorator/code>;开发时不会直接用到;依赖 vue;vue-property-decorator 中暴露的 @Component,其实是 vue-class-component 提供的;vue-property-decorator 中其他的装饰器,都是由 code>createDecorator/code> 创建的;/li>li>vue: Vue 基础库,提供 Vue 的类型声明,vue-property-decorator 中暴露的 Vue,其实就是 vue 中的 Vue 类;/li>/ul>p>我们来具体看一下这三个库对外暴露的接口:/p>p>vue-property-decorator:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>import/span> Vue span classkeyword>from/span> span classstring>vue/span>/span>br>span classline>span classkeyword>import/span> Component, { mixins } span classkeyword>from/span> span classstring>vue-class-component/span>/span>br>span classline>/span>br>span classline>span classkeyword>export/span> { Component, Vue, mixins span classkeyword>as/span> Mixins }/span>br>span classline>/span>br>span classline>span classkeyword>export/span> { Emit } span classkeyword>from/span> span classstring>./decorators/Emit/span>/span>br>span classline>span classkeyword>export/span> { Prop } span classkeyword>from/span> span classstring>./decorators/Prop/span>/span>br>span classline>span classkeyword>export/span> { Ref } span classkeyword>from/span> span classstring>./decorators/Ref/span>/span>br>span classline>span classkeyword>export/span> { Watch } span classkeyword>from/span> span classstring>./decorators/Watch/span>/span>br>span classline>span classcomment>// .../span>/span>br>/pre>/td>/tr>/table>/figure>p>vue-class-component/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>span classline>21/span>br>span classline>22/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>import/span> Vue, { ComponentOptions } span classkeyword>from/span> span classstring>vue/span>/span>br>span classline>span classkeyword>import/span> { VueClass } span classkeyword>from/span> span classstring>./declarations/span>/span>br>span classline>span classkeyword>import/span> { componentFactory, $internalHooks } span classkeyword>from/span> span classstring>./component/span>/span>br>span classline>/span>br>span classline>span classkeyword>export/span> { createDecorator, VueDecorator, mixins } span classkeyword>from/span> span classstring>./util/span>/span>br>span classline>/span>br>span classline>span classfunction>span classkeyword>function/span> span classtitle>Component/span> <span classtitle>V/span> span classtitle>extends/span> span classtitle>Vue/span>>(span classparams>options: ComponentOptions<V> & ThisType<V>/span>): <span classtitle>VC/span> span classtitle>extends/span> span classtitle>VueClass/span><span classtitle>V/span>>>(span classparams>target: VC/span>) > span classtitle>VC/span>/span>/span>br>span classline>span classfunction>span classfunction>span classkeyword>function/span> span classtitle>Component/span> <span classtitle>VC/span> span classtitle>extends/span> span classtitle>VueClass/span><span classtitle>Vue/span>>>(span classparams>target: VC/span>): span classtitle>VC/span>/span>/span>/span>br>span classline>span classfunction>span classfunction>span classfunction>span classkeyword>function/span> span classtitle>Component/span> (span classparams>options: ComponentOptions<Vue> | VueClass<Vue>/span>): span classtitle>any/span> /span>{/span>/span>/span>br>span classline>span classfunction>span classfunction> span classtitle>if/span> (span classparams>span classkeyword>typeof/span> options span classkeyword>function/span>/span>) /span>{/span>/span>br>span classline>span classfunction> span classtitle>return/span> span classtitle>componentFactory/span>(span classparams>options/span>)/span>/span>br>span classline>span classfunction> }/span>/span>br>span classline>span classfunction> span classtitle>return/span> span classfunction>span classkeyword>function/span> (span classparams>Component: VueClass<Vue>/span>) /span>{/span>/span>br>span classline>span classfunction> span classtitle>return/span> span classtitle>componentFactory/span>(span classparams>Component, options/span>)/span>/span>br>span classline>span classfunction> }/span>/span>br>span classline>span classfunction>}/span>/span>br>span classline>span classfunction>/span>/span>br>span classline>span classfunction>span classtitle>Component/span>.span classtitle>registerHooks/span> span classfunction>span classkeyword>function/span> span classtitle>registerHooks/span> (span classparams>keys: span classbuilt_in>string/span>/span>): span classtitle>void/span> /span>{/span>/span>br>span classline>span classfunction> span classtitle>$internalHooks/span>.span classtitle>push/span>(span classparams>...keys/span>)/span>/span>br>span classline>span classfunction>}/span>/span>br>span classline>span classfunction>/span>/span>br>span classline>span classfunction>span classtitle>export/span> span classtitle>default/span> span classtitle>Component/span>/span>/span>br>/pre>/td>/tr>/table>/figure>p>vue/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>import/span> { Vue } span classkeyword>from/span> span classstring>./vue/span>;/span>br>span classline>span classkeyword>import/span> span classstring>./umd/span>;/span>br>span classline>/span>br>span classline>span classkeyword>export/span> span classkeyword>default/span> Vue;/span>br>span classline>/span>br>span classline>span classkeyword>export/span> {/span>br>span classline> CreateElement,/span>br>span classline> VueConstructor/span>br>span classline>} span classkeyword>from/span> span classstring>./vue/span>;/span>br>span classline>/span>br>span classline>span classcomment>// .../span>/span>br>/pre>/td>/tr>/table>/figure>p>通过展示三个库对外暴露接口的 index.ts 文件,我们就可以很清晰看到它们暴露的内容,以及依赖关系。/p>p>我们再来看一下如何使用 装饰器、class 和 TypeScript 来写 Vue 组件:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>import/span> { Vue, Component, Prop } span classkeyword>from/span> span classstring>vue-property-decorator/span>/span>br>span classline>/span>br>span classline>span classmeta>@Component/span>/span>br>span classline>span classkeyword>export/span> span classkeyword>default/span> span classkeyword>class/span> YourComponent span classkeyword>extends/span> Vue {/span>br>span classline> span classmeta>@Prop/span>({ span classkeyword>default/span>: span classstring>default value/span> }) readonly title!: span classbuilt_in>string/span>/span>br>span classline>/span>br>span classline> message: span classbuilt_in>string/span> span classstring>hello, world/span>;/span>br>span classline>/span>br>span classline> onClick(): span classbuilt_in>void/span> {}/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>p>code>YourComponent extends Vue/code> 就可以继承 Vue 上面所有的属性和方法,方法包括实例方法和静态方法,YourComponent 可以直接通过 this 调用 Vue 上的实例方法,在开发时,编辑器也会自动提示。我们来看一下 Vue 的类型声明:/p>p>下面是 Vue 实例上的属性和方法的声明/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>span classline>21/span>br>span classline>22/span>br>span classline>23/span>br>span classline>24/span>br>span classline>25/span>br>span classline>26/span>br>span classline>27/span>br>span classline>28/span>br>span classline>29/span>br>span classline>30/span>br>span classline>31/span>br>span classline>32/span>br>span classline>33/span>br>span classline>34/span>br>span classline>35/span>br>span classline>36/span>br>span classline>37/span>br>span classline>38/span>br>span classline>39/span>br>span classline>40/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>export/span> span classkeyword>interface/span> Vue {/span>br>span classline> readonly $el: Element;/span>br>span classline> readonly $options: ComponentOptions<Vue>;/span>br>span classline> readonly $parent: Vue;/span>br>span classline> readonly $root: Vue;/span>br>span classline> readonly $children: Vue;/span>br>span classline> readonly $refs: { key: span classbuilt_in>string/span>: Vue | Element | (Vue | Element) | span classliteral>undefined/span> };/span>br>span classline> readonly $slots: { key: span classbuilt_in>string/span>: VNode | span classliteral>undefined/span> };/span>br>span classline> readonly $scopedSlots: { key: span classbuilt_in>string/span>: NormalizedScopedSlot | span classliteral>undefined/span> };/span>br>span classline> readonly $isServer: span classbuilt_in>boolean/span>;/span>br>span classline> readonly $data: Record<span classbuilt_in>string/span>, span classbuilt_in>any/span>>;/span>br>span classline> readonly $props: Record<span classbuilt_in>string/span>, span classbuilt_in>any/span>>;/span>br>span classline> readonly $ssrContext: span classbuilt_in>any/span>;/span>br>span classline> readonly $vnode: VNode;/span>br>span classline> readonly $attrs: Record<span classbuilt_in>string/span>, span classbuilt_in>string/span>>;/span>br>span classline> readonly $listeners: Record<span classbuilt_in>string/span>, span classbuilt_in>Function/span> | span classbuilt_in>Function/span>>;/span>br>span classline>/span>br>span classline> $mount(elementOrSelector?: Element | span classbuilt_in>string/span>, hydrating?: span classbuilt_in>boolean/span>): span classkeyword>this/span>;/span>br>span classline> $forceUpdate(): span classbuilt_in>void/span>;/span>br>span classline> $destroy(): span classbuilt_in>void/span>;/span>br>span classline> $span classkeyword>set/span>: span classkeyword>typeof/span> Vue.set;/span>br>span classline> $span classkeyword>delete/span>: span classkeyword>typeof/span> Vue.delete;/span>br>span classline> $watch(/span>br>span classline> expOrFn: span classbuilt_in>string/span>,/span>br>span classline> callback: span classfunction>(span classparams>span classkeyword>this/span>: span classkeyword>this/span>, n: span classbuilt_in>any/span>, o: span classbuilt_in>any/span>/span>) >/span> span classbuilt_in>void/span>,/span>br>span classline> options?: WatchOptions/span>br>span classline> ): span classfunction>(span classparams>(span classparams>/span>) > span classbuilt_in>void/span>/span>);/span>/span>br>span classline>span classfunction> $span classparams>watch/span><span classparams>T/span>>(span classparams>/span>/span>/span>br>span classline>span classfunction>span classparams> expOrFn: (span classparams>span classkeyword>this/span>: span classkeyword>this/span>/span>) > T,/span>/span>/span>br>span classline>span classfunction>span classparams> callback: (span classparams>span classkeyword>this/span>: span classkeyword>this/span>, n: T, o: T/span>) > span classbuilt_in>void/span>,/span>/span>/span>br>span classline>span classfunction>span classparams> options?: WatchOptions/span>/span>/span>br>span classline>span classfunction>span classparams> /span>): (span classparams>(span classparams>/span>) > span classbuilt_in>void/span>/span>);/span>/span>br>span classline>span classfunction> $span classparams>on/span>(span classparams>event: span classbuilt_in>string/span> | span classbuilt_in>string/span>, callback: span classbuilt_in>Function/span>/span>): span classparams>this/span>;/span>/span>br>span classline>span classfunction> $span classparams>once/span>(span classparams>event: span classbuilt_in>string/span> | span classbuilt_in>string/span>, callback: span classbuilt_in>Function/span>/span>): span classparams>this/span>;/span>/span>br>span classline>span classfunction> $span classparams>off/span>(span classparams>event?: span classbuilt_in>string/span> | span classbuilt_in>string/span>, callback?: span classbuilt_in>Function/span>/span>): span classparams>this/span>;/span>/span>br>span classline>span classfunction> $span classparams>emit/span>(span classparams>event: span classbuilt_in>string/span>, ...args: span classbuilt_in>any/span>/span>): span classparams>this/span>;/span>/span>br>span classline>span classfunction> $span classparams>nextTick/span>(span classparams>callback: (span classparams>span classkeyword>this/span>: span classkeyword>this/span>/span>) > span classbuilt_in>void/span>/span>): span classparams>void/span>;/span>/span>br>span classline>span classfunction> $span classparams>nextTick/span>span classparams>()/span>: span classparams>Promise/span><span classparams>void/span>>;/span>/span>br>span classline>span classfunction> $span classparams>createElement/span>: span classparams>CreateElement/span>;/span>/span>br>span classline>span classfunction>}/span>/span>br>/pre>/td>/tr>/table>/figure>p>下面是 Vue 的静态属性和静态方法,也即 Vue 构造函数上的属性和方法:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>span classline>21/span>br>span classline>22/span>br>span classline>23/span>br>span classline>24/span>br>span classline>25/span>br>span classline>26/span>br>span classline>27/span>br>span classline>28/span>br>span classline>29/span>br>span classline>30/span>br>span classline>31/span>br>span classline>32/span>br>span classline>33/span>br>span classline>34/span>br>span classline>35/span>br>span classline>36/span>br>span classline>37/span>br>span classline>38/span>br>span classline>39/span>br>span classline>40/span>br>span classline>41/span>br>span classline>42/span>br>span classline>43/span>br>span classline>44/span>br>span classline>45/span>br>span classline>46/span>br>span classline>47/span>br>span classline>48/span>br>span classline>49/span>br>span classline>50/span>br>span classline>51/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>export/span> span classkeyword>interface/span> VueConstructor<V span classkeyword>extends/span> Vue Vue> {/span>br>span classline> span classkeyword>new/span> <Data object, Methods object, Computed object, PropNames span classkeyword>extends/span> span classbuilt_in>string/span> never>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): CombinedVueInstance<V, Data, Methods, Computed, Record<PropNames, span classbuilt_in>any/span>>>;/span>br>span classline> span classcomment>// ideally, the return type should just contain Props, not Record<keyof Props, any>. But TS requires to have Base constructors with the same return type./span>/span>br>span classline> span classkeyword>new/span> <Data object, Methods object, Computed object, Props object>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): CombinedVueInstance<V, Data, Methods, Computed, Record<keyof Props, span classbuilt_in>any/span>>>;/span>br>span classline> span classkeyword>new/span> (options?: ComponentOptions<V>): CombinedVueInstance<V, object, object, object, Record<keyof object, span classbuilt_in>any/span>>>;/span>br>span classline>/span>br>span classline> extend<Data, Methods, Computed, PropNames span classkeyword>extends/span> span classbuilt_in>string/span> never>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): ExtendedVue<V, Data, Methods, Computed, Record<PropNames, span classbuilt_in>any/span>>>;/span>br>span classline> extend<Data, Methods, Computed, Props>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): ExtendedVue<V, Data, Methods, Computed, Props>;/span>br>span classline> extend<PropNames span classkeyword>extends/span> span classbuilt_in>string/span> never>(definition: FunctionalComponentOptions<Record<PropNames, span classbuilt_in>any/span>>, PropNames>): ExtendedVue<V, {}, {}, {}, Record<PropNames, span classbuilt_in>any/span>>>;/span>br>span classline> extend<Props>(definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>>): ExtendedVue<V, {}, {}, {}, Props>;/span>br>span classline> extend(options?: ComponentOptions<V>): ExtendedVue<V, {}, {}, {}, {}>;/span>br>span classline>/span>br>span classline> nextTick<T>span classfunction>(span classparams>callback: (span classparams>span classkeyword>this/span>: T/span>) > span classbuilt_in>void/span>, context?: T/span>): span classparams>void/span>;/span>/span>br>span classline>span classfunction> span classparams>nextTick/span>span classparams>()/span>: span classparams>Promise/span><span classparams>void/span>>/span>/span>br>span classline>span classfunction> span classparams>set/span><span classparams>T/span>>(span classparams>object: object, key: span classbuilt_in>string/span> | span classbuilt_in>number/span>, value: T/span>): span classparams>T/span>;/span>/span>br>span classline>span classfunction> span classparams>set/span><span classparams>T/span>>(span classparams>array: T, key: span classbuilt_in>number/span>, value: T/span>): span classparams>T/span>;/span>/span>br>span classline>span classfunction> span classparams>delete/span>(span classparams>object: object, key: span classbuilt_in>string/span> | span classbuilt_in>number/span>/span>): span classparams>void/span>;/span>/span>br>span classline>span classfunction> span classparams>delete/span><span classparams>T/span>>(span classparams>array: T, key: span classbuilt_in>number/span>/span>): span classparams>void/span>;/span>/span>br>span classline>span classfunction>/span>/span>br>span classline>span classfunction> span classparams>directive/span>(span classparams>/span>/span>/span>br>span classline>span classfunction>span classparams> id: span classbuilt_in>string/span>,/span>/span>/span>br>span classline>span classfunction>span classparams> definition?: DirectiveOptions | DirectiveFunction/span>/span>/span>br>span classline>span classfunction>span classparams> /span>): span classparams>DirectiveOptions/span>;/span>/span>br>span classline>span classfunction> span classparams>filter/span>(span classparams>id: span classbuilt_in>string/span>, definition?: span classbuilt_in>Function/span>/span>): span classparams>Function/span>;/span>/span>br>span classline>span classfunction>/span>/span>br>span classline>span classfunction> span classparams>component/span>(span classparams>id: span classbuilt_in>string/span>/span>): span classparams>VueConstructor/span>;/span>/span>br>span classline>span classfunction> span classparams>component/span><span classparams>VC/span> span classparams>extends/span> span classparams>VueConstructor/span>>(span classparams>id: span classbuilt_in>string/span>, constructor: VC/span>): span classparams>VC/span>;/span>/span>br>span classline>span classfunction> span classparams>component/span><span classparams>Data/span>, span classparams>Methods/span>, span classparams>Computed/span>, span classparams>Props/span>>(span classparams>id: span classbuilt_in>string/span>, definition: AsyncComponent<Data, Methods, Computed, Props>/span>): span classparams>ExtendedVue/span><span classparams>V/span>, span classparams>Data/span>, span classparams>Methods/span>, span classparams>Computed/span>, span classparams>Props/span>>;/span>/span>br>span classline>span classfunction> span classparams>component/span><span classparams>Data/span>, span classparams>Methods/span>, span classparams>Computed/span>, span classparams>PropNames/span> span classparams>extends/span> span classparams>string/span> span classparams>never/span>>(span classparams>id: span classbuilt_in>string/span>, definition?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>/span>): span classparams>ExtendedVue/span><span classparams>V/span>, span classparams>Data/span>, span classparams>Methods/span>, span classparams>Computed/span>, span classparams>Record/span><span classparams>PropNames/span>, span classparams>any/span>>>;/span>/span>br>span classline>span classfunction> span classparams>component/span><span classparams>Data/span>, span classparams>Methods/span>, span classparams>Computed/span>, span classparams>Props/span>>(span classparams>id: span classbuilt_in>string/span>, definition?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>/span>): span classparams>ExtendedVue/span><span classparams>V/span>, span classparams>Data/span>, span classparams>Methods/span>, span classparams>Computed/span>, span classparams>Props/span>>;/span>/span>br>span classline>span classfunction> span classparams>component/span><span classparams>PropNames/span> span classparams>extends/span> span classparams>string/span>>(span classparams>id: span classbuilt_in>string/span>, definition: FunctionalComponentOptions<Record<PropNames, span classbuilt_in>any/span>>, PropNames>/span>): span classparams>ExtendedVue/span><span classparams>V/span>, {}, {}, {}, span classparams>Record/span><span classparams>PropNames/span>, span classparams>any/span>>>;/span>/span>br>span classline>span classfunction> span classparams>component/span><span classparams>Props/span>>(span classparams>id: span classbuilt_in>string/span>, definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>>/span>): span classparams>ExtendedVue/span><span classparams>V/span>, {}, {}, {}, span classparams>Props/span>>;/span>/span>br>span classline>span classfunction> span classparams>component/span>(span classparams>id: span classbuilt_in>string/span>, definition?: ComponentOptions<V>/span>): span classparams>ExtendedVue/span><span classparams>V/span>, {}, {}, {}, {}>;/span>/span>br>span classline>span classfunction>/span>/span>br>span classline>span classfunction> span classparams>use/span><span classparams>T/span>>(span classparams>plugin: PluginObject<T> | PluginFunction<T>, options?: T/span>): span classparams>VueConstructor/span><span classparams>V/span>>;/span>/span>br>span classline>span classfunction> span classparams>use/span>(span classparams>plugin: PluginObject<span classbuilt_in>any/span>> | PluginFunction<span classbuilt_in>any/span>>, ...options: span classbuilt_in>any/span>/span>): span classparams>VueConstructor/span><span classparams>V/span>>;/span>/span>br>span classline>span classfunction> span classparams>mixin/span>(span classparams>mixin: VueConstructor | ComponentOptions<Vue>/span>): span classparams>VueConstructor/span><span classparams>V/span>>;/span>/span>br>span classline>span classfunction> span classparams>compile/span>(span classparams>template: span classbuilt_in>string/span>/span>): {/span>/span>br>span classline>span classfunction> span classparams>render/span>(span classparams>createElement: span classkeyword>typeof/span> Vue.prototype.$createElement/span>): span classparams>VNode/span>;/span>/span>br>span classline>span classfunction> span classparams>staticRenderFns/span>: (span classparams>(span classparams>/span>) > VNode/span>);/span>/span>br>span classline>span classfunction> };/span>/span>br>span classline>span classfunction>/span>/span>br>span classline>span classfunction> span classparams>observable/span><span classparams>T/span>>(span classparams>obj: T/span>): span classparams>T/span>;/span>/span>br>span classline>span classfunction>/span>/span>br>span classline>span classfunction> span classparams>util/span>: {/span>/span>br>span classline>span classfunction> span classparams>warn/span>(span classparams>msg: span classbuilt_in>string/span>, vm?: InstanceType<VueConstructor>/span>): span classparams>void/span>;/span>/span>br>span classline>span classfunction> };/span>/span>br>span classline>span classfunction>/span>/span>br>span classline>span classfunction> span classparams>config/span>: span classparams>VueConfiguration/span>;/span>/span>br>span classline>span classfunction> span classparams>version/span>: span classparams>string/span>;/span>/span>br>span classline>span classfunction>}/span>/span>br>/pre>/td>/tr>/table>/figure>h2 idzhuang-shi-qi-jiang-vue-class-zhuan-huan-wei-pei-zhi-dui-xiang-de-guo-cheng>装饰器将 vue class 转换为配置对象的过程/h2>p>下面我们讲一下 vue 的装饰器是如何将 vue class 形式的组件,转换为 vue 引擎能识别的传统的配置对象的。/p>h3 idzhuang-shi-qi-ding-yi>装饰器定义/h3>p>装饰器的定义如下:/p>blockquote>p>装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。/p>/blockquote>p>简单示例如下:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span> span classtitle>red/span>(span classparams>target/span>) /span>{/span>br>span classline> target.color span classstring>red/span>;/span>br>span classline>}/span>br>span classline>/span>br>span classline>span classmeta>@red/span>/span>br>span classline>span classkeyword>class/span> MyComponent {/span>br>span classline>}/span>br>span classline>/span>br>span classline>MyComponent.color span classcomment>// red/span>/span>br>/pre>/td>/tr>/table>/figure>p>我们定义了一个 red 装饰器,作用是给被装饰类添加一个值为 code>red/code> 的 color 属性。/p>p>我们看到,装饰器本质上就是一个函数,可以对数据做一定的转换(修饰)。/p>p>我们还可以利用装饰器工厂,来提高装饰器的复用性,例如我们想要一个可以给组件添加任意颜色的装饰器,则可以这样写:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span> span classtitle>addColor/span>(span classparams>color/span>) /span>{/span>br>span classline> span classkeyword>return/span> (target) {/span>br>span classline> target.color;/span>br>span classline> }/span>br>span classline>}/span>br>span classline>/span>br>span classline>span classmeta>@addColor/span>(span classstring>red/span>)/span>br>span classline>span classkeyword>class/span> MyComponent1 {/span>br>span classline>}/span>br>span classline>MyComponent1.color span classcomment>// red/span>/span>br>span classline>/span>br>span classline>span classmeta>@addColor/span>(span classstring>blue/span>)/span>br>span classline>span classkeyword>class/span> MyComponent2 {/span>br>span classline>}/span>br>span classline>MyComponent2.color span classcomment>// blue/span>/span>br>/pre>/td>/tr>/table>/figure>p>装饰器工厂,说白了,就是一个生成装饰器的函数/p>p>装饰器的概念,就简单介绍到这里。/p>h3 idzhuang-shi-qi-de-zhi-xing-shun-xu>装饰器的执行顺序/h3>p>在TypeScript中,装饰器的执行顺序为:/p>ol>li>类成员装饰器,一般按书写顺序从前往后执行;/li>li>类装饰器;/li>/ol>p>关于装饰器调用顺序,网上几乎所有中文资料都是错的!/p>p>因此对于下面使用装饰器的 Vue class 组件:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>import/span> { Vue, Component, Prop, Ref } span classkeyword>from/span> span classstring>vue-property-decorator/span>/span>br>span classline>/span>br>span classline>span classmeta>@Component/span>/span>br>span classline>span classkeyword>export/span> span classkeyword>default/span> span classkeyword>class/span> YourComponent span classkeyword>extends/span> Vue {/span>br>span classline> span classmeta>@Ref/span> readonly elementRef!: HTMLElement;/span>br>span classline> span classmeta>@Prop/span>({ span classkeyword>default/span>: span classstring>default value/span> }) readonly title!: span classbuilt_in>string/span>/span>br>span classline>/span>br>span classline> message: span classbuilt_in>string/span> span classstring>hello, world/span>;/span>br>span classline>/span>br>span classline> onClick(): span classbuilt_in>void/span> {}/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>p>会先执行类属性上的装饰器,如 @Ref、@Prop,这两个装饰器,会将被修饰的属性收集到对应类型的数组中(refs 数组,props 数组),然后是方法的装饰器,最后会执行 @Component ,对刚才装饰器收集的属性、方法,以及为修饰的属性、方法做分类处理,最终转成 Vue 引擎能识别的、原始的 vue 组件配置对象:/p>figure classhighlight ts>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>span classline>21/span>br>span classline>22/span>br>span classline>23/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>export/span> span classkeyword>default/span> {/span>br>span classline> props: {/span>br>span classline> title: {/span>br>span classline> span classkeyword>default/span>: span classstring>default value/span>,/span>br>span classline> },/span>br>span classline> },/span>br>span classline>/span>br>span classline> data() {/span>br>span classline> message: span classstring>hello, world/span>,/span>br>span classline> },/span>br>span classline>/span>br>span classline> methods: {/span>br>span classline> onClick() {}/span>br>span classline> },/span>br>span classline>/span>br>span classline> computed: {/span>br>span classline> elementRef: {/span>br>span classline> cache: span classliteral>false/span>,/span>br>span classline> span classkeyword>get/span>(span classkeyword>this/span>: Vue) {/span>br>span classline> span classkeyword>return/span> span classkeyword>this/span>.$refsspan classstring>elementRef/span>/span>br>span classline> },/span>br>span classline> }/span>br>span classline> }/span>br>/pre>/td>/tr>/table>/figure>p>使用 vue-property-decorator 装饰器,将 vue class 转换为 vue 配置对象的过程,就解释完了。/p>h2 idcan-kao>参考/h2>ul>li>a hrefhttps://www.tslang.cn/docs/handbook/decorators.html target_blank relnoopener>装饰器/a>/li>/ul> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2021/11/15/Lerna-Cheatsheet/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2021/11/15/Lerna-Cheatsheet/ classpost-title-link itempropurl>Lerna 备忘录/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2021-11-15 15:35:12 itempropdateCreated datePublished datetime2021-11-15T15:35:12+00:00>2021-11-15/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> p>a hrefhttps://github.com/lerna/lerna target_blank relnoopener>Lerna/a> 本身是一个非常好的 monorepo (单仓库-多项目)管理工具,当经常会因为时间久了不用,而导致生疏,因此总结一些常用的 Lerna 操作,以备后续用到时参考。/p> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2021/09/10/nvm-cli-advance-guide/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2021/09/10/nvm-cli-advance-guide/ classpost-title-link itempropurl>nvm 高级命令指南/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2021-09-10 10:30:56 itempropdateCreated datePublished datetime2021-09-10T10:30:56+00:00>2021-09-10/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> p>本文整理了一些 nvm 不常用、但非常有用的命令。主要分为以下几大类:/p>ul>li>reinstall-packages version> Reinstall global code>npm/code> packages contained in version> to current version/version>/version>/li>/ul>p>由于使用 npm install -g package> 安装全局 npm 包,是安装到当前使用的版本的 node 下面,在安装新版本的 node 或者 切换 node 版本后,之前安装的全局 npm 包往往就不生效了,比如,切换 node 版本后,code>yarn/code> 命令失效。/package>/p>p>这时可以用 code>nvm reinstall-packages <version>/code> 来将之前 node 下面所有的全局 npm 包都重新安装到当前使用的 node 版本中。/p>p>若想查看当前 node 版本中都安装了哪些全局 npm 包,可以使用 code>npm ls -g/code> 来查看。/p> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2021/07/25/VSCode-Cheat-Sheet/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2021/07/25/VSCode-Cheat-Sheet/ classpost-title-link itempropurl>VSCode Cheat Sheet -- 备忘录/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2021-07-25 10:07:23 itempropdateCreated datePublished datetime2021-07-25T10:07:23+00:00>2021-07-25/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> p>VSCode 入门简单、操作方便、扩展灵活的特点,让 VSCode 在编程届(尤其是前端)越来越流行;/p>p>而相比之下,Emacs 学习和维护成本都很高,导致用户群较小,开发和维护面临后继无人的局面。Emacs 相对于 VSCode 的优势已经不明显。Emacs 的优势:快捷键、magit 等,在 VSCode 上也都有相应的实现,因此从 Emacs 迁移到 VSCode 的成本也没有想象的那么大。/p>p>而缺点却比较多:/p>ul>li>出问题时,提示不友好,不知道错在哪里;/li>li>各种小问题不断,每个小问题可能都要查半天,时间成本比较高;/li>li>Emacs 配置比较复杂,需要学习 elisp 语言;/li>/ul>p>鉴于上述几点,本人也要从使用了多年的 Emacs 迁移到 VSCode 了。在此想到了几句话:唯一不变的是变化,选择比努力更重要。/p>p>本文将是本人学习 VSCode 总结的备忘录,后面遇到之前用过、总结过、但不熟悉的地方,可以快速查找到解决方法。主要是快捷键/p>h2 idmac-os-kuai-jie-jian>macOS 快捷键/h2>h3 idming-ci-jie-shi>名词解释/h3>ul>li>Explorer: 左侧边栏;/li>li>Editor: 编辑区;/li>li>Editor Group: 编辑组,一个编辑组可以包含多个打开的文件。可以使用 code>CMD + \/code> 来分割编辑组;/li>/ul>h3 idge-qu-yu-qie-huan>各区域切换/h3>table>thead>tr>th>快捷键/th>th>解释/th>/tr>/thead>tbody>tr>td>CMD + 0/td>td>左侧边栏/td>/tr>tr>td>CMD + 1, CMD + 2, CMD + 3…/td>td>聚焦 1st,2nd,3rd editor group/td>/tr>tr>td>CMD + B/td>td>切换左侧边栏是否可见/td>/tr>/tbody>/table>h3 idshe-zhi>设置/h3>!-- | ⌘k ⌘s | 调出快捷键设置面板 | --> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2021/04/30/npm-cli-advance-guide/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2021/04/30/npm-cli-advance-guide/ classpost-title-link itempropurl>npm 命令高级指南/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2021-04-30 11:23:56 itempropdateCreated datePublished datetime2021-04-30T11:23:56+00:00>2021-04-30/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> p>本文整理了一些 npm 不常用、但非常有用的命令。主要分为以下几大类:/p>ul>li>p>查看 npm 源(registry) 上的 npm 包信息/p>ul>li>npm view/li>/ul>/li>li>p>开发 npm 包时,用到的命令/p>ul>li>npm publish/li>li>npm pack/li>li>npm link & npm unlink/li>li>npm set-script/li>/ul>/li>li>p>查看已安装的 npm 包的本地信息/p>ul>li>npm ls/li>li>npm edit/li>li>npm explore/li>li>npm explain | npm why/li>/ul>/li>li>p>浏览器查看 npm 包信息/p>ul>li>npm docs/li>li>npm repo/li>/ul>/li>li>p>使用 npm 包开发其他项目时,用到的命令/p>ul>li>npm outdated/li>li>npm update/li>/ul>/li>li>p>其他 npm 命令/p>ul>li>npm config/li>li>npm bin/li>li>npm completion/li>/ul>/li>/ul>h1 idcha-kan-npm-yuan-registry-shang-de-npm-bao-xin-xi>查看 npm 源(registry) 上的 npm 包信息/h1>ul>li>npm view: 最为灵活、最为强大的 查看 npm 包信息的命令;/li>/ul>p>由于 code>npm view/code> 太灵活,所以需要单独一小节进行讲解。/p>h2 idnpm-view>npm view/h2>p>查看 npm 源上包的信息/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>/pre>/td>td classcode>pre>span classline>npm view <@scope>/<name>@<version> <field>.<subfield>.../span>br>span classline>/span>br>span classline>aliases: info, show, v/span>br>/pre>/td>/tr>/table>/figure>h3 idcha-kan-bao-de-ji-ben-xin-xi>查看包的基本信息/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm view <pkg>/span>br>/pre>/td>/tr>/table>/figure>p>示例:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>/pre>/td>td classcode>pre>span classline>› npm view ramda/span>br>span classline>/span>br>span classline>ramda@0.27.1 | MIT | deps: none | versions: 52/span>br>span classline>A practical functional library span classkeyword>for/span> JavaScript programmers./span>br>span classline>https://ramdajs.com//span>br>span classline>/span>br>span classline>dist/span>br>span classline>.tarball: http://r.npm.sankuai.com/ramda/download/ramda-0.27.1.tgz/span>br>span classline>.shasum: 66fc2df3ef873874ffc2da6aa8984658abacf5c9/span>br>span classline>/span>br>span classline>maintainers:/span>br>span classline>- aromano <aromano@preemptsecurity.com>/span>br>span classline>- bradcomp <notpmoc84@hotmail.com>/span>br>span classline>- .../span>br>span classline>/span>br>span classline>dist-tags:/span>br>span classline>0.2.0: 0.2.0 es-rc: 0.24.1-es.rc3 latest: 0.27.1/span>br>span classline>/span>br>span classline>published 9 months ago by davidchambers <dc@davidchambers.me>/span>br>/pre>/td>/tr>/table>/figure>h3 idgeng-jia-ling-huo-de-cha-kan-bao-de-xiang-xi-xin-xi>更加灵活的查看包的详细信息/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm view <pkg>@<version> <field>.<subfield>.../span>br>/pre>/td>/tr>/table>/figure>h4 idcha-kan-bao-de-yi-lai>查看包的依赖/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>/pre>/td>td classcode>pre>span classline>› npm view http-server dependencies/span>br>span classline>{/span>br>span classline> span classstring>basic-auth/span>: span classstring>^1.0.3/span>,/span>br>span classline> colors: span classstring>^1.4.0/span>,/span>br>span classline> corser: span classstring>^2.0.1/span>,/span>br>span classline> ecstatic: span classstring>^3.3.2/span>,/span>br>span classline> span classstring>http-proxy/span>: span classstring>^1.18.0/span>,/span>br>span classline> minimist: span classstring>^1.2.5/span>,/span>br>span classline> opener: span classstring>^1.5.1/span>,/span>br>span classline> portfinder: span classstring>^1.0.25/span>,/span>br>span classline> span classstring>secure-compare/span>: span classstring>3.0.1/span>,/span>br>span classline> union: span classstring>~0.5.0/span>/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>h4 idcha-kan-bao-de-zui-xin-ban-ben>查看包的最新版本/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>/pre>/td>td classcode>pre>span classline>› npm view ramda version/span>br>span classline>0.27.1/span>br>/pre>/td>/tr>/table>/figure>h4 idcha-kan-bao-fa-bu-de-suo-you-ban-ben>查看包发布的所有版本/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>span classline>21/span>br>/pre>/td>td classcode>pre>span classline>› npm view ramda versions/span>br>span classline>/span>br>span classline> span classstring>0.1.0/span>, span classstring>0.1.1/span>, span classstring>0.1.2/span>,/span>br>span classline> span classstring>0.1.4/span>, span classstring>0.1.5/span>, span classstring>0.2.0/span>,/span>br>span classline> span classstring>0.2.1/span>, span classstring>0.2.2/span>, span classstring>0.2.3/span>,/span>br>span classline> span classstring>0.2.4/span>, span classstring>0.3.0/span>, span classstring>0.4.0/span>,/span>br>span classline> span classstring>0.4.1/span>, span classstring>0.4.2/span>, span classstring>0.4.3/span>,/span>br>span classline> span classstring>0.5.0/span>, span classstring>0.6.0/span>, span classstring>0.7.0/span>,/span>br>span classline> span classstring>0.7.1/span>, span classstring>0.7.2/span>, span classstring>0.8.0/span>,/span>br>span classline> span classstring>0.9.0/span>, span classstring>0.9.1/span>, span classstring>0.10.0/span>,/span>br>span classline> span classstring>0.11.0/span>, span classstring>0.12.0/span>, span classstring>0.13.0/span>,/span>br>span classline> span classstring>0.14.0/span>, span classstring>0.15.0/span>, span classstring>0.15.1/span>,/span>br>span classline> span classstring>0.16.0/span>, span classstring>0.17.0/span>, span classstring>0.17.1/span>,/span>br>span classline> span classstring>0.18.0/span>, span classstring>0.19.0/span>, span classstring>0.19.1/span>,/span>br>span classline> span classstring>0.20.0/span>, span classstring>0.20.1/span>, span classstring>0.21.0/span>,/span>br>span classline> span classstring>0.22.0/span>, span classstring>0.22.1/span>, span classstring>0.23.0/span>,/span>br>span classline> span classstring>0.24.0/span>, span classstring>0.24.1-es.rc1/span>, span classstring>0.24.1-es.rc2/span>,/span>br>span classline> span classstring>0.24.1-es.rc3/span>, span classstring>0.24.1/span>, span classstring>0.25.0/span>,/span>br>span classline> span classstring>0.26.0/span>, span classstring>0.26.1/span>, span classstring>0.27.0/span>,/span>br>span classline> span classstring>0.27.1/span>/span>br>span classline>/span>br>/pre>/td>/tr>/table>/figure>h4 idcha-kan-zhi-ding-ban-ben-fan-wei-nei-de-bao-de-fa-bu-qing-kuang>查看指定版本范围内的包的发布情况/h4>p>查看自 0.25.0 以来,所有版本 ramda 包的信息/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm view ramda@span classstring>>0.25.0/span>/span>br>/pre>/td>/tr>/table>/figure>h4 idcha-kan-npm-bao-guan-wang-di-zhi>查看 npm 包官网地址/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm view ramda homepage/span>br>/pre>/td>/tr>/table>/figure>h4 idcha-kan-npm-bao-cang-ku-di-zhi>查看 npm 包仓库地址/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm view ramda repository.url/span>br>/pre>/td>/tr>/table>/figure>h4 idtong-guo-shell-jiao-ben-zu-he-chu-ling-huo-de-cha-xun-ming-ling>通过 shell 脚本,组合出灵活的查询命令/h4>p>可以通过一些 Shell 脚本轻松查看有关依赖项的信息。例如,要查看有关ronn所依赖的opts版本的所有数据,如下所示:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm view opts@$(npm view ronn dependencies.opts)/span>br>/pre>/td>/tr>/table>/figure>p>更多 code>npm view/code> 的高级用法,可以查看 a hrefhttps://docs.npmjs.com/cli/v7/commands/npm-view target_blank relnoopener>npm view 官方文档/a>/p>h1 idkai-fa-npm-bao-she-ji-de-ming-ling>开发 npm 包涉及的命令/h1>ul>li>p>npm publish: 将包发布到 npm 源(registry) 上去;/p>/li>li>p>npm pack: 打包;/p>/li>li>p>npm link: 将当前 package 文件夹软链到全局 npm 环境中;/p>/li>li>p>npm unlink: 断开全局软链时,其实就是 npm uninstall 的别名;/p>/li>li>p>npm set-script: 在 package.json 的 code>"scripts"/code> 字段中设置任务;/p>/li>/ul>h2 idnpm-publish-fa-bao>npm publish – 发包/h2>p>将包发布到 npm 源(registry) 上去。/p>h3 idmo-ni-fa-bao>模拟发包/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm publish --dry-run/span>br>/pre>/td>/tr>/table>/figure>p>code>npm publish --dry-run/code> 只是模拟发包,并不会真正发包。会将发包过程中的所有信息都打印出来,用作发包前进行信息确认。也可以使用 code>npm pack --dry-run/code>/p>h3 idfa-bu-de-bao-zhong-bao-han-de-wen-jian>发布的包中包含的文件/h3>p>要查看软件包中将包含的内容,请运行 code>npx npm-packlist/code>。默认情况下,所有文件都包括在内,但以下情况除外:/p>ul>li>p>始终包含与软件包安装和分发相关的某些文件。例如,code>package.json/code>,code>README.md/code>,code>LICENSE/code> 等。/p>/li>li>p>如果 code>package.json/code> 中有一个 code>"files"/code> 列表字段,则仅包含 code>"files"/code> 指定的文件。(如果指定了目录,则将遵循相同的忽略规则,以递归方式遍历目录并包含目录的内容。)/p>/li>li>p>如果存在 code>.gitignore/code> 或 code>.npmignore/code> 文件,则其中的被忽略文件以及所有子目录都将从软件包中排除。如果两个文件都存在,则将忽略 code>.gitignore/code>,而仅使用 code>.npmignore/code>。/p>p>code>.npmignore/code> 文件遵循与 code>.gitignore/code> 文件相同的模式规则/p>/li>li>p>strong>需要特别注意的是:如果文件匹配某些模式,则除非明确将其添加到 code>package.json/code> 的 “files” 列表中,否则将永远不会将其包括在内,或者在 code>.npmignore/code> 或 code>.gitignore/code> 文件中使用 code>!/code> ,来强制包含需要发布的文件/strong>。/p>p>例如,npm 发包,默认是会忽略 code>.npmrc/code> 文件的,如果我确实需要将 code>.npmrc/code> 包含进发布的包中,则需要使用在 code>.npmignore/code> 或 code>.gitignore/code> 中写入规则 code>!.npmrc/code>,code>!.npmrc/code> 表示强制包含 code>.npmrc/code>。/p>/li>li>p>符号链接永远不会包含在 npm 软件包中。/p>/li>/ul>p>有关已发布的软件包中包含的内容以及如何构建该软件包的详细信息,请查看 a hrefhttps://docs.npmjs.com/cli/v7/using-npm/developers target_blank relnoopener>开发者须知/a> 。/p>h2 idnpm-pack-da-bao>npm pack – 打包/h2>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm pack <@scope>/<pkg>... --dry-run/span>br>/pre>/td>/tr>/table>/figure>p>一般会用到 code>npm pack --dry-run/code> ,看一下即将发布的包的打包情况。加了 code>--dry-run/code> 会后,命令不会真正的执行,只是把之间结果打印出来,以供调试使用。/p>h2 idnpm-link-amp-npm-unlink-ben-di-kai-fa>npm link & npm unlink - 本地开发/h2>p>本地调试利器/p>h3 idnpm-link>npm link/h3>p>将当前 package 文件夹软链到全局 npm 环境中。开发某个 npm 包时,本地调试非常好用。避免了在开发过程中不断重复发包的困扰。/p>p>npm link 分两步:/p>ul>li>p>软链当前的 package;/p>/li>li>p>使用软链过的 package;/p>/li>/ul>p>如下所示:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>/pre>/td>td classcode>pre>span classline>span classbuilt_in>cd/span> ~/projects/node-redis span classcomment># go into the package directory/span>/span>br>span classline>npm link span classcomment># creates global link/span>/span>br>span classline>span classbuilt_in>cd/span> ~/projects/node-bloggy span classcomment># go into some other package directory./span>/span>br>span classline>npm link redis span classcomment># link-install the package/span>/span>br>/pre>/td>/tr>/table>/figure>h3 idnpm-unlink>npm unlink/h3>p>当本地调试完成,想要断开全局软链时,运行下面命令:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm unlink <pkg> -g/span>br>/pre>/td>/tr>/table>/figure>p>code>npm unlink/code> 会将 pkg> 从全局的 npm 环境中移除(断开软链)。/pkg>/p>p>注:code>npm unlink/code> 其实是 code>npm uninstall/code> 的别名,所以运行 code>npm unlink <pkg> -g/code> 等价于 code>npm uninstall <pkg> -g/code> 。/p>h3 idnpm-set-script>npm set-script/h3>p>在 package.json 的 code>"scripts"/code> 字段中设置任务。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm span classbuilt_in>set/span>-script <script> <span classbuilt_in>command/span>>/span>br>/pre>/td>/tr>/table>/figure>p>如果开发的 npm 包在安装时,需要动态修改项目中 code>package.json/code> 文件的 code>"scripts"/code> ,则可以使用该命令进行设置。/p>p>示例:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm span classbuilt_in>set/span>-script start span classstring>http-server ./span>/span>br>/pre>/td>/tr>/table>/figure>figure classhighlight json>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>/pre>/td>td classcode>pre>span classline>{/span>br>span classline> span classattr>name/span>: span classstring>my-project/span>,/span>br>span classline> span classattr>scripts/span>: {/span>br>span classline> span classattr>start/span>: span classstring>http-server ./span>,/span>br>span classline> span classattr>test/span>: span classstring>some existing value/span>/span>br>span classline> }/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>p>向 code>"scripts"/code> 中添加 code>"start"/code> 脚本。/p>h1 idcha-kan-yi-an-zhuang-de-npm-bao-de-ben-di-xin-xi>查看已安装的 npm 包的本地信息/h1>ul>li>p>npm ls: 列出安装的 packages,或者指定 package 的依赖树;/p>/li>li>p>npm edit: 使用默认的编辑器打开当前项目中指定的 npm 包;/p>/li>li>p>npm explore: 进入指定的被安装的 npm 包的目录;/p>/li>li>p>npm explain | npm why: 解释 packages 被安装的原因。主要是把指定包的依赖链条打印出来;/p>/li>/ul>h2 idnpm-ls>npm ls/h2>p>列出安装的 packages。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm ls <pkg> -g/span>br>/pre>/td>/tr>/table>/figure>h3 idda-yin-chu-dang-qian-xiang-mu-yi-an-zhuang-de-shou-ceng-packages>打印出当前项目已安装的首层 packages/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm ls/span>br>/pre>/td>/tr>/table>/figure>h3 idsuo-you-de-yi-lai-yi-yi-lai-shu-de-xing-shi-da-yin-chu-lai>所有的依赖以依赖树的形式打印出来/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm ls -all/span>br>/pre>/td>/tr>/table>/figure>p>当使用 code>--all/code> 时,会将所有的依赖以依赖树的形式打印出来。/p>h3 idda-yin-quan-ju-an-zhuang-de-packages>打印全局安装的 packages/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm ls -g/span>br>/pre>/td>/tr>/table>/figure>h3 idda-yin-zhi-ding-packages-de-an-zhuang-qing-kuang>打印指定 packages 的安装情况/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm ls name@version-range/span>br>/pre>/td>/tr>/table>/figure>p>code>npm ls name@version-range/code> 可以以结构树的形式打印出指定 package 在项目中的安装情况。/p>p>也可以使用 code>npm explain/code> 查看指定的 package 为什么会被安装,作用相当于 code>npm ls/code> ,只不过展示的顺序是反向的。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm explain name/span>br>/pre>/td>/tr>/table>/figure>h2 idnpm-edit>npm edit/h2>p>使用默认的编辑器,直接打开当前项目已经安装的 package 的文件夹。可以省去手动到 node_modules 中查找 package 的麻烦。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm edit <pkg>/span>br>/pre>/td>/tr>/table>/figure>p>例如:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm edit prettier/span>br>/pre>/td>/tr>/table>/figure>p>会打开当前项目下的 code>./node_modules/prettier/code> 目录。/p>h3 idzhi-ding-mo-ren-de-bian-ji-qi-xiu-gai-code-editor-code-bian-liang>指定默认的编辑器,修改 code>$EDITOR/code> 变量/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classbuilt_in>echo/span> span classbuilt_in>export/span> EDITORspan classstring>emacsclient -t/span> >> ~/.zshrc/span>br>/pre>/td>/tr>/table>/figure>h2 idnpm-explore>npm explore/h2>p>在命令行中,进入指定的被安装的 package 文件夹中。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm explore <pkg> -- <span classbuilt_in>command/span>>/span>br>/pre>/td>/tr>/table>/figure>p>示例:/p>ol>li>进入当前项目中某 package 的目录/li>/ol>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>/pre>/td>td classcode>pre>span classline>// 进入当前项目中安装的 prettier 目录/span>br>span classline>npm explore prettier/span>br>span classline>/span>br>span classline>span classbuilt_in>pwd/span>/span>br>span classline>// project/node_modules/prettier/span>br>/pre>/td>/tr>/table>/figure>ol start2>li>进入全局安装的某 package 目录/li>/ol>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>/pre>/td>td classcode>pre>span classline>// 进入全局安装的 prettier 目录/span>br>span classline>npm explore prettier -g/span>br>span classline>/span>br>span classline>span classbuilt_in>pwd/span>/span>br>span classline>// /Users/wangzengdi/.nvm/versions/node/v14.15.4/lib/node_modules/prettier/span>br>/pre>/td>/tr>/table>/figure>h2 idnpm-explain-npm-why>npm explain | npm why/h2>p>解释已安装的 packages,被安装的原因。/p>p>会将当前指定包的依赖关系打印出来(哪些包依赖的指定的包),可以用于解释为什么一个依赖为什么会被安装多次。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>/pre>/td>td classcode>pre>span classline>npm explain <folder | specifier>/span>br>span classline>/span>br>span classline>span classbuilt_in>alias/span>: why/span>br>/pre>/td>/tr>/table>/figure>p>例如:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>/pre>/td>td classcode>pre>span classline>› npm explain eslint/span>br>span classline>/span>br>span classline>eslint@7.25.0 dev/span>br>span classline>node_modules/eslint/span>br>span classline> dev eslint@span classstring>^7.25.0/span> from the root project/span>br>span classline> peer eslint@span classstring>> 4.12.1/span> from babel-eslint@10.1.0/span>br>span classline> node_modules/babel-eslint/span>br>span classline> dev babel-eslint@span classstring>^10.1.0/span> from the root project/span>br>span classline> peer eslint@span classstring>>7.0.0/span> from eslint-config-prettier@8.3.0/span>br>span classline> node_modules/eslint-config-prettier/span>br>span classline> dev eslint-config-prettier@span classstring>^8.3.0/span> from the root project/span>br>span classline> .../span>br>/pre>/td>/tr>/table>/figure>p>表示当前项目在 devDependencies 中安装了 7.25.0 版本的 eslint,安装的位置为 code>node_modules/eslint/code> 。后面的部分表明是哪些包的依赖,导致了 eslint 的安装。/p>h1 idliu-lan-qi-cha-kan-npm-bao-xin-xi>浏览器查看 npm 包信息/h1>ul>li>p>npm docs | npm home: 在浏览器中打开 npm 包官网;/p>/li>li>p>npm repo: 在浏览器中打开 npm 包仓库地址;/p>/li>/ul>h2 idnpm-docs>npm docs/h2>p>在浏览器中打开指定 npm 包的官方网站。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>/pre>/td>td classcode>pre>span classline>npm docs pakname/span>br>span classline>/span>br>span classline>aliases: home/span>br>/pre>/td>/tr>/table>/figure>p>例如/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>/pre>/td>td classcode>pre>span classline>span classcomment># 会打开 react 官网: https://reactjs.org//span>/span>br>span classline>npm docs react/span>br>span classline>/span>br>span classline>span classcomment># 会打开 ramda 官网: https://ramdajs.com//span>/span>br>span classline>npm docs ramda/span>br>/pre>/td>/tr>/table>/figure>p>npm 的官方网站一般写在 package.json 中的 “homepage” 字段中。/p>h2 idnpm-repo>npm repo/h2>p>在浏览器中打开指定 npm 包的仓库(一般为 github 仓库)地址。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm repo <pkgname> <pkgname> .../span>br>/pre>/td>/tr>/table>/figure>p>例如/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>/pre>/td>td classcode>pre>span classline>span classcomment># 会打开 ramda github 仓库: https://github.com/ramda/ramda/span>/span>br>span classline>npm repo ramda/span>br>/pre>/td>/tr>/table>/figure>h1 idshi-yong-npm-bao-kai-fa-qi-ta-xiang-mu-shi-yong-dao-de-ming-ling>使用 npm 包开发其他项目时,用到的命令/h1>h2 idnpm-outdated-vs-npm-update>npm outdated vs npm update/h2>h3 idnpm-outdated>npm outdated/h3>p>查看项目中是否存在过期的 packages,或者指定的 packages 是否过期/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm outdated <pkg> .../span>br>/pre>/td>/tr>/table>/figure>p>举例/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>/pre>/td>td classcode>pre>span classline>› npm outdated/span>br>span classline>Package Current Wanted Latest Location Depended by/span>br>span classline>semver-regex 3.1.2 3.1.2 4.0.0 node_modules/semver-regex fe.cli/span>br>/pre>/td>/tr>/table>/figure>p>在某一项目下运行 code>npm outdated/code>,便可以列出当前项目中所有已经过期的依赖。/p>p>如上所示,semver-regex 已经过期了,当前版本为 code>3.1.2/code>,最新版本为 code>4.0.0/code>。/p>p>想要查看 package 的详细信息,可以使用code>npm view <pkg>/code>,如查看 semver-regex: code>npm view semver-regex/code>。/p>h4 idcha-kan-quan-ju-yi-jing-guo-qi-de-package>查看全局已经过期的 package/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm outdated -g/span>br>/pre>/td>/tr>/table>/figure>h3 idnpm-update-amp-npm-upgrade>npm update & npm upgrade/h3>p>更新 packages,npm update 会将 package 更新到当前已发布的 strong>最新版本/strong>。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>/pre>/td>td classcode>pre>span classline>npm update -g <pkg>.../span>br>span classline>/span>br>span classline>aliases: up, upgrade/span>br>/pre>/td>/tr>/table>/figure>p>在上文中,我们已经介绍了使用 code>npm outdated/code> 查看过期的 packages。本段将介绍如何使用 code>npm update/code> 更新过期的 packages/p>h4 idgeng-xin-dang-qian-xiang-mu-zhong-suo-you-guo-qi-de-xiang-mu>更新当前项目中所有过期的项目/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm update/span>br>/pre>/td>/tr>/table>/figure>h4 idgeng-xin-quan-ju-de-guo-qi-xiang-mu>更新全局的过期项目/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm update -g/span>br>/pre>/td>/tr>/table>/figure>h4 idgeng-xin-zhi-ding-de-packages>更新指定的 packages/h4>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm update pkg1 pkg2 .../span>br>/pre>/td>/tr>/table>/figure>h1 idqi-ta-npm-ming-ling>其他 npm 命令/h1>ul>li>p>npm config: npm 配置/p>/li>li>p>npm bin: 打印 npm 可执行命令 bin 的文件夹/p>/li>li>p>npm completion: npm 补全脚本/p>/li>/ul>h2 idnpm-config>npm config/h2>p>npm 配置。用于列出当前 npm 环境的配置信息,或管理 npm 配置文件,一般是 .npmrc。/p>h3 idlie-chu-dang-qian-de-npm-pei-zhi>列出当前的 npm 配置/h3>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>/pre>/td>td classcode>pre>span classline>npm config list/span>br>span classline>/span>br>span classline>span classcomment># 列出详细配置/span>/span>br>span classline>npm config list -l/span>br>/pre>/td>/tr>/table>/figure>h2 idnpm-bin>npm bin/h2>p>打印 npm 可执行命令 bin 的文件夹/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>npm bin -g | --global/span>br>/pre>/td>/tr>/table>/figure>h2 idnpm-completion>npm completion/h2>p>npm 补全命令脚本, 可以通过下列命令将命令补全脚本注入到 .bashrc 或 .zshrc 中,这样即可以在终端的任何地方使用。/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>/pre>/td>td classcode>pre>span classline>npm completion >> ~/.bashrc/span>br>span classline>npm completion >> ~/.zshrc/span>br>/pre>/td>/tr>/table>/figure> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2018/09/09/Hexo-Init/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2018/09/09/Hexo-Init/ classpost-title-link itempropurl>Hexo-Init/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2018-09-09 08:54:45 itempropdateCreated datePublished datetime2018-09-09T08:54:45+00:00>2018-09-09/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> h2 idhexo-cai-keng-zhi-nan>Hexo 踩坑指南/h2>h3 idan-zhuang-zhu-ti>安装主题/h3>p>我选用 a hrefhttps://github.com/theme-next/hexo-theme-next target_blank relnoopener>next/a> 主题,简洁、素雅/p>p>安装指南:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>/pre>/td>td classcode>pre>span classline>$ span classbuilt_in>cd/span> hexo/span>br>span classline>$ git span classbuilt_in>clone/span> https://github.com/theme-next/hexo-theme-next themes/next/span>br>/pre>/td>/tr>/table>/figure>h3 idfavicon-pei-zhi>favicon 配置/h3>p>需要在 code>themes/next/_config.yml/code> 中对 favicon 进行配置,我的配置如下/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>/pre>/td>td classcode>pre>span classline>span classcomment># ---------------------------------------------------------------/span>/span>br>span classline>span classcomment># Site Information Settings/span>/span>br>span classline>span classcomment># ---------------------------------------------------------------/span>/span>br>span classline>/span>br>span classline>span classcomment># To get or check favicons visit: https://realfavicongenerator.net/span>/span>br>span classline>span classcomment># Put your favicons into `hexo-site/source/` (recommend) or `hexo-site/themes/next/source/images/` directory./span>/span>br>span classline>/span>br>span classline>span classcomment># Default NexT favicons placed in `hexo-site/themes/next/source/images/` directory./span>/span>br>span classline>span classcomment># And if you want to place your icons in `hexo-site/source/` root directory, you must remove `/images` prefix from pathes./span>/span>br>span classline>/span>br>span classline>span classcomment># For example, you put your favicons into `hexo-site/source/images` directory./span>/span>br>span classline>span classcomment># Then need to rename & redefine they on any other names, otherwise icons from Next will rewrite your custom icons in Hexo./span>/span>br>span classline>favicon:/span>br>span classline> small: /favicon.ico/span>br>span classline> medium: /favicon.ico/span>br>span classline> apple_touch_icon: /images/apple-touch-icon-next.ico/span>br>span classline> safari_pinned_tab: /logo.svg/span>br>span classline> span classcomment>#android_manifest: /images/manifest.json/span>/span>br>span classline> span classcomment>#ms_browserconfig: /images/browserconfig.xml/span>/span>br>/pre>/td>/tr>/table>/figure>h3 idguan-yu-mao-dian-shi-xiao-wen-ti>关于锚点失效问题/h3>p>这是 a hrefhttps://github.com/hexojs/hexo-renderer-markdown-it target_blank relnoopener>hexo-renderer-markdown-it/a> 的一个 bug,但作者不想修复,认为是其他库的坑,所以我们要参考这个 a hrefhttps://github.com/hexojs/hexo-renderer-markdown-it/issues/40 target_blank relnoopener>issue/a> ,手动到 node_modules 中修改该库,具体如下:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>span classline>21/span>br>span classline>22/span>br>span classline>23/span>br>span classline>24/span>br>/pre>/td>td classcode>pre>span classline>span classmeta>use strict/span>;/span>br>span classline>/span>br>span classline>span classbuilt_in>module/span>.exports span classfunction>span classkeyword>function/span> (span classparams>data, options/span>) /span>{/span>br>span classline> span classkeyword>var/span> MdIt span classbuilt_in>require/span>(span classstring>markdown-it/span>);/span>br>span classline> span classkeyword>var/span> cfg span classkeyword>this/span>.config.markdown;/span>br>span classline> span classkeyword>var/span> opt (cfg) ? cfg : span classstring>default/span>;/span>br>span classline> span classkeyword>var/span> parser (opt span classstring>default/span> || opt span classstring>commonmark/span> || opt span classstring>zero/span>) ?/span>br>span classline> span classkeyword>new/span> MdIt(opt) :/span>br>span classline> span classkeyword>new/span> MdIt(opt.render);/span>br>span classline>/span>br>span classline> parser.use(span classbuilt_in>require/span>(span classstring>markdown-it-named-headings/span>)) span classcomment>// 只需要添加这行代码/span>/span>br>span classline>/span>br>span classline> span classkeyword>if/span> (opt.plugins) {/span>br>span classline> parser opt.plugins.reduce(span classfunction>span classkeyword>function/span> (span classparams>parser, pugs/span>) /span>{/span>br>span classline> span classkeyword>return/span> parser.use(span classbuilt_in>require/span>(pugs));/span>br>span classline> }, parser);/span>br>span classline> }/span>br>span classline>/span>br>span classline> span classkeyword>if/span> (opt.anchors) {/span>br>span classline> parser parser.use(span classbuilt_in>require/span>(span classstring>./anchors/span>), opt.anchors);/span>br>span classline> }/span>br>span classline>/span>br>span classline> span classkeyword>return/span> parser.render(data.text);/span>br>span classline>};/span>br>/pre>/td>/tr>/table>/figure>h3 idzi-dong-hua-xiu-fu-yi-shang-wen-ti-de-jiao-ben>自动化修复以上问题的脚本/h3>p>写了一个自动化脚本,git pull 下我的博客备份后,自动安装、修复上述问题。/p>p>运行工程目录下的 code>./hexo-init.sh/code>,内容如下:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>/pre>/td>td classcode>pre>span classline>span classmeta>#!/bin/bash/span>/span>br>span classline>/span>br>span classline>npm i hexo-cli -g/span>br>span classline>/span>br>span classline>yarn/span>br>span classline>/span>br>span classline>git span classbuilt_in>clone/span> https://github.com/theme-next/hexo-theme-next themes/next/span>br>span classline>/span>br>span classline>span classcomment># 修复锚点不生效问题/span>/span>br>span classline>cp ./init_source/hexo-renderer-markdown-it/lib/renderer.js ./node_modules/hexo-renderer-markdown-it/lib/renderer.js/span>br>span classline>/span>br>span classline>span classcomment># 自动配置 favicon/span>/span>br>span classline>cp ./init_source/next/_config.yml ./themes/next/_config.yml/span>br>/pre>/td>/tr>/table>/figure>h3 idgit-pull-xia-lai-de-hexo-gong-cheng-hexo-deploy-hou-jiang-zheng-ge-xiang-mu-du-push-shang-qu-liao>Git pull 下来的 Hexo 工程,hexo deploy 后将整个项目都 push 上去了/h3>p>每次 git pull 备份的工程后,需要清理一下 hexo 工程,具体方法如下:/p>figure classhighlight bash>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>/pre>/td>td classcode>pre>span classline>$ span classbuilt_in>cd/span> adispring.github.io/span>br>span classline>rm -rf .git .deploy_git hexo clean/span>br>/pre>/td>/tr>/table>/figure> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2018/09/08/Fantasy-Land-Specification/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2018/09/08/Fantasy-Land-Specification/ classpost-title-link itempropurl>Fantasy-Land-Specification/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2018-09-08 16:45:36 itempropdateCreated datePublished datetime2018-09-08T16:45:36+00:00>2018-09-08/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> p>(又名 “代数 JavaScript 规范”)/p>p>img src./logo.png altlogo>/p>p>该项目规定了通用代数数据结构的互操作性:/p>ul>li>a href#setoid>Setoid/a>/li>li>a href#ord>Ord/a>/li>li>a href#semigroupoid>Semigroupoid/a>/li>li>a href#category>Category/a>/li>li>a href#semigroup>Semigroup/a>/li>li>a href#monoid>Monoid/a>/li>li>a href#group>Group/a>/li>li>a href#filterable>Filterable/a>/li>li>a href#functor>Functor/a>/li>li>a href#contravariant>Contravariant/a>/li>li>a href#apply>Apply/a>/li>li>a href#applicative>Applicative/a>/li>li>a href#alt>Alt/a>/li>li>a href#plus>Plus/a>/li>li>a href#alternative>Alternative/a>/li>li>a href#foldable>Foldable/a>/li>li>a href#traversable>Traversable/a>/li>li>a href#chain>Chain/a>/li>li>a href#chainrec>ChainRec/a>/li>li>a href#monad>Monad/a>/li>li>a href#extend>Extend/a>/li>li>a href#comonad>Comonad/a>/li>li>a href#bifunctor>Bifunctor/a>/li>li>a href#profunctor>Profunctor/a>/li>/ul>p>img src./dependencies.png altdependencies>/p>h2 idgai-lan>概览/h2>p>代数是遵循一定法则的、具有封闭性的,一系列值及一系列操作的集合。/p>p>每个 Fantasy Land 代数是一个单独的规范。一个代数可能依赖于其他必需实现的代数。/p>h2 idzhu-yu>术语/h2>ol>li>p>“值”:任何 JavaScript 值,包括下面定义的结构的任何值。/p>/li>li>p>“等价”:对给定值的等价性的恰当定义。这个定义应该保证两个值可以在其对应的抽象的程序中,能够安全地进行交换。例如:/p>ul>li>当两个列表对应的索引上的值都相等时,它们是等价的。/li>li>当两个普通的 JavaScript 对象所有键值对都相等时,它们(作为字典)是等价的。/li>li>当两个 promises 生成相等的值时,它们是等价的。/li>li>当两个函数给定相同的输入,产生相同的输出时,它们是等价的。/li>/ul>/li>/ol>h2 idlei-xing-qian-ming-fu-hao>类型签名符号/h2>p>本文档使用的类型签名符号如下所述:^1/p>ul>li>code>::/code> _em>“是 xx 的成员”。/em>ul>li>code>e :: t/code> 读作:“表达式 code>e/code> 是类型 code>t/code> 的成员”。/li>li>code>true :: Boolean/code> - “code>true/code> 是类型 code>Boolean/code> 的成员”。/li>li>code>42 :: Integer, Number/code> - “code>42/code> 是类型 code>Integer/code> 和 code>Number/code> 的成员”。/li>/ul>/li>li>em>新类型可以通过类型构造函数创建。/em>ul>li>类型构造函数可以接受零或多个类型参数。/li>li>code>Array/code> 是一个接受单个参数的类型构造函数。/li>li>code>Array String/code> 代表包含字符串的数组的类型。后面每个都是 code>Array String/code> 类型的:code>/code>,code>foo, bar, baz/code>。/li>li>code>Array (Array String)/code> 代表包含字符串的数组的数组的类型。后面每个都是 code>Array (Array String)/code> 类型的:code>/code>,code>, /code>, code>, foo, bar, baz/code>。/li>/ul>/li>li>em>小写字母代表类型变量。/em>ul>li>类型变量可以接受任何类型,除非受到类型约束的限制(参见下面的胖箭头)。/li>li>code>->/code> (箭头) em>函数类型的构造函数/em>/li>li>code>->/code> 是一个 em>中缀/em> 构造函数,它接受两个类型参数,左侧参数为输入的类型,右侧参数为输出的类型。/li>li>code>->/code> 的输入类型可以通过一组类型创建出来,该函数接受零个或多个参数。其语法是:code>(<input-types>) -> <output-type>/code>,其中 code><input-types>/code> 包含零个或多个 “逗号-空格” (code>, /code>)分开的类型表示,对于一元函数,圆括号也可以省略。/li>li>code>String -> Array String/code> 是一种接受一个 code>String/code> 并返回一个 code>Array String/code> 的函数的类型。/li>li>code>String -> Array String -> Array String/code> 是一种函数类型,它接受一个 code>String/code> 并返回一个函数,返回的函数接受一个 code>Array String/code> 并返回一个 code>Array String/code>。/li>li>code>(String, Array String) -> Array String/code> 是一种函数类型,它接受一个 code>String/code> 和 code>Aray String/code> 作为参数,并返回一个 code>Array String/code> 。/li>li>code>() -> Number/code> 是一种不带输入参数,返回 code>Number/code> 的函数类型。/li>/ul>/li>li>code>~>/code> (波浪形箭头) em>方法类型的构造函数。/em>ul>li>当一个函数是一个对象(Object)的属性时,它被称为方法。所有方法都有一个隐含的参数类型 - 它是属性所在对象的类型。/li>li>code>a ~> a -> a/code> 是一种对象中方法的类型,它接受 code>a/code> 类型的参数,并返回一个 code>a/code> 类型的值。/li>/ul>/li>li>code>>/code> (胖箭头) 表示对类型变量的约束。ul>li>在 code>a ~> a -> a/code>(参见上面的波浪形箭头)中,code>a/code> 可以为任意类型。半群 code>a > a ~> a -> a/code> 会添加一个约束,使得类型 code>a/code> 现在必须满足该半群的类型类。满足类型类意味着,须合法地实现该类型类指定所有函数/方法。/li>/ul>/li>/ul>p>例如:/p>figure classhighlight plain>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>/pre>/td>td classcode>pre>span classline>traverse :: Applicative f, Traversable t > t a ~> (TypeRep f, a -> f b) -> f (t b)/span>br>span classline>'------' '--------------------------' '-' '-------------------' '-----'/span>br>span classline> ' ' ' ' '/span>br>span classline> ' ' - type constraints ' ' - argument types ' - return type/span>br>span classline> ' '/span>br>span classline> '- method name ' - method target type/span>br>/pre>/td>/tr>/table>/figure>p>^1: 更多相关信息,请参阅 Sanctuary 文档中的 a hrefhttps://sanctuary.js.org/#types target_blank relnoopener>Types/a> 部分。/p>h2 idqian-zhui-fang-fa-ming>前缀方法名/h2>p>为了使数据类型与 Fantasy Land 兼容,其值必须具有某些属性。这些属性都以 code>fantasy-land//code> 为前缀。例如:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>/pre>/td>td classcode>pre>span classline>span classcomment>// MyType#fantasy-land/map :: MyType a ~> (a -> b) -> MyType b/span>/span>br>span classline>MyType.prototypespan classstring>fantasy-land/map/span> .../span>br>/pre>/td>/tr>/table>/figure>p>在本文中,不使用前缀的名称,只是为了减少干扰。/p>p>为了方便起见,你可以使用 code>fantasy-land/code> 包:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> fl span classbuilt_in>require/span>(span classstring>fantasy-land/span>)/span>br>span classline>/span>br>span classline>span classcomment>// .../span>/span>br>span classline>/span>br>span classline>MyType.prototypefl.map .../span>br>span classline>/span>br>span classline>span classcomment>// .../span>/span>br>span classline>/span>br>span classline>span classkeyword>var/span> foo barfl.map(span classfunction>span classparams>x/span> >/span> x + span classnumber>1/span>)/span>br>/pre>/td>/tr>/table>/figure>h2 idlei-xing-biao-shi-java-script-zhong-de-gou-zao-han-shu>类型表示 (JavaScript 中的构造函数?)/h2>p>某些行为是从类型成员的角度定义的。而另外一些行为不需要类型成员。因此,某些代数需要一个类型来提供值层面上的表示(具有某些属性)。例如,Identity 类型可以提供 code>Id/code> 作为其类型表示:code>Id :: TypeRep Identity/code>。/p>p>如果一个类型提供了类型表示,那么这个类型的每个成员都必须有一个指向该类型表示的 code>contructor/code> 属性。/p>h2 iddai-shu>代数/h2>h3 idsetoid>Setoid/h3>ol>li>code>a.equals(a) true/code> (自反性)/li>li>code>a.equals(b) b.equals(a)/code> (对称性)/li>li>如果 code>a.equals(b)/code> 并且 code>b.equals(a)/code>,则 code>a.equals(c)/code> (传递性)/li>/ol>h4 idcode-equals-code-fang-fa>code>equals/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>equals/span> :: span classtype>Setoid/span> a > a ~> a -> span classtype>Boolean/span>/span>br>/pre>/td>/tr>/table>/figure>p>具有 Setoid 的值必须提供 code>equals/code> 方法。code>equals/code> 方法接受一个参数:/p>pre>code>a.equals(b)/code>/pre>ol>li>code>b/code> 必须是相同 Setoid 的值ol>li>如果 code>b/code> 不是相同的 Setoid,则 code>equals/code> 的行为未指定(建议返回 code>false/code>)。/li>li>code>equals/code> 必须返回一个布尔值(code>true/code> 或 code>false/code>)。/li>/ol>/li>/ol>h3 idord>Ord/h3>p>实现 Ord 规范的值还必须实现 a href#setoid>Setoid/a> 规范。/p>ol>li>code>a.lte(b)/code> 或 code>b.lte(a)/code> (完全性)/li>li>如果 code>a.lte(b)/code> 且 code>b.lte(a)/code>,则 code>a.equals(b)/code> (反对称性)/li>li>如果 code>a.lte(b)/code> 且 code>b.lte(c)/code>,则 code>a.lte(c)/code> (传递性)/li>/ol>h4 idcode-lte-code-fang-fa>code>lte/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>lte/span> :: span classtype>Ord/span> a > a ~> a -> span classtype>Boolean/span>/span>br>/pre>/td>/tr>/table>/figure>p>具有 Ord 的值必须提供 code>lte/code> 方法。code>lte/code> 方法接受一个参数:/p>pre>code>`a.lte(b)`/code>/pre>ol>li>code>b/code> 必须是相同 Ord 的值。ol>li>如果 code>b/code> 不是相同的 Ord,则 code>lte/code> 的行为未指定 (建议返回 code>false/code>)。/li>/ol>/li>li>code>lte/code> 必须返回布尔值(code>true/code> 或 code>false/code>)。/li>/ol>h3 idsemigroupoid>Semigroupoid/h3>ol>li>code>a.compose(b).compose(c) a.compose(b.compose(c))/code> (结合性)/li>/ol>h4 idcode-compose-code-fang-fa>code>compose/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>compose/span> :: span classtype>Semigroupoid/span> c > c i j ~> c j k -> c i k/span>br>/pre>/td>/tr>/table>/figure>p>具有 Semigoupoid 的值必须提供 code>compose/code> 组合方法。code>compose/code> 方法接受一个参数:/p>pre>code>a.compose(b)/code>/pre>ol>li>code>b/code> 必须返回相同 Semigroupoid 规范。ol>li>如果 code>b/code> 不是相同的 Semigroupoid,compose 的行为未指定。/li>/ol>/li>li>code>compose/code> 必须返回相同 Semigroupoid 的值。/li>/ol>h3 idcategory>Category/h3>p>实现范畴规范的值还必须实现a href#semigroupoid>半群/a>规范。/p>ol>li>code>a.compose(C.id())/code> 等价于 code>a/code> (右同一性)/li>li>code>C.id().compose(a)/code> 等价于 code>a/code> (左同一性)/li>/ol>h4 idcode-id-code-fang-fa>code>id/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>id/span> :: span classtype>Category/span> c > () -> c a a/span>br>/pre>/td>/tr>/table>/figure>p>具有范畴的值必须在其a href#type-representatives>类型表示/a>中提供一个 code>id/code> 函数。/p>pre>code>C.id()/code>/pre>p>给定值 code>c/code>,可以通过 code>contructor/code> 属性来访问其类型表示:/p>pre>code>c.constructgor.id()/code>/pre>ol>li>code>id/code> 必须返回相同范畴的值。/li>/ol>h3 idsemigroup>Semigroup/h3>ol>li>code>a.concat(b).concat(c)/code> 等价于 code>a.concat(b.concat(c))/code> (结合性)/li>/ol>h4 idcode-concat-code-fang-fa>code>concat/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>concat/span> :: span classtype>Semigroup/span> a > a ~> a -> a/span>br>/pre>/td>/tr>/table>/figure>p>具有 Semigroup 的值必须提供 code>concat/code> 方法。code>concat/code> 方法接受一个参数:/p>pre>code>s.concat(b)/code>/pre>ol>li>code>b/code> 必须是相同 Semigroup 的值ol>li>如果 code>b/code> 不是相同的 Semigroup,则 code>concat/code> 的行为未指定。/li>/ol>/li>li>code>concat/code> 必须返回相同 Semigroup 的值。/li>/ol>h3 idmonoid>Monoid/h3>p>实现 Monoid 规范的值还必须实现 a href#semigroup>Semigroup/a> 规范/p>ol>li>code>m.concat(M.empty())/code> 等价于 code>m/code> (右结合性)/li>li>code>M.empty().concat(m)/code> 等价于 code>m/code> (左结合性)/li>/ol>h4 idcode-empty-code-fang-fa>code>empty/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>empty/span> :: span classtype>Monoid/span> m > () -> m/span>br>/pre>/td>/tr>/table>/figure>p>具有 Monoid 的值必须在其类型表示上提供 code>empty/code> 方法:/p>pre>code>M.empty()/code>/pre>p>给定值code> /code>m,可以通过 code>constructor/code> 属性来访问其类型表示:/p>pre>code>m.constructor.empty()/code>/pre>ol>li>code>empty/code> 必须返回相同 Monoid 的值。/li>/ol>h3 idgroup>Group/h3>p>实现 Group 规范的值还必须实现 a href#monoid>Monoid/a> 规范。/p>ol>li>code>g.concat(g.invert())/code> 等价于 code>g.constructor.empty()/code> (右反转性??)/li>li>code>g.invert().concat(g)/code> 等价于 code>g.constructor.empty()/code> (左翻转性??)/li>/ol>h4 idcode-invert-code-fang-fa>code>invert/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>invert/span> :: span classtype>Group/span> g > g ~> () -> g/span>br>/pre>/td>/tr>/table>/figure>p>具有 Semigroup 的值必须提供 code>invert/code> 方法。code>invert/code> 方法接受零个参数:/p>pre>code>g.invert()/code>/pre>ol>li>code>invert/code> 必须返回相同 Group 的值。/li>/ol>h3 idfilterable>Filterable/h3>ol>li>code>v.filter(x > p(x) && q(x))/code> 等价于 code>v.filter(p).filter(q)/code> (分配性)/li>li>code>v.filter(x > true)/code> 等价于 code>v/code> (同一性)/li>li>code>v.filter(x -> false)/code> 等价于 code>w.filter(x > false)/code>,如果 code>v/code> 和 code>w/code> 具有相同的 Filterable 值 (湮灭??)/li>/ol>h4 idcode-filter-code-fang-fa>code>filter/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>filter/span> :: span classtype>Filterable/span> f > f a ~> (a -> span classtype>Boolean/span>) -> f a/span>br>/pre>/td>/tr>/table>/figure>p>具有 Filterable 的值必须提供 code>filter/code> 方法。code>filter/code> 方法接受一个参数:/p>pre>code>v.filter(p)/code>/pre>ol>li>p>code>p/code> 必须是一个函数。/p>ol>li>如果 code>p/code> 不是函数,则 code>filter/code> 的行为未指定。/li>li>code>p/code> 必须返回 code>ture/code> 或 code>false/code>。如果返回任何其它值,code>filter/code> 的行为未指定。/li>/ol>/li>li>p>code>filter/code> 必须返回相同 Filterable 的值。/p>/li>/ol>h3 idfunctor>Functor/h3>ol>li>code>u.map(a > a)/code> 等价于 code>u/code> (同一性)/li>li>code>u.map(x > f(g(x)))/code> 等价于 code>u.map(g).map(f)/code> (组合性)/li>/ol>h4 idcode-map-code-fang-fa>code>map/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>map/span> :: span classtype>Functor/span> f > f a ~> (a -> b) -> f b/span>br>/pre>/td>/tr>/table>/figure>p>具有 Functor 的值必须提供 code>map/code> 方法。code>map/code> 方法接受一个参数:/p>pre>code>u.map(f)/code>/pre>ol>li>p>code>f/code> 必须是一个函数,/p>ol>li>如果 code>f/code> 不是函数,则 code>map/code> 的行为未指定。/li>li>code>f/code> 可以返回任何值。/li>li>code>f/code> 返回值的任何部分都不应该被检查(??)。/li>/ol>/li>li>p>code>map/code> 必须返回相同 Functor 的值。/p>/li>/ol>h3 idcontravariant>Contravariant/h3>ol>li>code>u.contramap(a > a)/code> 等价于 code>u/code> (同一性)/li>li>code>u.contramap(x > f(g(x)))/code> 等价于 code>u.contramap(f).contramap(g)/code> (组合性)/li>/ol>h4 idcode-contramap-code-fang-fa>code>contramap/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>contramap/span> :: span classtype>Contravariant/span> f > f a ~> (b -> a) -> f b/span>br>/pre>/td>/tr>/table>/figure>p>具有 Contravariant 的值必须提供 code>contramap/code> 方法。code>contramap/code> 方法接受一个参数:/p>pre>code>u.contramap(f)/code>/pre>ol>li>code>f/code> 必须是一个函数,ol>li>如果 code>f/code> 不是函数,则 code>contramap/code> 的行为未指定。/li>li>code>f/code> 可以返回任何值。/li>li>code>f/code> 返回值的任何部分都不应该被检查(??)。/li>/ol>/li>li>code>contramap/code> 必须返回相同 Contravariant 的值。/li>/ol>h3 idapply>Apply/h3>p>实现 Apply 规范的值还必须实现 a href#functor>Functor/a> 规范。/p>ol>li>code>v.ap(u.ap(a.map(f > g > x > f(g(x)))))/code> 等价于 code>v.ap(u).ap(a)/code> (组合型),推导过程??/li>/ol>h4 idcode-ap-code-fang-fa>code>ap/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>ap/span> :: span classtype>Apply/span> f > f a ~> f (a -> b) -> f b/span>br>/pre>/td>/tr>/table>/figure>p>具有 Apply 的值必须提供 code>ap/code> 方法。code>ap/code> 方法接受一个参数:/p>pre>code>a.ap(b)/code>/pre>ol>li>code>b/code> 必须是一个函数的 Applyol>li>如果 code>b/code> 不代表函数,则 code>ap/code> 的行为未指定。/li>li>code>b/code> 必须与 a 具有相同的 Apply。/li>/ol>/li>li>code>a/code> 可以是任意值的 Apply。(??)/li>li>code>ap/code> 必须能将 Apply code>b/code> 内的函数应用于 Apply code>a/code> 的值上ol>li>函数返回值的任何部分都不应该被检查。/li>/ol>/li>li>由 code>ap/code> 返回的 code>Apply/code> 必须与 code>a/code> 和 code>b/code> 的相同。/li>/ol>h3 idapplicative>Applicative/h3>p>实现 Applicative 规范的值还必须实现 a href#apply>Apply/a> 规范。/p>ol>li>code>v.ap(A.of(x > x))/code> 等价于 code>v/code> (同一性)/li>li>code>A.of(x).ap(A.of(f))/code> 等价于 code>A.of(f(x))/code> (同态性, homomorphism)/li>li>code>A.of(y).ap(u)/code> 等价于 code>u.ap(A.of(f > f(y)))/code> (交换性)/li>/ol>h4 idcode-of-code-fang-fa>code>of/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>of/span> :: span classtype>Applicative/span> f > a -> f a/span>br>/pre>/td>/tr>/table>/figure>p>具有 Applicative 的值必须在其类型表示中提供 code>of/code> 函数。code>of/code> 函数接受一个参数:/p>pre>code>F.of(a)/code>/pre>p>给定值 code>f/code>,可以通过 code>contructor/code> 属性访问其类型表示:/p>pre>code>f.contructor.of(a)/code>/pre>ol>li>code>of/code> 必须提供相同的 Applicativeol>li>a 的任何部分都不应该被检查/li>/ol>/li>/ol>h3 idalt>Alt/h3>p>实现 Alt 规范的值还必须实现 a href#functor>Functor/a> 规范。/p>ol>li>code>a.alt(b).alt(c)/code> 等价于 code>a.alt(b.alt(c))/code> (结合性)/li>li>code>a.alt(b).map(f)/code> 等价于 code>a.map(f).alt(b.map(f))/code> (分配性) (看起来像乘法,有什么实际用途呢?)/li>/ol>h4 idcode-alt-code-fang-fa>code>alt/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>alt/span> :: span classtype>Alt/span> f > f a ~> f a -> f a/span>br>/pre>/td>/tr>/table>/figure>p>具有 Alt 的值必须提供 code>alt/code> 方法。code>alt/code> 方法接受一个参数:/p>pre>code>a.alt(b)/code>/pre>ol>li>code>b/code> 必须是相同 Alt 的值ol>li>如果 code>b/code> 不是相同的 Alt,则 code>alt/code> 的行为未指定。/li>li>code>a/code> 和 code>b/code> 可以包含相同类型的任何值。/li>li>code>a/code> 和 code>b/code> 包含值的任何部分都不应该被检查。/li>/ol>/li>li>code>alt/code> 必须返回相同 Alt 的值。/li>/ol>h3 idplus>Plus/h3>p>实现 Plus 规范的值还必须实现 a href#alt>Alt/a> 规范。/p>ol>li>code>x.alt(A.zero())/code> 等价于 code>x/code> (右同一性)/li>li>code>A.zero().alt(x)/code> 等价于 code>x/code> (左同一性)/li>li>code>A.zero().map(f)/code> 等价于 code>A.zero()/code> (湮灭??)/li>/ol>h4 idcode-zero-code-fang-fa>code>zero/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>zero/span> :: span classtype>Plus/span> f > () -> f a/span>br>/pre>/td>/tr>/table>/figure>p>具有 Plus 的值必须在其类型表示中提供 code>zero/code> 函数:/p>pre>code>A.zero()/code>/pre>p>给定值 code>x/code>,可以通过 code>contructor/code> 属性访问其类型表示:/p>pre>code>x.contructor.zero()/code>/pre>p>code>zero/code> 必须返回相同 Plus 的值。/p>h3 idalternative>Alternative/h3>p>实现 Alternative 规范的值还必须实现 a href#applicative>Applicative/a> 和 a href#plus>Plus/a> 规范。/p>ol>li>code>x.ap(f.alt(g))/code> 等价于 code>x.ap(f).alt(x.ap(g))/code> (分配性)/li>li>code>x.ap(A.zero())/code> 等价于 code>A.zero()/code> (湮灭)/li>/ol>h3 idfoldable>Foldable/h3>p>code>u.reduce/code> 等价于 code>u.reduce((acc, x) > acc.concat(x), ).reduce/code>/p>h4 idcode-reduce-code-fang-fa>code>reduce/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>reduce/span> :: span classtype>Foldable/span> f > f a ~> ((b, a) -> b, b) -> b/span>br>/pre>/td>/tr>/table>/figure>p>具有 Foldable 的值必须在其类型表示中提供 code>reduce/code> 函数。code>reduce/code> 函数接受两个参数:/p>pre>code>u.reduce(f, x)/code>/pre>ol>li>code>f/code> 必须是一个二元函数ol>li>如果 code>f/code> 不是函数,则 code>reduce/code> 的行为未指定。/li>li>code>f/code> 的第一个参数类型必须与 code>x/code> 的相同。/li>li>code>f/code> 的返回值类型必须与 code>x/code> 的相同。/li>li>code>f/code> 返回值的任何部分都不应该被检查。/li>/ol>/li>li>code>x/code> 是归约的初始累积值ol>li>code>x/code> 的任何部分都不应该被检查/li>/ol>/li>/ol>h3 idtraversable>Traversable/h3>p>实现 Traversable 规范的值还必须实现 a href#functor>Functor/a> 和 a href#foldable>Foldable/a> 规范。/p>ol>li>对于任意 code>t/code>,code>t(u.traverse(F, x > x))/code> 等价于 code>u.traverse(G, t)/code> ,因为 code>t(a).map(f)/code> 等价于 code>t(a.map(f))/code> (自然性)/li>li>对于任意 Applicative code>F/code>,code>u.traverse(F, F.of)/code> 等价于 code>F.of(u)/code> (同一性)/li>li>code>u.traverse(Compose, x > new Compose(x))/code> 等价于 code>new Compose(u.traverse(F, x > x).map(x > x.traverse(G, x > x)))/code>,对下面定义的 code>Compose/code> 和 任意 Applicatives code>F/code> 和 code>G/code> 都适用 (组合性)/li>/ol>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> Compose span classfunction>span classkeyword>function/span>(span classparams>c/span>) /span>{/span>br>span classline> span classkeyword>this/span>.c c;/span>br>span classline>};/span>br>span classline>/span>br>span classline>Compose.of span classfunction>span classkeyword>function/span>(span classparams>x/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>new/span> Compose(F.of(G.of(x)));/span>br>span classline>};/span>br>span classline>/span>br>span classline>Compose.prototype.ap span classfunction>span classkeyword>function/span>(span classparams>f/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>new/span> Compose(span classkeyword>this/span>.c.ap(f.c.map(span classfunction>span classparams>u/span> >/span> span classfunction>span classparams>y/span> >/span> y.ap(u))))/span>br>span classline>};/span>br>span classline>/span>br>span classline>Compose.prototype.map span classfunction>span classkeyword>function/span>(span classparams>f/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>new/span> Compose(span classkeyword>this/span>.c.map(span classfunction>span classparams>y/span> >/span> y.map(f)));/span>br>span classline>};/span>br>/pre>/td>/tr>/table>/figure>h4 idcode-traverse-code-fang-fa>code>traverse/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>traverse/span> :: span classtype>Applicative/span> f, span classtype>Traversable/span> t > t a ~> (span classtype>TypeRep/span> f, a -> f b) -> f (t b)/span>br>/pre>/td>/tr>/table>/figure>p>具有 Traversable 的值必须提供 code>traverse/code> 函数。code>traverse/code> 函数接受两个参数:/p>pre>code>u.traverse(A, f)/code>/pre>ol>li>code>A/code> 必须是一个 Applicative 的类型表示。/li>li>code>f/code> 必须是一个返回值的函数ol>li>如果 code>f/code> 不是函数,则 code>traverse/code> 的行为未指定。/li>li>code>f/code> 必须返回类型表示为 code>A/code> 的值。/li>/ol>/li>li>code>traverse/code> 必须返回类型表示为 code>A/code> 的值。/li>/ol>h3 idchain>Chain/h3>p>实现 Chain 规范的值还必须实现 a href#apply>Apply/a> 规范。/p>ol>li>code>m.chain(f).chain(g)/code> 等价于 code>m.chain(x > f(x).chain(g))/code> (结合性)/li>/ol>h4 idcode-chain-code-fang-fa>code>chain/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>chain/span> :: span classtype>Chain/span> m > m a ~> (a -> m b) -> m b/span>br>/pre>/td>/tr>/table>/figure>p>具有 Chain 的值必须提供 code>chain/code> 函数。code>chain/code> 函数接受一个参数:/p>pre>code>m.chain(f)/code>/pre>ol>li>code>f/code> 必须是一个返回值的函数ol>li>如果 code>f/code> 不是函数,则 code>chain/code> 的行为未指定。/li>li>code>f/code> 必须返回相同 Chain 的值。/li>/ol>/li>li>code>chain/code> 必须返回相同 Chain 的值。/li>/ol>h3 idchain-rec>ChainRec/h3>p>实现 ChainRec 规范的值还必须实现 a href#chain>Chain/a> 规范。/p>ol>li>code>M.chainRec((next, done, v) > p(v) ? d(v).map(done) : n(v).map(next), i)/code> 等价于 code>function step(v) { return p(v) ? d(v) : n(v).chain(step); }(i)/code> (等价性)/li>li>code>M.chainRec(f, i)/code> 栈的用量必须是 code>f/code> 自身栈用量的常数倍。/li>/ol>h4 idcode-chain-rec-code-fang-fa>code>chainRec/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>chainRec/span> :: span classtype>ChainRec/span> m > ((a -> c), b -> c, a) -> m b/span>br>/pre>/td>/tr>/table>/figure>p>具有 ChainRec 的值必须在其类型表示中提供 code>chainRec/code> 函数。code>chainRec/code> 函数接受两个参数:/p>pre>code>M.chainRec(f, i)/code>/pre>p>给定值 code>m/code>,可以通过 code>contructor/code> 属性访问其类型表示:/p>pre>code>m.constructor.chainRec(f, i)/code>/pre>ol>li>code>f/code> 必须是一个返回值的函数ol>li>如果 code>f/code> 不是函数,则 code>chainRec/code> 的行为未指定。/li>li>code>f/code> 接受三个参数 code>next/code>,code>done/code>,code>value/code>ol>li>code>next/code> 是一个函数,其接受一个与 i 类型相同的参数,可以返回任意值/li>li>code>done/code> 也是一个函数,其接受一个参数,并返回一个与 code>next/code> 返回值类型相同的值/li>li>code>value/code> 是一个与 i 类型相同的值。/li>/ol>/li>li>code>f/code> 必须返回一个相同 ChainRec 的值,其中包含的是从 code>done/code> 或 code>next/code> 返回的值。/li>/ol>/li>li>code>chainRec/code> 必须返回一个相同 ChainRec 的值,其中包含的值的类型与 code>done/code> 的参数类型相同。/li>/ol>h3 idmonad>Monad/h3>p>实现 Monad 规范的值还必须实现 a href#applicative>Applicative/a> 和 a href#chain>Chain/a> 规范。/p>ol>li>code>M.of(a).chain(f)/code> 等价于 code>f(a)/code> (左同一性)/li>li>code>m.chain(M.of)/code> 等价于 code>m/code> (右同一性)/li>/ol>h3 idextend>Extend/h3>p>实现 Extend 规范的值还必须实现 a href#functor>Functor/a> 规范。/p>ol>li>code>w.extend(g).extend(f)/code> 等价于 code>w.extend(\_w > f(\_w.extend(g)))/code>/li>/ol>p>code>extend/code> 方法/p>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>extend/span> :: span classtype>Extend/span> w > w a ~> (w a -> b) -> w b/span>br>/pre>/td>/tr>/table>/figure>p>具有 Extend 的值必须提供 code>extend/code> 函数。code>extend/code> 函数接受一个参数:/p>pre>code>w.extend(f)/code>/pre>ol>li>code>f/code> 必须是一个返回值的函数,ol>li>如果 code>f/code> 不是函数,则 extend 的行为未指定。/li>li>code>f/code> 必须返回一个 code>v/code> 类型的值,其中 code>v/code> 是 code>w/code> 中包含的某个变量 code>v/code> (??)/li>li>code>f/code> 返回值的任何部分都不应该被检查。/li>/ol>/li>li>code>extend/code> 必须返回相同 Extend 的值。/li>/ol>h3 idcomonad>Comonad/h3>p>实现 Comonad 规范的值还必须实现 a href#extend>Extend/a> 规范。/p>ol>li>code>w.extend(_w > _w.extract())/code> 等价于 code>w/code> (左同一性)/li>li>code>w.extend(f).extract()/code> 等价于 code>f(w)/code> (右同一性)/li>/ol>h4 idcode-extract-code-fang-fa>code>extract/code> 方法/h4>p>具有 Comonad 的值必须提供 code>extract/code> 函数。code>extract/code> 函数接受零个参数:/p>pre>code>w.extract()/code>/pre>ol>li>code>extract/code> 必须返回一个 code>v/code> 类型的值,其中 code>v/code> 是 code>w/code> 中包含的某个变量 code>v/code> (??)ol>li>code>v/code> 必须与在 code>extend/code> 中的 code>f/code> 返回的类型相同。/li>/ol>/li>/ol>h3 idbifunctor>Bifunctor/h3>p>实现 Bifunctor 规范的值还必须实现 a href#functor>Functor/a> 规范。/p>ol>li>code>p.bimap(a > a, b > b)/code> 等价于 code>p/code> (同一性)/li>li>code>p.bimap(a > f(g(a)), b > h(i(b)))/code> 等价于 code>p.bimap(g, i).bimap(f, h)/code> (组合性)/li>/ol>h3 idcode-bimap-code-fang-fa>code>bimap/code> 方法/h3>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>bimap/span> :: span classtype>Bifunctor/span> f > f a c ~> (a -> b, c -> d) -> f b d/span>br>/pre>/td>/tr>/table>/figure>p>具有 Bifunctor 的值必须提供 code>bimap/code> 函数。code>bimap/code> 函数接受两个参数:/p>pre>code>c.bimap(f, g)/code>/pre>ol>li>code>f/code> 必须是一个返回值的函数,ol>li>如果 code>f/code> 不是函数,则 code>bimap/code> 的行为未指定。/li>li>code>f/code> 可以返回任意值/li>li>code>f/code> 返回值的任何部分都不应该被检查。/li>/ol>/li>li>code>g/code> 必须是一个返回值的函数,ol>li>如果 code>g/code> 不是函数,则 code>bimap/code> 的行为未指定。/li>li>code>g/code> 可以返回任意值br>3.code>g/code> 返回值的任何部分都不应该被检查。/li>/ol>/li>li>code>bimap/code> 必须返回相同 Bifunctor 的值。/li>/ol>h3 idprofunctor>Profunctor/h3>p>实现 Profunctor 规范的值还必须实现 a href#functor>Functor/a> 规范。/p>ol>li>code>p.promap(a > a, b > b)/code> 等价于 code>p/code> (同一性)/li>li>code>p.promap(a > f(g(a)), b > h(i(b)))/code> 等价于 code>p.promap(f, i).promap(g, h)/code> (组合性)/li>/ol>h4 idcode-promap-code-fang-fa>code>promap/code> 方法/h4>figure classhighlight hs>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classtitle>promap/span> :: span classtype>Profunctor/span> p > p b c ~> (a -> b, c -> d) -> p a d/span>br>/pre>/td>/tr>/table>/figure>ol>li>code>f/code> 必须是一个返回值的函数,ol>li>如果 code>f/code> 不是函数,则 code>promap/code> 的行为未指定。/li>li>code>f/code> 可以返回任意值/li>li>code>f/code> 返回值的任何部分都不应该被检查。/li>/ol>/li>li>code>g/code> 必须是一个返回值的函数,ol>li>如果 code>g/code> 不是函数,则 code>promap/code> 的行为未指定。/li>li>code>g/code> 可以返回任意值/li>li>code>g/code> 返回值的任何部分都不应该被检查。/li>/ol>/li>li>code>promap/code> 必须返回相同 Profunctor 的值。/li>/ol>h2 idtui-dao>推导/h2>p>当创建满足多个代数的数据类型是,作者可以选择实现某些方法,然后推导出剩余的方法。推导:/p>ul>li>p>a href#code-equals-code-fang-fa>code>equals/code>/a> 可以由 a href#code-lte-code-fang-fa>code>lte/code>/a> 推导出:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>other/span>) /span>{ retrun span classkeyword>this/span>.lte(other) && other.lte(span classkeyword>this/span>) }/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-map-code-fang-fa>code>map/code>/a> 可以由 a href#code-ap-code-fang-fa>code>ap/code>/a> 和 a href#code-of-code-fang-fa>code>of/code>/a> 推导出:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>f/span>) /span>{ span classkeyword>return/span> span classkeyword>this/span>.ap(span classkeyword>this/span>.of(f))}/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-map-code-fang-fa>code>map/code>/a> 可以由 a href#code-chain-code-fang-fa>code>chain/code>/a> 和 a href#code-of-code-fang-fa>code>of/code>/a> 推导出:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>f/span>) /span>{ span classkeyword>return/span> span classkeyword>this/span>.chain(span classfunction>span classparams>a/span> >/span> span classkeyword>this/span>.of(f(a))); }/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-map-code-fang-fa>code>map/code>/a> 可以由 a href#code-bimap-code-fang-fa>code>bimap/code>/a> 推导出 (??):/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>f/span>) /span>{ span classkeyword>return/span> span classkeyword>this/span>.bimap(span classfunction>span classparams>a/span> >/span> a, f); }/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-map-code-fang-fa>code>map/code>/a> 可以由 a href#code-promap-code-fang-fa>code>promap/code>/a> 推导出:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>f/span>) /span>{ span classkeyword>return/span> span classkeyword>this/span>.promap(span classfunction>span classparams>a/span> >/span> a, f); }/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-ap-code-fang-fa>code>ap/code>/a> 可以由 a href#code-chain-code-fang-fa>code>chain/code>/a> 推导出:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>m/span>) /span>{ span classkeyword>return/span> m.chain(span classfunction>span classparams>f/span> >/span> span classkeyword>this/span>.map(f)); }/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-reduce-code-fang-fa>code>reduce/code>/a> 可以由下列推导出:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>f, acc/span>) /span>{/span>br>span classline> span classfunction>span classkeyword>function/span> span classtitle>Const/span>(span classparams>value/span>) /span>{/span>br>span classline> span classkeyword>this/span>.value value;/span>br>span classline> }/span>br>span classline> Const.of span classfunction>span classkeyword>function/span>(span classparams>\_/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>new/span> Const(acc);/span>br>span classline> }/span>br>span classline> Const.prototype.map span classfunction>span classkeyword>function/span>(span classparams>\_/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>this/span>;/span>br>span classline> }/span>br>span classline> Const.prototype.ap span classfunction>span classkeyword>function/span>(span classparams>b/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>new/span> Const(f(b.value, span classkeyword>this/span>.value));/span>br>span classline> }/span>br>span classline> span classkeyword>return/span> span classkeyword>this/span>.traverse(span classfunction>span classparams>x/span> >/span> span classkeyword>new/span> Const(x), Const.of).value;/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-map-code-fang-fa>code>map/code>/a> 的推导如下:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>f/span>) /span>{/span>br>span classline> span classfunction>span classkeyword>function/span> span classtitle>Id/span>(span classparams>value/span>) /span>{/span>br>span classline> span classkeyword>this/span>.value value;/span>br>span classline> }/span>br>span classline> Id.of span classfunction>span classkeyword>function/span>(span classparams>x/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>new/span> Id(x);/span>br>span classline> }/span>br>span classline> Id.prototype.map span classfunction>span classkeyword>function/span>(span classparams>f/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>new/span> Id(f(b.value));/span>br>span classline> }/span>br>span classline> Id.prototype.ap span classfunction>span classkeyword>function/span>(span classparams>b/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classkeyword>new/span> Id(span classkeyword>this/span>.value(b.value));/span>br>span classline> }/span>br>span classline> span classkeyword>return/span> span classkeyword>this/span>.traverse(span classfunction>span classparams>x/span> >/span> Id.of(f(x)), Id.of).value;/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-filter-code-fang-fa>code>filter/code>/a> 可以由 a href#code-of-code-fang-fa>code>of/code>/a>,a href#code-chain-code-fang-fa>code>chain/code>/a> 和 a href#code-zero-code-fang-fa>code>zero/code>/a> 推导出:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>pred/span>) /span>{/span>br>span classline> span classkeyword>var/span> F span classkeyword>this/span>.constructor;/span>br>span classline> span classkeyword>return/span> span classkeyword>this/span>.chain(span classfunction>span classparams>x/span> >/span> pred(x) ? F.of(x) : F.zero());/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>/li>li>p>a href#code-filter-code-fang-fa>code>filter/code>/a> 还可以由 a href#code-concat-code-fang-fa>code>concat/code>/a>,a href#code-of-code-fang-fa>code>of/code>/a>,a href#code-zero-code-fang-fa>code>zero/code>/a> 和 a href#code-reduce-code-fang-fa>code>reduce/code>/a>:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span>(span classparams>pred/span>) /span>{/span>br>span classline> span classkeyword>var/span> F span classkeyword>this/span>.constructor;/span>br>span classline> span classkeyword>return/span> span classkeyword>this/span>.reduce(span classfunction>(span classparams>f, x/span>) >/span> pred(x) ? f.concat(F.of(x)) : f, F.zero());/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>/li>/ul>h2 idzhu-yi>注意/h2>ol>li>如果实现的方法和规则不止一种,应该选择一种实现,并为其他用途提供包装。/li>li>我们不鼓励重载特定的方法。那样会很容易造成崩溃和错误的行为。/li>li>建议对未指定的行为抛出异常。/li>li>在 a hrefhttps://github.com/sanctuary-js/sanctuary-identity target_blank relnoopener>sanctuary-identity/a> 中提供了一个实现了许多方法的 Id 容器。/li>/ol>h2 idbei-xuan-fang-an>备选方案/h2>p>此外,还存在一个 Static Land 规范,其思想与 Fantasy Land 完全相同,但是是基于静态方法而非实例方法。/p> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2017/12/16/The-Philosophy-of-Ramda/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2017/12/16/The-Philosophy-of-Ramda/ classpost-title-link itempropurl>Ramda 的哲学/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2017-12-16 21:39:27 itempropdateCreated datePublished datetime2017-12-16T21:39:27+00:00>2017-12-16/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> h1 idmu-biao>目标/h1>p>我们编写 Ramda 的目的是,用比原生 JavaScript 更好的方式进行编程。给定数据如下:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>/pre>/td>td classcode>pre>span classline>span classcomment>// `projects` 是一个以下形式的对象类型的数组/span>/span>br>span classline>span classcomment>// {codename: atlas, due: 2013-09-30, budget: 300000, /span>/span>br>span classline>span classcomment>// completed: 2013-10-15, cost: 325000},/span>/span>br>span classline>span classcomment>///span>/span>br>span classline>span classcomment>// `assignments` 是将工程名映射到员工名的对象类型的数组,如下所示:/span>/span>br>span classline>span classcomment>// {codename: atlas, name: abby},/span>/span>br>span classline>span classcomment>// {codename: atlas, name: greg},/span>/span>br>/pre>/td>/tr>/table>/figure>p>我们想按以下形式进行编程:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>span classline>12/span>br>span classline>13/span>br>span classline>14/span>br>span classline>15/span>br>span classline>16/span>br>span classline>17/span>br>span classline>18/span>br>span classline>19/span>br>span classline>20/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> employeesByProjectName R.pipe(/span>br>span classline> R.propEq(span classstring>codename/span>), /span>br>span classline> R.flip(R.filter)(assignments), /span>br>span classline> R.map(R.prop(span classstring>name/span>))/span>br>span classline>);/span>br>span classline>span classkeyword>var/span> onTime R.filter(span classfunction>span classparams>proj/span> >/span> proj.completed < proj.due);/span>br>span classline>span classkeyword>var/span> withinBudget R.filter(span classfunction>span classparams>proj/span> >/span> proj.cost < proj.budget);/span>br>span classline>span classkeyword>var/span> topProjects R.converge(R.intersection, onTime, withinBudget);/span>br>span classline>span classkeyword>var/span> bonusEligible R.pipe(/span>br>span classline> topProjects, /span>br>span classline> R.map(R.prop(span classstring>codename/span>)), /span>br>span classline> R.map(employeesByProjectName), /span>br>span classline> R.flatten, /span>br>span classline> R.uniq/span>br>span classline>);/span>br>span classline>/span>br>span classline>span classbuilt_in>console/span>.log(bonusEligible(projects));/span>br>span classline>span classcomment>// Live version at https://codepen.io/adispring/pen/WdQjXL?editors0012/span>/span>br>span classline>span classcomment>// 译者注:原文用的 ramda@0.22.1 版本比较旧了,converge 第二个之后的函数未加中括号/span>/span>br>span classline>span classcomment>// 本文采用 ramda@0.25.0/span>/span>br>/pre>/td>/tr>/table>/figure>p>这段代码是一段 “函数式” 的 code>pipeline/code>。它是由模块化、可组合的函数构建而成,这些函数拼接在一起形成长长的管道,然后我们可以从管道入口传入待处理的数据。上面的每个 var 变量声明都代表一个单输入单输出的函数。每个函数的输出结果在管道中继续传递下去。/p>p>这些函数对数据进行转换并将转换结果传给下一个函数。需要注意的是,这些函数都不会改变输入参数的值。/p>p>Ramda 的目标是让这种风格的编码在 JavaScript 中更容易些。这就是它的目的,我们的设计决策都是由这个目标驱动的。还有一个唯二值得关注的点:简洁(Simplicity)。我们追求的是简洁(Simple),而不是简单(容易,Easy)。如果你没有看过 Rich Hickey 的 “Simple Made Easy”,你应该花点时间看看。简洁,意味着不要将独立的功能点耦合或纠缠到一起。Ramda 努力坚持这个原则。(单一职责原则)/p>h1 idzuo-you-ming>座右铭/h1>p>Ramda 自认为是 “一个实用的 JavaScript 函数式编程库”。什么意思呢?/p>p>在本文接下来的部分,我们将这句话的解释分成几部分,并在下文中分别讨论每部分在 Ramda 中的含义。/p>h1 idwei-java-script-bian-cheng-ren-yuan-er-she-ji>为 JavaScript 编程人员而设计/h1>h2 idyou-xie-liang-ya>有些惊讶?/h2>p>Ramda 是为编程人员设计的库。它不是一个学术试验品。它是为一线人员构建系统而准备的,它必须能运行,并且是良好、高效地运行。/p>p>我们尽量描述清楚函数的作用,以确保不会因误解而发生意外。不过,我们做的一些事情可能会让很多学术同仁感到惊讶。但只要日常的(工业界)编程人员理解我们,我们就愿意冒这个风险。例如,Ramda 的 code>is/code> 函数可以用来代替 code>isArray/code>、code>isNumber/code>、code>isFunction/code> 等函数。Ramda 版本的类型判断函数接受一个构造函数和一个对象:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>/pre>/td>td classcode>pre>span classline>is(span classbuilt_in>Number/span>, span classnumber>42/span>); span classcomment>//> true/span>/span>br>span classline>is(span classbuilt_in>Function/span>, span classnumber>42/span>); span classcomment>//> false/span>/span>br>span classline>is(span classbuilt_in>Object/span>, {}); span classcomment>//> true/span>/span>br>/pre>/td>/tr>/table>/figure>p>这也适用于自定义的构造函数。如果 code>Square/code> 的原型链上包含 code>Rectangle/code>,则可以这样做:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>is(Rectangle, span classkeyword>new/span> Square(span classnumber>4/span>)); span classcomment>//> true/span>/span>br>/pre>/td>/tr>/table>/figure>p>但这也可能引起学术界同仁的疑惑:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>is(span classbuilt_in>Object/span>, span classnumber>42/span>); span classcomment>//> false/span>/span>br>/pre>/td>/tr>/table>/figure>p>现实世界的编程人员知道这是完全正确的。字符串、布尔值和数字是原生类型,但它们不是对象。然而学者们可能会坚持,认为包装过的 Number 类型继承自 Object,类比 Square/Rectangle ,也应该返回 true。当然,他们可以那么认为… 在他们自己的库里。这些函数对一线的编程人员才是最有用的。(译者注:Ramda 作者可能被学术界 Nerd 们的絮叨伤害过…)/p>h2 idming-ling-shi-shi-xian>命令式实现/h2>p>我们并没有非得以函数式的方式实现 Ramda 的函数。许多我们提出的构造,像 folds、maps、filters 只能通过递归进行函数式实现。但由于 JavaScript 并没有对递归进行优化;我们不能用优雅的递归算法来编写这些函数。相反,我们诉诸于丑陋的、命令式的 while 循环。我们来编写令人讨厌的代码,以便(Ramda)用户可以编写更优雅的代码。Ramda 的实现绝不应该被认为是如何编写函数式代码的指导。(译者注:为了效率和实用性的考虑,Ramda 底层实现其实是命令式的)/p>p>虽然我们从 Haskell、ML 和 LISP(及其变种的函数式部分)等函数式语言中获得很多灵感,但 Ramda 从不试图实现这些语言的任何部分。/p>p>Ramda 也没有试图简单地以函数式的方式重写原生 API。机械的生搬硬套没有任何意义。当我们实现 code>map/code> 函数时,我们既不用非得遵循 Array.prototype.map 的 ECMAScript 规范,也没有囿于已有的实现。我们可以自由地为我们的库定义每个函数的功能,它是如何工作的,确切的参数顺序,它会不会更改输入参数等(永远不会!),返回什么,以及它会抛出什么类型的错误等。换句话说,API 是我们自己的。我们确实受到了函数式编程的传统的限制,但如果在 JavaScript 中使用某些东西需要做出妥协,我们可以做出任何被认为实用的选择。(译者注:总之,我们对 Ramda 有绝对的掌控权)/p>h1 idzuo-wei-yi-ge-ku>作为一个库/h1>p>Ramda 是一个库,一个工具包,或者类比 Underscore ,是一个辅助开发工具。它不是一个决定如何构建应用程序结构的框架(如 React)。相反,它只是一组函数,旨在使之前描述的可组合函数风格的编程更容易一些。这些函数并没有决定你的工作流程。例如,你不必为了使用过滤器而传递 code>where/code> 函数的结果。/p>h2 idwei-shi-yao-bu-shi-yong>为什么不使用…/h2>p>Ramda 不可避免的会与 a hrefhttp://underscorejs.org/ target_blank relnoopener>Underscore/a> 和 a hrefhttp://lodash.com/ target_blank relnoopener>Lodash/a> 做对比;其所提供的函数在功能和函数名称会有重叠。但是,Ramda 不会成为这些库的替代品。即使有一个神奇的参数顺序调整机制,它仍然不是一个简单的替代品。Ramda 有自身的优势、专注于不同领域。请记住,如果这些库能够很容易地按我们想要的方式进行编程,那么就不需要 Ramda 了。/p>p>当我们开始编写该库时,主要的函数式编程库有:/p>ul>li>p>Oliver Steele 的 a hrefhttp://osteele.com/sources/javascript/functional/ target_blank relnoopener>Functional Javascript/a>, 这是首次使用令人难以置信的方式,展示真的可以在 JavaScript 中用函数式的方式编程。但它也只是个玩具,用生产环境中不想要的技巧进行Hack。/p>/li>li>p>Reg Braithwaite 的 a hrefhttps://github.com/raganwald/allong.es target_blank relnoopener>allong.es/a>,这本书已经出来了,并且这个鲜为人知的库已经可以用了。但这个库自称是 Underscore 或 Lodash 的伴侣,虽然做得很好,但它似乎只是一个支持这本书的最小代码集合,而不是一个完整的库。/p>/li>li>p>Michael Fogus 的 a hrefhttps://github.com/fogus/lemonad target_blank relnoopener>Lemonad/a> 是一个具有前瞻性的实验品,也许是这里面最有趣的一个,它的一些函数在其他 JavaScript 库中是没有的。但它似乎只是一个 playground,基于此,该库基本上被废弃了。/p>/li>li>p>当然还有一些大块头,比如 Jeremy Ashkenas 的 a hrefhttp://underscorejs.org/ target_blank relnoopener>Underscore/a> 和 John-David Dalton 的 a hrefhttp://lodash.com/ target_blank relnoopener>Lodash/a>。这些库的广泛使用,显示了大量的 JavaScript 开发人员不再害怕函数式构造。它们非常受欢迎,已经包含了许多我们想要的工具。/p>/li>/ul>p>那么为什么我们不使用 Underscore/Lodash 呢?答案很简单。对于我们想要的编程形式,它们犯了一些根本性的错误:它们传递参数的顺序是错误的。/p>p>这听起来很可笑,甚至无足轻重,但是对于这种编程风格来说确实 strong>必不可少/strong>。为了构建简单可组合的函数,我们需要能正确协同工作的工具。其中最重要的是自动柯里化。为了能正确地进行柯里化,我们必须确保最经常变化的参数 – 通常是数据 – 放到最后。/p>p>差别很简单。假设我们有这样一个可用的函数:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> add span classfunction>span classkeyword>function/span>(span classparams>a, b/span>) /span>{span classkeyword>return/span> a + b;};/span>br>/pre>/td>/tr>/table>/figure>p>并且我们想要一个函数,可以计算一篮子水果的总价格,例如:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> basket /span>br>span classline> {span classattr>item/span>: span classstring>apples/span>, span classattr>per/span>: span classnumber>.95/span>, span classattr>count/span>: span classnumber>3/span>, span classattr>cost/span>: span classnumber>2.85/span>},/span>br>span classline> {span classattr>item/span>: span classstring>peaches/span>, span classattr>per/span>: span classnumber>.80/span>, span classattr>count/span>: span classnumber>2/span>, span classattr>cost/span>: span classnumber>1.60/span>},/span>br>span classline> {span classattr>item/span>: span classstring>plums/span>, span classattr>per/span>: span classnumber>.55/span>, span classattr>count/span>: span classnumber>4/span>, span classattr>cost/span>: span classnumber>2.20/span>}/span>br>span classline>;/span>br>/pre>/td>/tr>/table>/figure>p>我们想要这样写:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> sum reduce(add, span classnumber>0/span>);/span>br>/pre>/td>/tr>/table>/figure>p>并且这样使用:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> totalCost compose(sum, pluck(span classstring>cost/span>));/span>br>/pre>/td>/tr>/table>/figure>p>这就是我们想要的效果。注意看 code>sum/code> 和 code>totalCost/code> 是如此的简洁。使用 Underscore 写一个计算总价的函数并不难,但不会如此简洁。/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> sum span classfunction>span classkeyword>function/span>(span classparams>list/span>) /span>{/span>br>span classline> span classkeyword>return/span> _.reduce(list, add, span classnumber>0/span>);/span>br>span classline>};/span>br>span classline>span classkeyword>var/span> totalCost span classfunction>span classkeyword>function/span>(span classparams>basket/span>) /span>{/span>br>span classline> span classkeyword>return/span> sum(_.pluck(basket, span classstring>cost/span>));/span>br>span classline>};/span>br>/pre>/td>/tr>/table>/figure>p>在 Lodash 中可能的实现如下:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> sum span classfunction>span classkeyword>function/span>(span classparams>list/span>) /span>{/span>br>span classline> span classkeyword>return/span> _.reduce(list, add, span classnumber>0/span>);/span>br>span classline>};/span>br>span classline>span classkeyword>var/span> getCosts _.partialRight(_.pluck, span classstring>cost/span>);/span>br>span classline>span classkeyword>var/span> totalCost _.compose(sum, getCosts);/span>br>/pre>/td>/tr>/table>/figure>p>或者跳过中间变量:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> sum span classfunction>span classkeyword>function/span>(span classparams>list/span>) /span>{/span>br>span classline> span classkeyword>return/span> _.reduce(list, add, span classnumber>0/span>);/span>br>span classline>};/span>br>span classline>span classkeyword>var/span> totalCost _.compose(sum, .partialRight(_.pluck, span classstring>cost/span>));/span>br>/pre>/td>/tr>/table>/figure>p>虽然这已经非常接近我们想要的效果,但是跟 Ramda 版本的相比,还是有差距的:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> sum R.reduce(add, span classnumber>0/span>);/span>br>span classline>span classkeyword>var/span> total R.compose(sum, R.pluck(span classstring>cost/span>));/span>br>/pre>/td>/tr>/table>/figure>p>在 Ramda 中实现这种风格的秘诀非常简单:我们将函数参数放在第一位,数据参数放到最后,并且将每个函数都柯里化。/p>p>来看一下 code>pluck/code>。Ramda 有一个 code>pluck/code> 函数,它和 Underscore 及 Lodash 中的 code>pluck/code> 函数的功能差不多。这些函数接受一个字符串属性名和一个列表;返回由列表元素的属性值组成的列表。但 Underscore 和 Lodash 要求先提供列表,Ramda 希望最后传入列表。当你加入柯里化时,区别非常明显:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>R.pluck(span classstring>cost/span>); span classcomment>//> function :: Object -> costs/span>/span>br>/pre>/td>/tr>/table>/figure>p>通过简单地暂时不传列表参数给 code>pluck/code>,我们得到一个新函数:接受一个列表,并从新提供的列表中提取 code>cost/code> 属性值。/p>p>重申一下,就是这个简单的区别,将数据参数放到最后的自动柯里化函数让这两种风格变得不同:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> sum span classfunction>span classkeyword>function/span>(span classparams>list/span>) /span>{/span>br>span classline> span classkeyword>return/span> _.reduce(list, add, span classnumber>0/span>);/span>br>span classline>};/span>br>span classline>span classkeyword>var/span> total span classfunction>span classkeyword>function/span>(span classparams>basket/span>) /span>{/span>br>span classline> span classkeyword>return/span> sum(_.pluck(basket, span classstring>cost/span>));/span>br>span classline>};/span>br>/pre>/td>/tr>/table>/figure>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> sum R.reduce(add, span classnumber>0/span>);/span>br>span classline>span classkeyword>var/span> total R.compose(sum, R.pluck(span classstring>cost/span>));/span>br>/pre>/td>/tr>/table>/figure>p>这就是我们开始编写一个新库的原因。/p>h1 idshe-ji-xuan-ze>设计选择/h1>p>接下来的问题是我们想要一个什么类型的库。我们当然知道我们想要一个简洁而又不怪异的 API。但是,这里仍然有一个悬而未决的问题:需要怎样确定 API 的适用广度和深度。/p>p>API 的广度,仅仅指它想要覆盖多少不同类型的功能。有两百个函数的 API 比只有十个函数的 API 适用范围要广得多。与大多数其他库一样,我们对其广度(适用范围)没有特别的限制。我们添加有用的函数,而不用担心库的规模的增大会导致崩溃。/p>p>一个库的深度,可以衡量它的函数们在独立使用时,可以提供多少种的不同的方式。(关于它们如何组合,是另一个完全不同的问题)在这里,我们走向了与 Underscore 及 Lodash 完全不同的方向。因为 JavaScript 不会去检查参数的类型和数量,所以编写根据传入确切参数(参数的类型和数量)而具有多种不同行为的单个函数是相当容易的。Underscore 和 Lodash 使用这种方法让它们的函数更灵活。例如,在 Lodash 中,code>pluck/code> 不仅可以作用在 list 上,还可以作用在 object 和 string 上。从这个意义上讲,Lodash 是一个相当有深度的 API。Ramda 试图保持相对较浅的深度,原因如下:/p>p>Lodash 提供的功能如下:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>_.pluck(span classstring>abc/span>, propertyName);/span>br>/pre>/td>/tr>/table>/figure>p>其将字符串拆分成由单字母字符串组成的数组,然后返回从每个字符串中提取的指定属性形成的数组。想找个这样的合适的应用场景是非常困难的:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>_.pluck(span classstring>abc/span>, span classstring>length/span>); span classcomment>//> 1, 1, 1/span>/span>br>/pre>/td>/tr>/table>/figure>p>如果你真的想要一个元素为 code>1/code> ,且对应字符串中的每个字母的列表,下面这段代码比我的 Ramda 解法要短一些:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>map(always(span classnumber>1/span>), split(span classstring>/span>, span classstring>abc/span>));/span>br>/pre>/td>/tr>/table>/figure>p>但这貌似没什么用,因为唯一另外一个属性是有意义的:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>_.pluck(span classstring>abc/span>, span classstring>0/span>); span classcomment>//> a, b, c/span>/span>br>/pre>/td>/tr>/table>/figure>p>如果 code>pluck/code> 不存在,下面这样也是可以的:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classstring>abc/span>.split(span classstring>/span>); span classcomment>//> a, b, c/span>/span>br>/pre>/td>/tr>/table>/figure>p>所以在字符串上操作并没多大用处。之所以将其(字符串)包含进来,可能是因为所有属于 Lodash “集合” 类的函数都应该能同时适用于数组、对象和字符串;这只是一个一致性问题。(令人失望的是,Lodash 没有打算扩展到其他实际的集合中去,比如 Map 和 Set)我们已经理解了 code>pluck/code> 是如何在数组上工作的。它涵盖的另一种类型是对象,如下所示:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> flintstones1 {/span>br>span classline> A: {span classattr>name/span>: span classstring>fred/span>, span classattr>age/span>: span classnumber>30/span>},/span>br>span classline> B: {span classattr>name/span>: span classstring>wilma/span>, span classattr>age/span>: span classnumber>28/span>},/span>br>span classline> C: {span classattr>name/span>: span classstring>pebbles/span>, span classattr>age/span>: span classnumber>2/span>}/span>br>span classline>};/span>br>span classline>_.pluck(flintstones1, span classstring>age/span>); span classcomment>//> 30, 28, 2/span>/span>br>/pre>/td>/tr>/table>/figure>p>可以创建一个对象,code>flintstones2/code> ,且以下结果为 code>true/code>:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>_.isEqual(flintstones1, flintstones2); span classcomment>//> true/span>/span>br>/pre>/td>/tr>/table>/figure>p>但下面结果却为 code>false/code>:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>_.pluck(flintstones1, span classstring>age/span>); _.pluck(flintstones2, span classstring>age/span>); span classcomment>//> false;/span>/span>br>/pre>/td>/tr>/table>/figure>p>下面是一种可能的情况:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> flintstones2 {/span>br>span classline> B: {span classattr>name/span>: span classstring>wilma/span>, span classattr>age/span>: span classnumber>28/span>},/span>br>span classline> A: {span classattr>name/span>: span classstring>fred/span>, span classattr>age/span>: span classnumber>30/span>},/span>br>span classline> C: {span classattr>name/span>: span classstring>pebbles/span>, span classattr>age/span>: span classnumber>2/span>}/span>br>span classline>};/span>br>span classline>_.pluck(flintstones2, span classstring>age/span>); span classcomment>//> 28, 30, 2/span>/span>br>/pre>/td>/tr>/table>/figure>p>问题在于,a hrefhttp://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4 target_blank relnoopener>根据规范/a>,对象 keys 的迭代顺序是依赖于实现的;通常它们按照添加到对象中的顺序进行迭代。。/p>p>在写本文时,我提交了一个关于这个问题的 issue。在最好的情况下,只有通过记录问题才能解决问题。但这个问题实在影响深远。如果你想统一列表和对象的行为,你将会不断遇到这个问题,除非你实现一个(非常慢的!)统一的顺序对 Object 属性进行迭代。/p>p>在 Ramda 中,code>pluck/code> 只作用于列表。它接受一个属性名和一个列表,并返回一个相同长度的新列表。仅此而已。这个 API 深度很浅。(译者注:适用范围不太广)。/p>p>你可以将其看作特点,也可以看作是缺点。以 Lodash 的 code>filter/code> 为例: 它接受一个数组、对象或字符串作为第一个集合(参数),然后接受一个函数、对象、字符串或者空作为它的回调,并且还需要一个对象或空作为它的 this 参数。你将一次获得 3 * 4 * 2 24 个函数!这要么是一个很大的问题,要么增加了从中找到一个你真正想要的方案的难度,增加了太多复杂性。决定权在于你。/p>p>在 Ramda 中,我们认为这种风格会增加不必要的复杂性。我们发现简单的函数签名对于维持简洁是至关重要的。如果我们需要函数既能作用于列表,又能作用于对象,我们会创建各自独立的函数(译者注:一般情况下会这样,但也有特例,比如 code>map/code>)。如果有一个参数我们偶尔会用到,我们不会创建一个可选参数,而是创建两个函数。尽管这扩大了 API 的规模,但是它们保持了一至的浅度。/p>h2 idapi-de-zeng-chang>API 的增长/h2>p>有一个我们已经意识到的危险,一个可以用三个字母拼出来的危险:“PHP”。我们不希望我们的 API 变成一个不可持续的、功能不一致的怪物。这是真正的威胁,没有强制性的规范来确定我们应该或不应该包含什么。/p>p>我们一直在努力;我们不希望包含一个貌似有用的函数。/p>p>为了避免变成 “PHP” 风格的庞然大物,我们专注于几件事情。首先,API 为王。虽然我们想要函数实现尽可能优雅,但我们为了即使是轻微的 API 性能改进,而牺牲了大量优雅的实现。我们试图执行严格的一致性标准。例如:像 code>somethingBy/code> 这样的 Ramda 函数,以标准的方式看,与 code>somethingWith/code> 函数是不同的。如 a hrefhttps://github.com/ramda/ramda/issues/65 target_blank relnoopener>issue 65/a> 所述,我们/p>blockquote>p>使用 xxBy 来表示单一属性的比较,无论是对象的自然属性还是合成属性;使用 xxWith 表示更具一般性的函数。/p>/blockquote>p>一些使用这种方式的函数的例子包括max / min / sort / uniq / difference。/p>h1 idhan-shu-shi>函数式/h1>p>JavaScript 是一门多范式语言。你可以编写简单的命令式代码,面对对象的代码,或函数式代码。原始命令式的代码非常直白、简单。有很多库可以帮助你将 JavaScript 作为面向对象的语言使用。但是将 JavaScript 作为函数式语言使用的库非常少。Ramda 帮忙填补了这个空缺。/p>p>如前所述,我们当然不是第一个。其他库通过各种不同方式让人们可以在 JavaScript 中进行函数式编程(FP)。在我看来,将函数式世界与 JavaScript 结合最成功的可能是 a hrefhttps://github.com/raganwald/allong.es target_blank relnoopener>allong.es/a>。但它不是一个流行的库,与 a hrefhttp://underscorejs.org/ target_blank relnoopener>Underscore/a> 、 a hrefhttp://lodash.com/ target_blank relnoopener>Lodash/a> 这些库不在一个级别上(就流行程度而言);并且它有一个与 Ramda 不同的目标:它被设计为一种教学工具,一本书的演示库。/p>p>Ramda 正在尝试一些不同的东西。它的目标是成为一个能进行日常实际工作的实用的函数式库。/p>p>我们从头开始构建这个函数式库,使用了许多其他函数式语言通用的技术,以对 JavaScript 有意义的方式对这些技术进行移植。我们并没有试图弥合与面向对象世界之间的鸿沟,或者复制每一种函数式语言的每一个特性。实际上,我们甚至没有试图复制单一函数式语言的每个特性。它仍然是 JavaScript,甚至还继承了 JavaScript 缺陷。/p>h2 idhan-shu-shi-te-xing>函数式特性/h2>p>那么,在广阔的函数式编程领域里,哪些部分是我们想要保留的,又有哪些不在我们的考虑范围呢?下面列出了函数式编程的一些主要(不完整)特性:/p>ul>li>一等函数/li>li>高阶函数/li>li>词法闭包/li>li>引用透明/li>li>数据不可变/li>li>模式匹配/li>li>惰性求值/li>li>高效递归(TCO)/li>li>a hrefhttps://en.wikipedia.org/w/index.php?titleHomoiconicity&redirectno target_blank relnoopener>同像性(Homoiconic)/a>/li>/ul>p>前几个特性都已经内置在 JavaScript 中了。JavaScript 中的函数是一等公民,意味着我们可以像使用字符串、数字或对象等,对其引用或传递。我们还可以将函数作为参数传递给其他函数,并返回全新的函数,所以 JavaScript 中包含高阶函数。因为返回函数可以访问其在创建时的上下文中的所有变量,所以我们也在语言中构建出了词法闭包。/p>p>除此之外,上面列出其他的特性都没有自动包含在 JavaScript 中。有的可以轻易实现,有的只能部分或很难实现,有的则超出了语言的当前能力。/p>p>Ramda 可以确保在不会导致你的代码出问题的情况下,帮助实现(管理)上面的其他一些特性。例如,Ramda 不会改变你的输入数据。永远也不会!如果使用 code>append/code> 将元素添加到列表的末尾,则会返回包含添加元素的新列表。你的原始列表保持不变。所以,由于 Ramda 不会尝试强行改变不可变的客户端数据,它可以很容易的与不可变数据一起工作。/p>p>另一方面,Ramda 强制要求引用透明。这个概念的意思是:可以在不改变整个程序行为的情况下,将表达式替换为其对应的计算值。对于 Ramda 来说,这意味着 Ramda 不会在应用程序中存储内部状态,也不会引用任何全局变量或者内部状态可以变的闭包。简言之,当你使用相同的值调用 Ramda 函数时,总会得到相同的结果。/p>p>在撰写本文时,正在讨论 Ramda 的惰性求值问题。一些库如 a hrefhttp://danieltao.com/lazy.js/ target_blank relnoopener>Lazy.js/a> 和 a hrefhttps://github.com/goatslacker/lz target_blank relnoopener>Lz.js/a> ,表明在 JavaScript 中进行惰性求值是可行的。a hrefhttps://github.com/cognitect-labs/transducers-js target_blank relnoopener>Transducer/a> 提供了一种模拟惰性求值的方法。Ramda 正在努力增强自己这方面的能力。但这是一个巨大的改变,并不会很快实现。/p>p>Ramda 还会考虑加入一定程度的模式匹配,但不会像 Erlang 或 Haskell 这样的语言中的那么强大或方便。我们并没有看到会改变语言语法的宏,所以我们最多可以做一些类似于 a hrefhttp://raganwald.com/2014/06/23/multiple-dispatch.html#guarded-functions target_blank relnoopener>Reg Braithwaite 所描述的东西/a>。但是这至少在某种程度上讲是一种模式匹配的技术。/p>p>其他特性都超出了 Ramda 的能力。虽然有 a hrefhttps://en.wikipedia.org/wiki/Trampoline_(computing) target_blank relnoopener>trampolining/a> 技术可以让你在不使用尾递归优化工具的情况下获得递归的一些好处,但是它们由于侵入性太强而不能被普遍使用。所以 Ramda 内部没有使用太多递归,也没有提供任何帮助来实现有效的递归。好消息是它将会被提到下一版语言规范的计划中去。/p>p>然后是 strong>同像性(homoiconicity)/strong> – 某些语言(LISP、Prolog)的特性:程序的语法可以用一种在自身语言中易于理解和修改的数据结构表示的。这远远超出了 JavaScript 当前的能力,甚至超出了 Ramda 的梦想。/p>h1 idzu-he-xing>组合性/h1>p>Ramda 的目标之一是,允许用户使用小的可组合函数,这是函数式编程的关键。/p>p>函数式编程通常涉及一些少量常见的数据结构,以及搭配操作它们的大量函数。这就是 Ramda 的工作原理。/p>p>简言之,Ramda 主要进行列表操作。但 JavaScript 没有列表的实现;最接近的模拟是 Array(数组)。这是 Ramda 使用的最基本的数据结构。我们不关心 JavaScript 数组的一些深层次可能的性质。我们忽略稀疏数组。如果你传了一个这样的数组给 Ramda,有可能会得到意想不到的结果。你需要传给 Ramda 以 Array 实现的列表。(如果这对你没有意义,不用担心;这是人们使用 JavaScript 数组的标准方式,你必须非常努力,才能创建出不寻常的情况(译者注:错误的情况))。/p>p>许多 Ramda 函数接受列表并且返回列表。这些函数都很容易组合。/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>/pre>/td>td classcode>pre>span classline>span classcomment>// :: Comment -> Number /span>/span>br>span classline>span classkeyword>var/span> userRatingForComments R.compose(/span>br>span classline> R.pluck(span classstring>rating/span>), span classcomment>// User -> Number/span>/span>br>span classline> R.map(R.propOf(users)), span classcomment>// String -> User/span>/span>br>span classline> R.pluck(span classstring>username/span>) span classcomment>// Comment -> String/span>/span>br>span classline>);/span>br>/pre>/td>/tr>/table>/figure>p>Ramda 还包含 code>pipe/code> 函数,它跟 code>compose/code> 功能相同,但顺序是反的;我个人觉得它更可读一些:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>/pre>/td>td classcode>pre>span classline>span classcomment>// :: Comment -> Number /span>/span>br>span classline>span classkeyword>var/span> userRatingForComments R.pipe(/span>br>span classline> R.pluck(span classstring>username/span>), span classcomment>// Comment -> String/span>/span>br>span classline> R.map(R.propOf(users)), span classcomment>// String -> User/span>/span>br>span classline> R.pluck(span classstring>rating/span>) span classcomment>// User -> Number/span>/span>br>span classline>);/span>br>/pre>/td>/tr>/table>/figure>p>当然,组合可以作用于任何类型。如果下一个函数接受当前函数返回的类型,那么一切都应该没问题。/p>p>为了让其工作,Ramda 的函数必须具有足够小的规模。这与 Unix 的哲学不谋而合:大型的工具应该由小工具构建而成,每个工具做且只做一件事情。Ramda 的函数也是如此。理想情况下,这意味着以这些函数为基础的系统的复杂性只是问题自身固有的复杂性,而不是由库增加的附带的复杂性。/p>h2 idbu-bian-xing>不变性/h2>p>需要再次重申,Ramda 函数不会修改输入数据。这是函数式编程的核心原则,也是 Ramda 工作的核心。虽然这些函数可能会改变内部局部变量,但 Ramda 不会改变传递给它的任何数据。/p>p>这并不意味着你使用的所有东西都会被复制。Ramda 重用了它所能用到的。因此,在像 code>assoc/code> 和 code>assocPath/code> 这样的函数,返回具有特定更新属性的对象的克隆中,原始数据的所有非原生(non-primitive)属性在新对象中将以引用的方式使用。如果你想要一个对象的完全解耦的副本,Ramda 提供了 code>cloneDeep/code>(译者注:现在 Ramda 只提供 code>clone/code> 用作深拷贝) 函数。/p>p>这种不变性对 Ramda 来说是硬性规定。任何牵扯到变更用户数据的 pull request 都会被拒绝。我们认为这是 Ramda 的主要特征之一。/p>h1 idshi-yong-xing>实用性/h1>p>最后,Ramda 的目标是成为一个实用的库。这更难表述,因为实用性就像 “美丽” 一样:总是在旁观者眼中才能反映出来。永远都会有对不符合 Ramda 哲学的功能的要求,在那些提议者心目中,这些功能都是非常实用的。通常这些函数(功能)本身是有用的,但是由于不符合 Ramda 的哲学而被拒绝。/p>p>对于 Ramda 而言,实用性意味着一些具体的事情。/p>h2 idming-ling-shi-shi-xian-1>命令式实现/h2>p>首先,Ramda 的实现并未遵循 LISP、ML 或者 Haskell 库中的优雅的编码技术。我们使用丑陋的命令式的循环,而不是优雅的递归代码块。一些 Ramda 的作者曾经在一个叫 a hrefhttps://github.com/CrossEye/eweda target_blank relnoopener>Eweda/a> 的早起的库中走过这条路,代码非常漂亮,但是在解决实际问题上它却失败了。许多列表函数只能处理一千个左右的条目,而且性能也很糟糕。 JavaScript 的设计没有很好的处理递归,大多数当前的引擎不执行任何尾部调用优化。/p>p>而 Ramda 的源代码却使用了乱七八糟的丑陋的 code>while/code> 循环。/p>p>这意味着 Ramda 的实现不能作为如何编写功能良好的 JavaScript 的模型(模板)。这太糟糕了。但它是目前的 JavaScript 引擎最实用的一种选择(方案)。/p>h2 idhe-li-de-api>合理的 API/h2>p>Ramda 还试图就 API 中应该包含什么做出实用的选择。我们并没有试图移植 Clojure、Haskell 或任何其他函数式语言中的任何特定的函数子集,也没有试图模仿更成熟的 JavaScript 库或规范的 API。我们采纳函数的标准是,它们表现出合理的效用。当然,它们也必须与我们的函数式范式相契合才会被考虑,但这还不够;我们必须确信它们将会被用到,并且它们提供了通过当前函数不容易实现的价值。/p>p>后者是比较棘手的。有一个平衡的方案,以确定什么情况下语法糖是可以接受的。在之前,我们讨论了 code>compose/code> 有一个执行顺序相反孪生同胞 code>pipe/code>。有一种观点认为这是一种浪费,我们不应该把 API 因为这些多余的函数而搞乱。毕竟,/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>R.pipe(fn1, fn2, ..., fnN)/span>br>/pre>/td>/tr>/table>/figure>p>可以重写为如下形式:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>R.apply(R.compose, R.reverse(fn1, fn2, ..., fnN));/span>br>/pre>/td>/tr>/table>/figure>p>但是,我们确实选择将 code>pipe/code> 以及其他一些看似多余的函数包含到其中,当它们符合下面的条件时:/p>ul>li>很有可能会被用到/li>li>能更好的表达开发人员的意图/li>li>足够简单的实现/li>/ul>h2 idzheng-ji-qie-yi-zhi-de-api>整洁且一致的 API/h2>p>对于整体一致 API 的追求,听起来不像是一个现实的考虑,更像是一个纯粹主义者的目标。但事实上,提供简单而一致的 API 使得 Ramda 更易于使用。例如,一旦你习惯了 Ramda 对参数顺序的设定,你将很少需要查阅文档以确定如何构建你的调用。/p>p>另外,Ramda 坚决反对可选参数。这个决定有助于形成非常整洁的 API。一个函数应该做什么以及如何调用,通常是非常直观的。/p>h2 idbing-mei-you-shi-yao-hui-bang-zhu-wo-de-jian-yi>并没有 “什么会帮助我” 的建议/h2>p>最后,向某个人解释这个问题通常是最困难的,那就是一个用户对什么才是实用的概念与整个库的实用性实际上可能只有一点点关系。即使提出的函数有助于解决某个难题,如果问题太过狭隘,或者解决方案偏离了我们的基础哲学,那么它也不会被纳入到 Ramda 中。虽然实用性是在旁观者眼中反映出来的,但那些能够纵观整个库的旁观者会有一个宏观的不同的视野,只有那些能够在整体上提升 Ramda 的改变才会被采纳。/p>h1 idjie-lun-sheng-er-bu-tong>结论:生而不同/h1>p>Ramda 的诞生是因为,没有任何其他的库能以我们想要的方式工作。我们想要将可以作用于不可变数据的小型可组合函数,组合成简洁的函数式的 pipeline (管道)。当 Ramda 与类似的库相比较时,这涉及到一些似乎颇具争议的决定。我们并不担心这一点。Ramda 为我们工作的很好,似乎也满足了a hrefhttps://github.com/ramda/ramda/stargazers target_blank relnoopener>社区的需求/a>。/p>p>我们不再孤单。自从我们开始以来,a hrefhttps://github.com/nullobject/fkit target_blank relnoopener>FKit/a> 也萌发了相似的想法。这是一个不太成熟的库,它的工作方式和 a hrefhttps://github.com/CrossEye/eweda target_blank relnoopener>Eweda/a> 一样,试图在 API 及其实现上同时保持真正的优雅。在我看来,他们很可能会遇到性能瓶颈。但是,我们无能为力,只能祝福他们。/p>p>Ramda 正在努力坚持它作为 “JavaScript 开发人员的实用的函数式库” 的座右铭。我们认为我们正在管理和维护 Ramda。但我们也a hrefhttps://github.com/ramda/ramda/issues target_blank relnoopener>很乐意倾听/a> 您的想法。/p> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> article itemscope itemtypehttp://schema.org/Article classpost-block langen> link itempropmainEntityOfPage hrefhttps://adispring.github.io/2017/12/04/Functions-in-Functional-Programming/> span hidden itempropauthor itemscope itemtypehttp://schema.org/Person> meta itempropimage content/images/avatar.gif> meta itempropname contentwangzengdi> meta itempropdescription contentFE@Meituan.com> /span> span hidden itemproppublisher itemscope itemtypehttp://schema.org/Organization> meta itempropname contentwangzengdis Blog> /span> header classpost-header> h2 classpost-title itempropname headline> a href/2017/12/04/Functions-in-Functional-Programming/ classpost-title-link itempropurl>函数式编程中的“函数们”/a> /h2> div classpost-meta> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar>/i> /span> span classpost-meta-item-text>Posted on/span> time titleCreated: 2017-12-04 08:30:46 itempropdateCreated datePublished datetime2017-12-04T08:30:46+00:00>2017-12-04/time> /span> span classpost-meta-item> span classpost-meta-item-icon> i classfar fa-calendar-check>/i> /span> span classpost-meta-item-text>Edited on/span> time titleModified: 2022-04-27 12:20:36 itempropdateModified datetime2022-04-27T12:20:36+00:00>2022-04-27/time> /span> /div> /header> div classpost-body itemproparticleBody> p>函数式编程中的函数有三种不同的解读方式,分别为纯函数、高阶函数和一等函数。本文分别对这三者的概念、应用和联系进行详解。/p>h1 idchun-han-shu>纯函数/h1>p>定义:/p>blockquote>ol>li>相同的输入必定产生相同的输出;/li>li>在计算的过程中,不会产生副作用。/li>/ol>/blockquote>p>满足上述两个条件,我们就说该函数是纯函数。/p>p>纯函数也即数学意义上的函数,表达的是数据之间的转换(映射)关系,而非计算步骤的详述。数学函数的定义:/p>blockquote>p>函数通常由定义域 em>X/em> 、值域 em>Y/em> 以及定义域到值域的映射 em>f/em> (em>f: X -> Y/em>)组成。/p>/blockquote>p>img src./function.png altfunction>/p>p>纯函数让我们对写出的函数具有完全的控制能力。纯函数的结果 strong>必须/strong> 只依赖于输入参数,不受外部环境的影响;同时纯函数在计算结果的过程中,也不会影响(污染)外部环境,即不会产生副作用。/p>h2 idhan-shu-zu-he>函数组合/h2>p>纯函数定义中的两个条件保证了它(的计算过程)与外界是完全隔离,这也是函数组合的基础。/p>p>只有函数组合中的所有函数都是纯函数,我们组合起来的新函数才会是纯函数。我们可以对使用纯函数组合出来的新函数从数学上证明(推导)其正确性,而无需借助大量的单元测试。/p>p>只要在函数组合时引入一个非纯函数,整个组合出来的函数将沦为非纯函数。如果将函数组合比作管道的拼接,只要组成管道的任何一小节有泄露或者外部注入,我们便失去了对整条管道的完全控制。/p>p>img src./pipeline-leaking.png altpipeline-leaking>/p>p>要想实现函数组合,还需要满足连续性,描述如下:/p>p>因为纯函数可以看作定义域到值域映射,待组合的函数中的上一个函数的值域须等于下一个函数的定义域,也即上一个函数的输出(类型)等于下一个的输入(类型)。/p>p>假设有两个函数:em>f: X -> Y/em> 和 em>g: Y -> Z/em>,只有 code>codomain(f) domain(g)/code> 时,em>f/em> 和 em>g/em> 才可以组合。/p>p>img src./functions_composition.png altfunction_composition>/p>h2 idyin-yong-tou-ming-ji-huan-cun>引用透明及缓存/h2>p>在不改变整个程序行为的情况下,如果能将其中的一段代码替换为其执行的结果,我们就说这段代码是引用透明的。/p>p>因此,执行一段引用透明的代码(函数),对于相同的参数,总是给出相同的结果。我们也称这样的函数(代码)为纯函数。/p>p>引用透明的一个典型应用即函数缓存。我们可以将已经执行过的函数输入值缓存起来,下次调用时,若输入值相同,直接跳过计算过程,用缓存结果代替计算结果返回即可。/p>p>函数缓存的实现依赖于闭包,而闭包的实现又依赖于高阶函数,高阶函数的实现又依赖于一等函数。我们按照这条依赖链,从里往外依次对它们进行讲解。/p>h1 idyi-deng-han-shu-first-class-functions>一等函数(First Class Functions)/h1>p>程序语言会对基本元素的使用方式进行限制,带有最少限制的元素被称为一等公民,其拥有的 “权利” 如下:/p>blockquote>ul>li>可以使用变量命名;/li>li>可以提供给函数作为参数;/li>li>可以由函数作为结果返回;/li>li>可以包含在数据结构中;/li>/ul>/blockquote>p>乍一看,我们应该首先会想到程序中的基本数据结构(如 number、array、object 等)是一等公民。如果函数也被视为一等公民,我们便可以像使用普通数据一样对其使用变量命名,作为参数或返回值使用,或者将其包含在数据结构中。在这里函数和数据的边界开始变得不再那么分明了。函数被视为一等公民后,其能力和适用范围被大大扩展了。/p>p>下面使用 JavaScript 对上面第一条和第四条 “权利” 进行讲解。第二、三条与高阶函数密切相关,将放到下一节的高阶函数中讲解。/p>h2 idshi-yong-bian-liang-ming-ming>使用变量命名/h2>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>const/span> square span classfunction>span classparams>x/span> >/span> x * x/span>br>/pre>/td>/tr>/table>/figure>p>上面代码定义了一个求平方值的函数,并将其赋给了 square 变量。/p>h2 idke-yi-bao-han-zai-shu-ju-jie-gou-zhong>可以包含在数据结构中/h2>p>a hrefhttp://ramda.cn/ target_blank relnoopener>Ramda/a> 中有一个API:a hrefhttp://ramda.cn/docs/#evolve target_blank relnoopener>evolve/a>,其接受的首个参数便是一个属性值为函数的对象。a hrefhttp://ramda.cn/docs/#evolve target_blank relnoopener>evolve/a> 函数会递归地对 “待处理对象” 的属性进行变换,变换的方式由 transformation 内置函数属性值的对象定义。示例如下(示例中的 code>R.xxx/code> 都是 a hrefhttp://ramda.cn/docs/ target_blank relnoopener>Ramda/a> 中的API,相关API的功能可以参考a hrefhttp://ramda.cn/docs/ target_blank relnoopener>Ramda/a>文档):/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>var/span> tomato {span classattr>name/span>: span classstring>Tomato/span>, span classattr>data/span>: {span classattr>elapsed/span>: span classnumber>100/span>, span classattr>remaining/span>: span classnumber>1400/span>}, span classattr>id/span>:span classnumber>123/span>};/span>br>span classline>span classkeyword>var/span> transformations {/span>br>span classline> name: R.toUpper,/span>br>span classline> data: {span classattr>elapsed/span>: R.add(span classnumber>1/span>), span classattr>remaining/span>: R.add(span classnumber>-1/span>)}/span>br>span classline>};/span>br>span classline>/span>br>span classline>R.evolve(transformations)(tomato);/span>br>span classline>span classcomment>//> {name: TOMATO, data: {elapsed: 101, remaining: 1399}, id:123}/span>/span>br>/pre>/td>/tr>/table>/figure>h1 idgao-jie-han-shu>高阶函数/h1>p>定义:/p>blockquote>p>使用函数作为输入参数,或者返回结果为函数的函数,被称为高阶函数。/p>/blockquote>p>作为参数或返回值的函数,是一等函数的应用之一。高阶函数以一等函数作为基础,只有支持一等函数的语言才能进行高阶函数编程。/p>p>以熟悉的 a hrefhttp://ramda.cn/docs/#filter target_blank relnoopener>filter/a> 函数为例,我们可以用 a hrefhttp://ramda.cn/docs/#filter target_blank relnoopener>filter/a> 对列表中的元素进行过滤,筛选出符合条件的元素。a hrefhttp://ramda.cn/docs/#filter target_blank relnoopener>filter/a> 的类型签名和示例代码如下:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>filter :: (a → span classbuilt_in>Boolean/span>) → a → a/span>br>/pre>/td>/tr>/table>/figure>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>const/span> isEven span classfunction>span classparams>n/span> >/span> n % span classnumber>2/span> span classnumber>0/span>;/span>br>span classline>/span>br>span classline>span classkeyword>const/span> filterEven R.filter(isEven);/span>br>span classline>/span>br>span classline>filterEven(span classnumber>1/span>, span classnumber>2/span>, span classnumber>3/span>, span classnumber>4/span>); span classcomment>//> 2, 4/span>/span>br>/pre>/td>/tr>/table>/figure>p>a hrefhttp://ramda.cn/docs/#filter target_blank relnoopener>filter/a> 接受一个判断函数(判断输入值是否为偶数)code>isEven/code>,返回一个过滤出偶数的函数 code>filterEven/code>。/p>h1 idbi-bao>闭包/h1>p>定义:/p>blockquote>p>闭包是由函数及该函数捕获的其上下文中的自由变量组成的记录/p>/blockquote>p>举例讲:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>span classline>9/span>br>span classline>10/span>br>span classline>11/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span> span classtitle>add/span>(span classparams>x/span>) /span>{/span>br>span classline> span classkeyword>const/span> xIn x;/span>br>span classline> span classkeyword>return/span> span classfunction>span classkeyword>function/span> span classtitle>addInner/span>(span classparams>y/span>) /span>{/span>br>span classline> span classkeyword>return/span> xIn + y;/span>br>span classline> }/span>br>span classline>}/span>br>span classline>span classkeyword>const/span> inc add(span classnumber>1/span>);/span>br>span classline>inc(span classnumber>8/span>); span classcomment>//> 9;/span>/span>br>span classline>/span>br>span classline>span classkeyword>const/span> plus2 add(span classnumber>2/span>);/span>br>span classline>plus2(span classnumber>8/span>); span classcomment>//> 10;/span>/span>br>/pre>/td>/tr>/table>/figure>p>上述代码中返回的函数 code>addInner/code> 及由其捕获的在其上下文中定义的自由变量 code>xIn/code>,便组成了一个闭包。/p>p>img src./closure.png altclosure>/p>p>上述代码中最外层的 code>add/code> 函数是一个高阶函数,其返回值为一等函数 code>addInner/code>。/p>p>其实 code>add/code> 函数的参数 code>x/code> 也是 code>addInner/code> 上下文的一部分,所以 ‘xIn’ 也就没有存在的必要了,code>add/code> 代码优化如下:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>/pre>/td>td classcode>pre>span classline>span classfunction>span classkeyword>function/span> span classtitle>add/span>(span classparams>x/span>) /span>{/span>br>span classline> span classkeyword>return/span> span classfunction>span classkeyword>function/span> span classtitle>addInner/span>(span classparams>y/span>) /span>{/span>br>span classline> span classkeyword>return/span> x + y;/span>br>span classline> }/span>br>span classline>}/span>br>/pre>/td>/tr>/table>/figure>p>借助于箭头函数,我们可以进一步优化 code>add/code> 的实现:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>const/span> add span classfunction>span classparams>x/span> >/span> span classfunction>span classparams>y/span> >/span> x + y/span>br>/pre>/td>/tr>/table>/figure>p>是不是非常简洁?由此我们可以一窥函数式编程强大的表达能力。/p>p>闭包主要用来做数据缓存,而数据缓存应用非常广泛:包括函数工厂模式、模拟拥有私有变量的对象、函数缓存、还有大名鼎鼎的柯里化。/p>p>其实上述代码中 code>add/code> 函数便是柯里化形式的函数。/p>p>上述代码中的 code>const inc add(1);/code> 和 code>const plus2 add(2);/code> 是一种函数工厂模式,通过向 code>add/code> 函数传入不同的参数,便会产生功能不同的函数。函数工厂可以提高函数的抽象和复用能力。/p>p>例如我们有一个如下形式的 Ajax 请求函数:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>const/span> ajax span classfunction>span classparams>method/span> >/span> span classfunction>span classparams>type/span> >/span> span classfunction>span classparams>query/span> >/span> { ... };/span>br>span classline>/span>br>span classline>span classkeyword>const/span> span classkeyword>get/span> ajax(GET);/span>br>span classline>const post ajax(POST);/span>br>span classline>/span>br>span classline>const getJson span classkeyword>get/span>(json);/span>br>span classline>const getHtml ajax(GET)(text/html) span classkeyword>get/span>(text/html);/span>br>/pre>/td>/tr>/table>/figure>p>我们抽象出了最一般的 code>ajax/code> 请求函数,在具体应用时,我们用能通过函数工厂生产出作用不同的函数。/p>p>通过上面几个小节,我们讲解了纯函数(数学意义上的函数)、一等函数、高阶函数,还有闭包。/p>p>下面通过一个集上述所有概念于一身的 strong>函数缓存/strong> ,来结束函数式编程中的 “函数们” 的论述。/p>h1 idhan-shu-huan-cun-memoize>函数缓存 memoize/h1>p>函数实现:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>span classline>2/span>br>span classline>3/span>br>span classline>4/span>br>span classline>5/span>br>span classline>6/span>br>span classline>7/span>br>span classline>8/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>const/span> memoize span classfunction>span classparams>pureFunc/span> >/span> {/span>br>span classline> span classkeyword>const/span> cache {};/span>br>span classline> span classkeyword>return/span> span classfunction>span classkeyword>function/span>(span classparams>/span>) /span>{/span>br>span classline> span classkeyword>const/span> argStr span classbuilt_in>JSON/span>.stringify(span classbuilt_in>arguments/span>);/span>br>span classline> cacheargStr cacheargStr || pureFunc.apply(pureFunc, span classbuilt_in>arguments/span>);/span>br>span classline> span classkeyword>return/span> cacheargStr;/span>br>span classline> };/span>br>span classline>};/span>br>/pre>/td>/tr>/table>/figure>p>code>memoize/code> 的功能是对传入函数 code>pureFunc/code> 进行缓存,返回缓存版本的 code>pureFunc/code>。当我们使用参数调用缓存的函数时,缓存的函数会到 code>cache/code> 中查找该参数是否被缓存过,如果有缓存,则不需要再次计算,直接返回已缓存值,否则对本次输入的参数进行计算,缓存计算的结果以备后用,然后将结果返回。/p>p>code>memoize/code> 只有对纯函数的缓存才有意义。因为纯函数是引用透明的,其输出只依赖于输入,并且计算过程不会影响外部环境。/p>p>举一个极端的例子,假如我们有一个随机数字生成函数 code>random()/code>, 如果对其进行了缓存:/p>figure classhighlight js>table>tr>td classgutter>pre>span classline>1/span>br>/pre>/td>td classcode>pre>span classline>span classkeyword>const/span> memoizedRandom memoize(random);/span>br>/pre>/td>/tr>/table>/figure>p>code>memoizedRandom/code> 除了第一次生成一个随机值外,随后的调用都返回第一次缓存的值,这样就失去了 code>random/code> 的意义。再假如,我们对终端字符输入函数 code>getchar()/code> 进行了缓存,每次调用都会是第一次获取的字母。/p>p>code>memoize/code> 内部实现了一个闭包的创建。返回的缓存函数和自由变量 code>cache/code> 共同构成了一个闭包。自由变量 code>cached/code> 用于对已经计算过的数据(参数)的缓存。而闭包本身是由高阶函数和一等函数实现的。/p>p>img src./functions-in-memoize.png altfunctions-in-memoize>/p>h1 idzong-jie>总结/h1>p>本文对函数式编程中的 “函数们” 做了详细解释:纯函数、一等函数、高阶函数,并展示了它们的应用。其中纯函数是函数组合的基础;一等函数是高阶函数的实现基础,一等函数和高阶函数又是闭包的实现基础。/p>p>最后通过函数缓存函数 code>memoize/code> 将纯函数、一等函数、高阶函数和闭包联系了起来,用函数式编程中的 “函数们” (函数式三镖客)的一次 “联合行动” 结束本文。/p>h1 idcan-kao-wen-dang>参考文档/h1>p>a hrefhttp://www.mathsisfun.com/sets/function.html target_blank relnoopener>What is a Function?/a>./p>p>a hrefhttps://en.wikipedia.org/wiki/Functional_programming target_blank relnoopener>Functional Programming/a>./p>p>a hrefhttps://en.wikipedia.org/wiki/Referential_transparency target_blank relnoopener>Referential Transparency/a>./p> /div> footer classpost-footer> div classpost-eof>/div> /footer> /article> nav classpagination> span classpage-number current>1/span>a classpage-number href/page/2/>2/a>span classspace>…/span>a classpage-number href/page/4/>4/a>a classextend next relnext href/page/2/>i classfa fa-angle-right aria-labelNext page>/i>/a> /nav> /div> script> window.addEventListener(tabs:register, () > { let { activeClass } CONFIG.comments; if (CONFIG.comments.storage) { activeClass localStorage.getItem(comments_active) || activeClass; } if (activeClass) { let activeTab document.querySelector(`ahref#comment-${activeClass}`); if (activeTab) { activeTab.click(); } } }); if (CONFIG.comments.storage) { window.addEventListener(tabs:click, event > { if (!event.target.matches(.tabs-comment .tab-content .tab-pane)) return; let commentClass event.target.classList1; localStorage.setItem(comments_active, commentClass); }); }/script> /div> div classtoggle sidebar-toggle> span classtoggle-line toggle-line-first>/span> span classtoggle-line toggle-line-middle>/span> span classtoggle-line toggle-line-last>/span> /div> aside classsidebar> div classsidebar-inner> ul classsidebar-nav motion-element> li classsidebar-nav-toc> Table of Contents /li> li classsidebar-nav-overview> Overview /li> /ul> !--noindex--> div classpost-toc-wrap sidebar-panel> /div> !--/noindex--> div classsite-overview-wrap sidebar-panel> div classsite-author motion-element itempropauthor itemscope itemtypehttp://schema.org/Person> p classsite-author-name itempropname>wangzengdi/p> div classsite-description itempropdescription>FE@Meituan.com/div>/div>div classsite-state-wrap motion-element> nav classsite-state> div classsite-state-item site-state-posts> a href/archives/> span classsite-state-item-count>34/span> span classsite-state-item-name>posts/span> /a> /div> div classsite-state-item site-state-categories> a href/categories/> span classsite-state-item-count>6/span> span classsite-state-item-name>categories/span>/a> /div> div classsite-state-item site-state-tags> span classsite-state-item-count>1/span> span classsite-state-item-name>tags/span> /div> /nav>/div> /div> /div> /aside> div idsidebar-dimmer>/div> /div> /main> footer classfooter> div classfooter-inner> div classcopyright> © span itempropcopyrightYear>2022/span> span classwith-love> i classfa fa-heart>/i> /span> span classauthor itempropcopyrightHolder>wangzengdi/span>/div> div classpowered-by>Powered by a hrefhttps://hexo.io classtheme-link relnoopener target_blank>Hexo/a> & a hrefhttps://muse.theme-next.org classtheme-link relnoopener target_blank>NexT.Muse/a> /div> /div> /footer> /div> script src/lib/anime.min.js>/script> script src/lib/velocity/velocity.min.js>/script> script src/lib/velocity/velocity.ui.min.js>/script>script src/js/utils.js>/script>script src/js/motion.js>/script>script src/js/schemes/muse.js>/script>script src/js/next-boot.js>/script> /body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]