在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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。