在JavaScript中实现链式调用的实现
链式调用实现本身比较简单,也有很多文章详细阐述了其实现方式。本文更多从链式调用方法返回值的角度,更进一步来说明如何实现链式调用。
什么是链式调用
链式调用在 JavaScript语言界很常见,如 jQuery、 Promise等,都是使用的链式调用。链式调用可以让我们在进行连续操作时,写出更简洁的代码。
newPromise((resolve,reject)=>{ resolve(); }) .then(()=>{ thrownewError('Somethingfailed'); }) .then(()=>{ console.log('Dothiswhateverhappenedbefore'); }) .catch(()=>{ console.log('Dothat'); })
逐步实现链式调用
假设,我们要实现一个 math模块,使之能够支持链式调用:
constmath=require('math'); consta=math.add(2,4).minus(3).times(2); constb=math.add(2,4).times(3).divide(2); constc={a,b}; console.log(a.times(2)+b+1);//22 console.log(a.times(2)+b+2);//23 console.log(JSON.stringify(c));//{"a":6,"b":9}
基本的链式调用
链式调用通常的实现方式,就是在函数调用结果返回模块本身。那么 math模块的代码大致应该是这样子的:
exportdefault{ add(...args){ //add returnthis; }, minus(...args){ //minus returnthis; }, times(...args){ //times returnthis; }, divide(...args){ //divide returnthis; }, }
方法如何返回值
上述代码实现了链式调用,但是也存在一个问题,就是无法获取计算结果。所以我们需要对模块进行改造,使用一个内部变量来存储计算结果。
exportdefault{ value:NaN, add(...args){ this.value=args.reduce((pv,cv)=>pv+cv,this.value||0); returnthis; }, }
这样,我们在最后一步,通过 .value就可以拿到最终的计算结果了。
问题真的解决了吗
上面我们看似通过一个 value变量解决了存储计算结果的问题,但是发生第二次链式调用时,value的值因为已经有了初始值,我们会得到错误的计算结果!
consta=math.add(5,6).value;//11 constb=math.add(5,7).value;//23而非12
既然是因为 value有了初始值,那么能不能在获取 value的值时重置掉呢?答案是不能,因为我们并不能确定使用者会在什么时候取值。
另外一种思路是在每次链式调用之前生成一个新的实例,这样就可以确保实例之间相互独立了。
constmath=function(){ if(!(thisinstanceofmath))returnnewmath(); }; math.prototype.value=NaN; math.prototype.add=function(...args){ this.value=args.reduce((pv,cv)=>pv+cv,this.value||0); returnthis; }; consta=math().add(5,6).value; constb=math().add(5,7).value;
但是这样也不能彻底解决问题,假设我们如下调用:
constm=math().add(5,6); constc=m.add(5).value;//16 constd=m.add(5).value;//21而非16
所以,最终要解决这个问题,只能每个方法都返回一个新的实例,这样可确保无论怎么调用,相互之间都不会被干扰到。
math.prototype.add=function(...args){ constinstance=math(); instance.value=args.reduce((pv,cv)=>pv+cv,this.value||0); returninstance; };
如何支持不通过 .value对结果进行普通运算
通过改造 valueOf方法或者 Symbol.toPrimitive方法。其中 Symbol.toPrimitive方法优先 valueOf方法被调用,除非是ES环境不支持。
如何支持 JSON.stringify序列化计算结果
通过自定义 toJSON方法。 JSON.stringify将值转换为相应的JSON格式时,如果被转换值有 toJSON方法,则优先使用该方法返回的值。
最终的完整实现代码
classMath{ constructor(value){ lethasInitValue=true; if(value===undefined){ value=NaN; hasInitValue=false; } Object.defineProperties(this,{ value:{ enumerable:true, value:value, }, hasInitValue:{ enumerable:false, value:hasInitValue, }, }); } add(...args){ constinit=this.hasInitValue?this.value:args.shift(); constvalue=args.reduce((pv,cv)=>pv+cv,init); returnnewMath(value); } minus(...args){ constinit=this.hasInitValue?this.value:args.shift(); constvalue=args.reduce((pv,cv)=>pv-cv,init); returnnewMath(value); } times(...args){ constinit=this.hasInitValue?this.value:args.shift(); constvalue=args.reduce((pv,cv)=>pv*cv,init); returnnewMath(value); } divide(...args){ constinit=this.hasInitValue?this.value:args.shift(); constvalue=args.reduce((pv,cv)=>pv/cv,init); returnnewMath(value); } toJSON(){ returnthis.valueOf(); } toString(){ returnString(this.valueOf()); } valueOf(){ returnthis.value; } [Symbol.toPrimitive](hint){ constvalue=this.value; if(hint==='string'){ returnString(value); }else{ returnvalue; } } } exportdefaultnewMath();
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。