当前位置: 首页 > Web前端 > HTML

精读《pipe operator for JavaScript》

时间:2023-03-28 18:54:18 HTML

PipeOperator(|>)forJavaScript提案为js添加Pipe语法,本次结合ApipeoperatorforJavaScript:introductionandusecases一文详细了解该提案。概述Pipe语法可以按顺序展平函数调用。如下面这个函数,有三层嵌套,但是我们在解释的时候需要从里往外读,因为调用顺序是从里往外的:consty=h(g(f(x)))pipe即可将其转换为正常顺序:consty=x|>f(%)|>g(%)|>h(%)管道语法有两种风格,来自Microsoft的F#)和Facebook的Hack)。之所以引入这两个,是因为js的proposal首先决定“借鉴”哪个样式。js提案最终采用了Hack风格,所以我们最好把F#和Hack风格都了解一下,比较一下他们的优劣就知道为什么了。HackPipe语法Hack语法比较冗余,用%把结果传到Pipe:'123.45'|>Number(%)这个%可以用在任何地方,基本原生js语法支持:value|>someFunction(1,%,3)//函数调用值|>%.someMethod()//方法调用值|>%+1//运算符值|>[%,'b','c']//数组字面值|>{someProp:%}//objectliteralvalue|>await%//waitingaPromisevalue|>(yield%)//yieldageneratorvalueF#管道语法F#语法相对简单,默认不使用额外的符号:'123.45'|>Number但需要显式声明参数时,为了解决前面Pipe结果的符号从哪里来的问题,写的比较复杂:2|>$=>add2(1,$)await关键字-Hack优F#awaityield时需要特殊的语法支持,Hack自然可以使用js内置关键字。//Hackvalue|>await%//F#value|>awaitF#代码看起来很精简,但实际上付出了高昂的代价——await是一个只存在于Pipe语法中的关键字,不是普通的await关键字。如果不将其视为关键字,则执行逻辑变为await(value)而不是awaitvalue。解构——F#之所以优秀,是因为F#繁琐的变量声明,但是却可以轻松应对解构场景://F#value|>({a,b})=>someFunction(a,b)//Hackvalue|>someFunction(%.a,%.b)Hack也不是没有解构的方法,只是比较繁琐。使用立即调用的函数表达式IIFE:value|>(({a,b})=>someFunction(a,b))(%)或使用do关键字:value|>do{const{a,b}=%;someFunction(a,b)}但是Hack是一个光荣的失败,因为解决方案使用了js提供的原生语法,所以表现出与js现有生态更强的亲和力,F#之所以能优雅的解决,都感谢对于自创的语法,这些语法虽然甜美,但是切断了js生态,这也是F#likeproposal被放弃的重要原因之一。虽然潜在的改进方案选择了Hack风格,但是F#和Hack各有优缺点,所以罗列了一些优化方案。使用部分应用程序语法提议来降低F#参数传递的复杂性。F#被诟病的一个原因是传递参数不像Hack://Hack2|>add2(1,%)//F#2|>$=>add2(1,$)那样简单而是使用提案PartialApplicationstage1中的语法可以很好的解决问题。这是一个小插曲。Node.js本身不支持柯里化,但部分应用程序语法提案解决了这个问题。语法如下:constadd=(x,y)=>x+y;constaddOne=add~(1,?);添加一个(2);//3是使用fn~(?,arg)的语法来柯里化任何函数。这个特性完美的解决了F#复杂的传参问题,因为要求F#的每一个Pipe都是一个函数,我们可以把要传参的地方记录为?,这样返回值还是一个函数,完全符合F#的语法://F#2|>add~(1,?)上面的例子可以反汇编看:constaddOne=add~(1,?)2|>addOne的思路很好,但是必须先实现部分应用程序语法。结合F#和Hack语法简单情况下使用F#,需要使用%传递参数,使用Hack语法。混合两者是:constresultArray=inputArray|>filter(%,str=>str.length>=0)//Hack|>map(%,str=>'['+str+']')//Hack|>console.log//F#但是这个提议被放弃了。创建一个新的操作符如果你使用|>用于Hack语法,|>>用于F#语法会怎么样?constresultArray=inputArray|>filter(%,str=>str.length>=0)//Hack|>map(%,str=>'['+str+']')//Hack|>>console.log//F#看起来也不错,但这个功能还没有被提出来。如何用现有的语法模拟管道即使JavaScript提案没有管道运算符(|>),也可以使用js现有的语法来模拟管道的效果。以下是几种解决方法。Function.pipe()使用自定义函数构造管道方法。语法类似于F#:constresultSet=Function.pipe(inputSet,$=>filter($,x=>x>=0)$=>map($,x=>x*2)$=>newSet($))缺点是不支持await,有额外的函数调用。说白了,使用中间变量就是把Pipe过程拆开,一步一步写:constfiltered=filter(inputSet,x=>x>=0)constmapped=map(filtered,x=>x*2)constresultSet=newSet(mapped)没什么大问题,但是比较冗余。本来可以一行解决的问题变成了三行,声明了三个中间变量。转换重用变量,使中间变量重用:let$=inputSet$=filter($,x=>x>=0)$=map($,x=>x*2)constresultSet=newSet($)这种方式可能会造成变量污染,可以使用IIFE来解决。精读管道运算符,语义价值非常明显,甚至可以改变编程的思维方式。在串行处理数据的时候很重要,所以命令行场景很常见,比如:cat"somefile.txt"|echo因为命令行是一个典型的输入输出场景,而且大部分都是单输入单输出。常见的代码场景也需要这个特性,尤其是处理数据的时候。大多数具有抽象思维的代码已经实现了各种类型的管道抽象,例如:constnewValue=pipe(value,doSomething1,doSomething2,doSomething3)如果PipeOperator(|>)forJavaScript提案通过,我们不需要任何库来实现pipeaction,可以直接写成:constnewValue=value|>doSomething1(%)|>doSomething2(%)|>doSomething3(%)等价于:constnewValue=doSomething3(doSomething2(doSomething1(value)))显然,使用管道特性来编写处理流程更加直观,执行逻辑与读取逻辑一致。实现管道函数即使JavaScript提案没有管道运算符(|>),我们也可以在一行中实现管道函数:constpipe=(...args)=>args.reduce((acc,el)=>el(acc))但是不可能实现Hack的参数样式,顶多能实现F#的参数样式。js中实现管道语法的注意事项从提案记录来看,F#失败的原因有以下三个:内存性能问题。等待特殊语法。分裂js生态。其中,拆分js生态意味着由于F#语法的特殊性,如果过多的库按照其语法实现功能,可能无法被非Pipe语法场景复用。甚至还有一些成员反对Tacitprogramming和curryingproposalPartialApplicationSyntax,这会使js支持的编程风格与现在的编程风格相差太大。看来鄙视链顶端的编程风格在js中是否支持,不是能不能的问题,而是你要不要的问题。管道语法的缺点下面是正常的setState语法:setState(state=>({...state,value:123}))如果改成immer,写法如下:setState(produce(draft=>draft.value=123))得益于ts类型的自动推导,innerproducer已经知道value是string类型。这时候如果输入的是输入字符串,就会报错,如果是在另一个上下文的setState中,类型也会随着上下文发生变化。种类。但是如果是pipe方式写的:produce(draft=>draft.value=123)|>setState,因为首先考虑的是如何修改数据,后续的pipe过程此时未知,所以类型草案的无法确定。所以管道语法只适用于固定类型的数据处理流程。总结Pipe直译为管道。潜在的意思是“数据像管道一样被处理”。也可以形象地理解,每个函数都是不同的流水线。显然,下一个流水线必须处理上一个流水线的数据,并将结果输出到下一个流水线。管道作为输入。合适的管道数量和体积决定了一条生产线是否高效。管道种类太多,会使线路散乱。管道太少会使线路笨重且难以扩展。这是工作中最大的考验。讨论地址为:Jingdu《pipe operator for JavaScript》·Issue#395·dt-fe/weekly想参与讨论的请戳这里,每周都有新话题,周末或周一发布。前端精读——帮你过滤靠谱的内容。关注前端精读微信公众号版权声明:免费转载-非商业-非衍生保留属性(CreativeCommons3.0License)