问题的出现
之前只是大致了解 ==
和 ===
的区别,之前认为 ==
是比较值的大小,===
不仅需要比较值,还需要比较值的类型。
之前在一次逻辑处理中,做验证数据不为空时写了这样一个逻辑。varietyData数组中元素每个字段都不能为空字符串或空数组。
let varietyData =[{value1:'123',value2:'456',value3:0,value4:[1,2,3]},
{value1:'123',value2:'456',value3:0,value4:[3]},
{value1:'123',value2:'456',value3:0,value4:[]}
];
let obj = {value1:'姓名',value2:'爱好',value3:'成绩',value4:'资源'};
varietyData.every((val,index)=>{
let index = Object.keys(val).every(e=>{
if(val[e] == ''||val[e] == []){
this.$message({type:"warning",message:`第${index+1}行,${obj[e]}不能为空!`});
return false
}else{
return true;
}
})
当value3的值为0时,数据应该是通过的,结果验证逻辑一直不通过,打断点调试后才发现问题出在这里。
console.log(0 == ''); //true
console.log(0 == []); //true
解决办法
之后将 ==
改成 ===
就没问题了。
let varietyData =[{value1:'123',value2:'456',value3:0,value4:[1,2,3]},
{value1:'123',value2:'456',value3:0,value4:[3]},
{value1:'123',value2:'456',value3:0,value4:[]}
];
let obj = {value1:'姓名',value2:'爱好',value3:'成绩',value4:'资源'};
varietyData.every((val,index)=>{
let index = Object.keys(val).every(e=>{
if(val[e] === ''||val[e] === []){
this.$message({type:"warning",message:`第${index+1}行,${obj[e]}不能为空!`});
return false
}else{
return true;
}
})
知识梳理
对于 == 和 === 的比较规则,想趁着这次机会重新梳理下。
全等运算符(===)
官方描述
严格相等运算符(===)也叫全等运算符,其会检查两个操作数是否相等,并且返回一个布尔值结果。
7.2.15Strict Equality Comparison
The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:
- If Type(x) is different from Type(y), return false.
- If Type(x) is Number, then
- If x is NaN, return false.
- If y is NaN, return false.
- If x is the same Number value as y, return true.
- If x is +0 and y is -0, return true.
- If x is -0 and y is +0, return true.
- Return false.
- Return SameValueNonNumber(x, y).
SameValueNonNumber方法描述
7.2.12SameValueNonNumber ( x, y )
The internal comparison abstract operation SameValueNonNumber(x, y), where neither x nor y are Number values, produces true or false. Such a comparison is performed as follows:
- Assert: Type(x) is not Number.
- Assert: Type(x) is the same as Type(y).
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is String, then
- If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true; otherwise, return false.
- If Type(x) is Boolean, then
- If x and y are both true or both false, return true; otherwise, return false.
- If Type(x) is Symbol, then
- If x and y are both the same Symbol value, return true; otherwise, return false.
- If x and y are the same Object value, return true. Otherwise, return false.
比较规则
- 若两个操作数类型不同,返回false。
- 若两个操作数类型为Number,则会有一些特殊情况,
NaN === NaN
结果为flase,+0 === -0
结果为true。 - 若两个操作数都为 null,或者两个操作数都为 undefined,返回 true。
- 若两个操作数为字符串类型,必须拥有相同顺序的相同字符。
- 若两个操作数为Boolean类型,必须同时为 true 或同时为 false。
- 若两个操作数都是对象,只有当它们指向同一个对象时才返回 true。
let obj1 = {a:1};
let obj2 = obj1;
let obj3 = {a:1};
console.log(obj1 === obj2); //true
console.log(obj2 === obj3); //false
相等运算符(==)
官方描述
相等运算符(==||!=),提供非严格相等语义,在ECMAScript中的描述是这样的:
7.2.14 Abstract Equality Comparison
The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:
- If Type(x) is the same as Type(y), then
- Return the result of performing Strict Equality Comparison x === y.
- If x is null and y is undefined, return true.
- If x is undefined and y is null, return true.
- If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
- If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.
- If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
- If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
- If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
- If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.
- Return false.
比较规则
其比较规则如下:
相同类型比较
如果操作数具有相同的类型,则比较结果和**全等运算符(===)**比较结果一样,同上面的全等运算符比较。
不同类型比较
null 和 undefined
如果x,y两个操作数,x为null
,y为undefined
或x为undefined
,y为null
,则返回true。
Number 和 String
如果x,y两个操作数,x为Number类型,y为String类型或y为Number类型,x为String类型,会先将String类型数据转化为Number类型,再做比较。下面两者是等价的。
需要注意的是:Number()
将字符串转化为数字时,空字符串会转化为0,即Number('')
的值为0,也就是说 0 == ''
结果为 true。
带非数字字符的字符串会转化为NaN
Boolean 和其他基础类型
如果x,y两个操作数,x为Boolean类型,y为其他类型或y为Boolean类型,x为其他类型,则通过Number将Boolean类型转化为数字,再进行比较。其中true
转化为1,false
转化为0。
如果另一个操作数是字符串类型,比较时会将字符串转化为数字,再进行比较。于是我们就会发现一种比较有趣的情况, false == '0'
和 false == ''
都为 true,但''=='0'
为false。这是因为同类型和不同类型的比较规则是不同的,想通过他们之间的连等来判断是不可取的。
Symbol 和 其他基础类型
Symbol的值都是唯一的,所以它只在与自身比较时才为true,与其他值比较时都为false。
BigInt 和 其他基础类型
BigInt和Number类型相似,进行比较时会先将其他类型转化为Number类型,再进行比较。
Object 和 其他基础类型
如果x,y两个操作数,x为Object,y为Symbol,Number,String类型,则会先将Object对象类型数据转化为基本类型,按此顺序使用对象的Symbol.toPrimitive,valueOf(),toString()方法将对象转化为基本类型(这个基本类型转换与相加中使用的转化相同),再进行比较。
- Symbol.toPrimitive: 是内置的symbol属性,其指定了一种接收首选类型并返回对象原始值的方法,它被所有的强制类型转化算法优先调用。
- valueOf:Object实例的
valueOf()
方法,将this
值转化为对象。该方法被派生类重写,以实现自定义类型转换的逻辑。 - toString:方法返回一个表示该对象的字符串。旨在重写(自定义)派生类对象的类型转换逻辑。
-
总结一下,Object类型数据与基本类型数据进行比较时,如果Object类型数据有Symbol.toPrimitive方法,则会优先调用此方法,将数据转化为基本类型数据。没有则会调用valueOf方法返回一个数据,若返回的数据是对象则再调用toString方法将其转化为基本类型。
我们通过下面的例子来验证下它的转换过程
在上面的例子中,valueOf
和 toString
都是原型对象(Object.prototype)上的方法,因此可以直接调用,但Symbol.toPrimitive需要我们自己定义。
let obj = {
[Symbol.toPrimitive](hint){
if(hint == 'default'){
return 42
}else{
return 43;
}
}
}
console.log(obj == 42); //true
上面例子比较结果为true,其中Symbol.toPrimitive
方法接收一个参数作为转换原始值的类型,这个参数的默认值为default
。我们在这个对象上定义了Symbol.toPrimitive
方法,再将对象转化为原始值时,会首先调用这个方法,没有查找到这个方法才会使用valueOf
,toString
方法进行转换。
想了解更多Symbol.toPrimitive相关知识,请移步:
developer.mozilla.org/zh-CN/docs/…
验证了Object是如何转换为基础数据类型的,接下来验证下特殊对象(数组,函数)是如何转换的?
1,空数组的转换
空数组经过转化后的基本类型数据为空字符串,因此相等运算[] == ''
和[] == 0
的结果为true。
2,非空数组的转换
通过上面例子可以看出非空数组转化为基本数据类型时,会先将数组元素转化为字符串,然后再用逗号拼接起来。
3,函数的转换
通过上面的例子可以看出函数转化为基本数据类型时,会将函数代码转化为字符串格式。
总结
1,如果两个操作数类型相同,则返回 x === y的结果
2,如果两个操作数类型不相同,且均为基础数据类型,则有以下几种情况
(1)类型为null或undefined时,除与自身比较为true外,还有null == undefined
为true,其余情况为false。
(2)类型为Symbol时,只与自身比较为true,其余情况为false。
(3)类型为Boolean时,会将Boolean类型数据转化为Number类型数据,true为1,false为0,再进行比较
(4)其余的情况比较时,先通过Number()方法转化为数字后,再进行比较。
3,如果某个操作数为对象,则通过Symbol.toPrimitive,valueOf,toString将其转化为基础数据类型后,再进行比较。