You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
consttagMap='html,body,base,head,link,meta,style,title,'+'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,'+'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,'+'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,'+'s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,'+'embed,object,param,source,canvas,script,noscript,del,ins,'+'caption,col,colgroup,table,thead,tbody,td,th,tr,'+'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,'+'output,progress,select,textarea,'+'details,dialog,menu,menuitem,summary,'+'content,element,shadow,template,blockquote,iframe,tfoot'// 查询是否是 HTML 标签constisHTMLTag=isInMap('div',tagMap)
在多次调用的情况下,每一次都需要 for 循环遍历,会增大开销。而 Vue 中为了减少开销,是这么做的:
函数式编程
本篇由一个常见的面试题引入,尝试了解下函数式编程在前端的应用
add 函数
实现
add
函数,支持如下调用和返回:实现思路
add(1)
的结果需要跟2
一起作为add(2)
的输入,依此类推...利用闭包的知识,容易想到最笨的实现:
显然这是不现实的,因为随着累加次数增大,无法维护
继续观察,容易想到:对重复性的操作(累加)可以使用递归(一个函数通过名称调用自己)来实现,而递归则需要终止条件,结合题目,先试一下把『不再有参数输入』作为终止条件:
那么
y === undefined
怎么成立呢?自然是add(1)(2)...()
,最后必须进行一次不传参数的调用,这貌似不符合题目要求如果不判断『是否有参数输入』呢?代码如下:
执行
add(1)(2)(3)
,显然它的输出结果是一个函数,而且没有把计算结果返回,这次递归的终止条件是『不再有函数调用』,打印可发现sum
是已经计算完毕的,如何将sum
与返回的函数绑定?函数是需要调用的,既然不考虑显式调用,那么将值储存起来,需要的时候,进行隐式调用。隐式调用让人联想到隐式转换:
扩展
对题目要求进行扩展
这时就需要从
arguments
上考虑:柯里化
容易写出求和函数如下,它有『多个参数』
对比上面的题目,虽然结果一样,但是
add
函数拥有『较少参数』核心的点在于:参数数量的变化。变化不意味着丢失,所以每次调用
curried
函数时,需要进行『参数收集』,直到收集了非柯里化函数的所有参数根据定义,首先尝试完成
sum(a, b, c)
到add(a)(b)(c)
的转换:同样,基于闭包和递归的知识进行抽象。一个通用的用于柯里化转换的函数实现如下:
为了方便理解,它的流程图:
注意:我们使用了
fn.length
取到了函数参数(形参)的个数,来终止递归。因此,在进行柯里化时,我们必须要能确定待柯里化函数参数的个数!使用函数参数扩展和箭头函数的 ES6 版本的实现
柯里化的应用
固化参数
Vue 的 patch 方法
Vue 源码在 src/core/vdom/patch.js 中,定义了一个
createPatchFunction
方法:这样在将数据渲染到视图的
__patch__
过程中,就可以直接调用最终导出的patch
方法,而不用每次都重复传递nodeOps
和modules
参数:提前确认
map 映射
查找一个值是否在一个较长的集合中,或许可以这么编写:
试想:对于
map
很长的情况,每次调用都需要传递map
参数。还是以 Vue 为例,判断字符串是否为 HTML 标签,它的map
是这样的:在多次调用的情况下,每一次都需要 for 循环遍历,会增大开销。而 Vue 中为了减少开销,是这么做的:
ES5 bind 的实现
利用函数柯里化可以实现 ES5 的
bind
:函数式编程
函数式编程(Functional Programming)区别于面向对象编程(Object-Oriented Programming)
面向对象编程注重过程和抽象,本质是利用抽象(值或方法)来访问数据(对象)
函数式编程,顾名思义,需要正确理解函数并运用函数。在 ES 中:
arguments
是一个类数组对象;在写 JS 代码时,可以适当利用函数式编程的一些思想
组合函数技巧
以 Node.js 库 koa 和 redux 为例
koa 中间件执行机制的实现
koa 应用中,以下示例代码的输出和执行顺序图示:
执行顺序图示:
koa 通过 koa-compose来实现这一机制:
koa 使用
compose
:结合示例代码分析一下函数的调用顺序:
redux compose
同样的,在 redux 中,也有组合中间件的 compose 函数实现:
不可变性和无副作用的思想
这两者更偏向于理论或者说约定的原则。比如 Vue 的父子组件规定了单向数据流;在 React 中,强调 props 的只读性
函数式编程缺点
参考资料
The text was updated successfully, but these errors were encountered: