JS如何实现XDM
这篇文章主要介绍“JS如何实现XDM”,在日常操作中,相信很多人在JS如何实现XDM问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JS如何实现XDM”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
偏函数
传参现状
我们经常会写出这样的代码:
function ajax(url,data,callback) { // ..}function getPerson(data,cb) { ajax( "http://some.api/person", data, cb );}
ajax 函数有三个入参,在 getPerson 函数里调用,其中 url 已确定,data 和 cb 两个参数则等待传入。(因为很多时候参数都不是在当前能确定的,需要等待其它函数的操作后确定了再继续传入)
但是我们的原则是:入参最理想的情况下只需一个!
怎样优化,可以实现这一点呢?
我们或许可以在外层再套一个函数来进一步确定传参,比如:
function getCurrentUser(cb) { ...// 通过某些操作拿到 CURRENT_USER_ID getPerson( { user: CURRENT_USER_ID }, cb );}
这样,data 参数也已经确定,cb 参数仍等待传入;函数 getCurrentUser 就只有一个入参了!
数据的传递路线是:
ajax(url,data,callback) => getPerson(data,cb) => getCurrentUser(cb)
这样函数参数个数逐渐减少的过程就是偏应用。
也可以说:getCurrentUser(cb) 是 getOrder(data,cb) 的偏函数,getOrder(data,cb) 是 ajax(url,data,cb) 函数的偏函数。
设想下:
如果一个函数是这样的:
function receiveMultiParam(a,b,c,......,x,y,z){ // ..}
我们难道还要像上面那样手动指定外层函数进行逐层嵌套吗?
显示我们不会这么做!
封装 partial
我们只需要封装一个 partial(..) 函数:
function partial(fn,...presetArgs) { return function partiallyApplied(...laterArgs){ return fn( ...presetArgs, ...laterArgs ); };}
它的基础逻辑是:
var partial = (fn, ...presetArgs) => (...laterArgs) => fn( ...presetArgs, ...laterArgs );
把函数作为入参!还记得我们之前所说:
一个函数如果可以接受或返回一个甚至多个函数,它被叫做高阶函数。
我们借用 partial() 来实现上述举例:
var getPerson = partial( ajax, "http://some.api/person" );var getCurrentUser = partial( getPerson, { user: CURRENT_USER_ID } ); // 版本 1
以下函数内部分析非常重要:
运行机制
getPerson() 的内部运行机制是:
var getPerson = function partiallyApplied(...laterArgs) { return ajax( "http://some.api/person", ...laterArgs );};
getCurrentUser() 的内部运行机制是:
var getCurrentUser = function outerPartiallyApplied(...outerLaterArgs) { var getPerson = function innerPartiallyApplied(...innerLaterArgs){ return ajax( "http://some.api/person", ...innerLaterArgs ); }; return getPerson( { user: CURRENT_USER_ID }, ...outerLaterArgs );}
数据进行了传递:
getCurrentUser(outerLaterArgs) => getPerson(innerLaterArgs) => ajax(...params)
我们通过这样一层额外的函数包装层,实现了更加强大的数据传递,
我们将需要减少参数输入的函数传入 partial()中作为第一个参数,剩下的是 presetArgs,当前已知几个,就可以写几个。还有不确定的入参 laterArgs,可以在确定后继续追加。
像这样进行额外的高阶函数包装层,是函数式编程的精髓所在!
“随着本系列的继续深入,我们将会把许多函数互相包装起来。记住,这就是函数式编程!” —— 《JavaScript 轻量级函数式编程》
实际上,实现 getCurrentUser() 还可以这样写:
// 版本 2var getCurrentUser = partial( ajax, "http://some.api/person", { user: CURRENT_USER_ID });// 内部实现机制var getCurrentUser = function partiallyApplied(...laterArgs) { return ajax( "http://some.api/person", { user: CURRENT_USER_ID }, ...laterArgs );};
但是版本 1 因为重用了已经定义好的函数,所以它在表达上更清晰一些。它被认为更加贴合函数式编程精神!
拓展 partial
我们再看看 partial() 函数还可它用:
function partial(fn,...presetArgs) { return function partiallyApplied(...laterArgs){ return fn( ...presetArgs, ...laterArgs ); };}
比如:将数组 [1,2,3,4,5] 每项都加 3,通常我们会这么做:
function add(x,y) { return x + y[1,2,3,4,5].map( function adder(val){ return add( 3, val );} );// [4,5,6,7,8]
借助 partial():
[1,2,3,4,5].map( partial( add, 3 ) );// [4,5,6,7,8]
add(..) 不能直接传入 map(..) 函数里,通过偏应用进行处理后则能传入;
实际上,partial() 函数还可以有很多变体:
回想我们之前调用 Ajax 函数的方式:ajax( url, data, cb )。如果要偏应用 cb 而稍后再指定 data 和 url 参数,我们应该怎么做呢?
function reverseArgs(fn) { return function argsReversed(...args){ return fn( ...args.reverse() ); };}function partialRight( fn, ...presetArgs ) { return reverseArgs( partial( reverseArgs( fn ), ...presetArgs.reverse() ) );}var cacheResult = partialRight( ajax, function onResult(obj){ cache[obj.id] = obj;});// 处理后:cacheResult( "http://some.api/person", { user: CURRENT_USER_ID } );
柯里化
函数柯里化实际上是一种特殊的偏函数。
我们用 curry(..) 函数来实现此前的 ajax(..) 例子,它会是这样的:
var curriedAjax = curry( ajax );var personFetcher = curriedAjax( "http://some.api/person" );var getCurrentUser = personFetcher( { user: CURRENT_USER_ID } );getCurrentUser( function foundUser(user){ } );
柯里化函数:接收单一实参(实参个数:1)并返回另一个接收下一个实参的函数。
它将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。
实现:
function curry(fn,arity = fn.length) { return (function nextCurried(prevArgs){ return function curried(nextArg){ var args = prevArgs.concat( [nextArg] ); if (args.length >= arity) { return fn( ...args ); } else { return nextCurried( args ); } }; })( [] );}
到此,关于“JS如何实现XDM”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341