众所周知,在JavaScript中,逻辑运算符的行为与其他的语言有那么点不同。
在诸如Java、C++等语言中,逻辑运算符的行为大都一样,都会返回一个布尔类型的结果,要么是true要么是false,可是在JS里就不同了;在JS中,逻辑运算符竟然会返回参与其运算的表达式的值本身。
用几个例子来说明一下:
let t1 = 0 && 1;
let t2 = false || '';
let t3 = {} || false;
let t4 = true && null;
如上代码所示,按照其他语言的逻辑来看,这几个变量的值应该是很显然的,t1、t2、t4都应该是false,t3是true,然而,在JS里,结果却不是这样:
console.log(t1);//0
console.log(t2);//''
console.log(t3);//{}
console.log(t4);//null
在JS中,这四个变量按照逻辑运算符的运算顺序得到了其中表达式本身的值:对于t1,&&运算符在遇到0这个表达式时已经得到false的结果,故不会再运算1,于是返回了0的值;对于t2,||运算符首先遇到false,其继续运算第二个表达式的值,接着遇到了一个空字符串,其替换为布尔值时也为false,然而,整个表达式没有返回false,而是返回了最后运算的表达式,也就是空字符串;t3、t4同理,t3中空对象对应的布尔值是true,所以||运算符直接返回了{};t4则得到了null值。
在JS的日常运用当中,其实这种机制也不会引发多少问题,因为在JS中,除了undefined、null、0,false和空字符串以外,其他的任何值取布尔值都是true,所以在一些if、else if的判断中,逻辑运算符的这种机制也不会引发什么问题。不过,当遇到在某些场景中,确实明确的需要一个布尔类型的值,而不能是其他类型的值时,这种机制就会引发一些奇怪的问题了,而我今天就遇到了这么一个问题。
这是我在坐一个小项目时遇到的问题,简单地说就是我在项目中使用着Vue框架,然后在某个地方需要让按钮组件根据某种条件决定其disabled这个属性的值(在这disabled被设定为一个要求为Boolean类型的prop),简单缩略后的代码如下:
<button :disabled="disabled"></button>
//disabled = istooLong(val,num)
istooLong (value, length) {
return value && value.length > length;
}
大概就是根据某个input的输入字段值的长度,通过istooLong改变disabled的值然后通过vue绑定到button的disabled属性上。然后,就出问题了,在该输入字段一开始什么都没输入的时候,该字段所对应的变量的值是undefined,根据前面所描述的,istooLong将返回undefined, undefined的值为false,所以button的disabled的属性值将为false;之后,当将该input的输入字段清空后,就出问题了。在这里,该字段对应的值会变为一个空字符串,其布尔值也为false,照理说button是不会变为disabled的,然而button却变成了disabled的(这里的需求是该字段应该不影响按钮的可点击性);我的猜测是原因在于这里istooLong函数返回了一个空字符串值,然后将该值绑定给vue的组件属性时,效果相当于写法上变成了:
<button disabled></button>
因此我想到的解决方法就是在这里令istooLong函数明确返回一个布尔类型的值,改写方法如下:
istooLong (value, length) {
return value instanceof String && value.length > length;
}
&&运算符两边的表达式都会返回true或false,就解决了这个由逻辑运算符特性引起的一个古怪小问题。
严格意义上说这也不算是逻辑运算符的问题吧,我想主要问题还是在于Vue的传值以后的绑定机制,从这个问题可看出Vue应该不会将传给props的变量值进行隐式的数据类型转换,否则如果能将空字符串正确的转换为false赋值给disabled变量的话,这个问题应该不存在才对,以后能空出时间来研究Vue的源码之后,如果能完全了解该问题的缘由,我会再返回来修改该博客的描述的。
ps:其实在今天做项目的途中还巩固了一个关于async函数的知识点,不过这个知识点很小感觉没必要再单独写一篇博客了,就写在这底下一起记录下来吧。
这个问题出在我在使用IView这个UI库中的Upload文件上传组件的时候。由于我想自己定义文件上传的具体逻辑代码,因此我在给Upload组件的before-upload属性提供的方法中返回了false值来打断该组件默认的Ajax上传流程。然而,在提供给该属性的方法中,由于存在异步操作,我习惯性的写成了下面这种写法:
async onBeforeQrcode (file) {
await this.uploadQRcode(file);
return false;
}
然后就造成了每次上传文件的时候,我在浏览器的调试窗口中,总能看到我发出了两个post请求,一个来自我自己定义的上传代码,一个来自于Upload组件的默认上传代码。问题出现了,为什么明明在函数中返回了false值,却没能打断组件的上传呢?
原因在于async函数的返回值,async函数的返回值是一个Promise对象,在遇到await关键字时,async函数就会先返回一个Promise了,而之后的return false语句事实上没有什么意义(其实想一想,async函数是一个异步函数,那么异步的返回一个值当然没有意义咯,以前在学异步的时候听过这么一句话:“no return, use callback”,放在这里也一样有它的道理2333),所以在Upload组件中是接收不到这个false值的,只能接收到async函数返回的Promise对象。所以如果要打断默认的上传流程的话,就不能定义成asnyc函数了:
onBeforeQrcode (file) {
this.uploadQRcode(file);
return false;
}