比较运算符
相等运算符和全等运算符的区别?
-
等于运算符在进行比较的时候,如果遇到数据类型不同,会先进行数据类型的转换,再比较值。
在对于两个不同类型的数值,内部遵循的转换规则如下:
-
两个数都是基本类型,全部转化为Number类型进行比较
-
一个值是基本数据类型,一个是引用数据类型,那么就会调用该引用数据类型的valueOf(),用得到的基本类型值按照前面的规则进行比较。
比如:
let obj = { valueOf(){ return 1; } }; let obj1 = {}; obj == 1; //true obj1 == 1; //falsevalueOf()是对象原型上的方法,它返回的是该对象的原始值。
但是javaScript上的许多内置对象都重写了这个方法,导致针对不同的内置对象,调用的valueOf()返回的值都是不一样的。
当然我们也可以通过Object.prototype.valueOf = ()=>{...}来重写它。
-
如果两个都是引用类型就判断他们的地址指针是否相同,若相同再做进一步判断属性值是否相同。
-
null和undefined是相等的
-
NaN不等于自身
也就说NaN在等于运算符之下,不等于任何值,包括它自身。所以我们判断一个值是不是NaN不能用==运算符,一般使用的是isNaN()函数。
-
-
全等运算符就不会进行数值类型的转换,如果数值类型不一样直接返回false,如果数值类型还要继续判断他们的值是否相等,只有值相等了才会返回true。
null 和 undefined比较,相等操作符(==)为true,全等为false;
综上所述,相等操作符会进行数值类型的转换,所以有的时候会让我们得到预料之外的结果,因此除了在比较null和undefined的情况下可以使用相等操作符,其他情况建议使用全等操作符。
算数运算符
前自增和后自增的区别?
- 相同点:无论是a++,还是++a,都会立即使得原来的a+1
- 不同点:++a返回的是a+1(新值),但是a++返回的是a(旧值)
👏PS:自减也是这个道理;
逻辑运算符
逻辑运算符最为我们常用的就是它的“短路赋值”机制,基于这个机制我们可以使用逻辑运算符作为条件判断的依据,我们还可以对一些值设置默认值。(解构赋值也可以设置默认值,我们可以根据语义的不同选择不一样的方法去设置默认值)
&&:遇到false就短路,直接返回false所在的原值
||:遇到true就短路,直接返回true所在的原值
链判断运算符(可选链运算符)
在编程实务中,我们会遇到这样的需求:定义一个变量,如果某个对象存在某个值就将该值赋值给某个变量,否则就赋值默认值。具体如下
const a = obj.name || 'default';
但是这种需求存在两个最为常见的痛点:
-
如果上述对象obj存在多层嵌套,比如我们要求的时候obj.father.son.name,那我们在取值的时候是需要一层层判断它的上层属性是否存在的。
let obj = { father: { son: { name: 'kkk' } } } const name = obj.father.son.name || 'default'; //或者 const name = obj.father.son.name ? obj.father.son.name : 'default' /* 但是像上面写是很不安全的,因为如果name的某个上层对象不存在,那么就会报错, 所以我们一般会像下面这样写 */ const name = obj && obj.father && obj.father.son && obj.father.son.name || 'default'; //但是这样的面临着一个问题,就是随着嵌套层数的增加,程序执行判断的次序也在不断增加为了解决这个问题,ES6引入了新的运算符: ?. (链判断运算符或者说是可选链运算符)
这个运算符它是通过链式调用的时候进行判断,如果判断出来null或者undefined,就直接中断并返回undefined。那么上述的赋值就可以改成:
const name = obj?.father?.son?.name || 'default'关于链判断运算符,我们还有一个十分常见的用法:用链判断运算符来尝试调用一个可能不存在的方法, 这是很有帮助的,因为当我们调用的方法真的不存在它也只是会返回undefined,而不是抛出异常。比如下述的例子。
关于链判断表达式还需要注意以下几点:
-
链判断运算符不能用于赋值
obj.father?.son?.name = 'what'; //🙅♀️上述语法是不允许的,会报错 -
链判断运算符可以访问数组
let arr = [1,2,3]; arr?.[2] -
链判断运算符可以和表达式结合
let obj = null; let a = 0; obj?.[a++] //这里的a是1
-
Null判断运算符
-
链判断运算符解决的问题是针对链式调用中,检测上层对象是否存在,防止对象不存在报错。但是即使使用了链判断也不能很好解决我们想要为值为null或者undefined的变量赋默认值。如下例:
let obj = { num: { x: 0 } } const y = obj?.num?.x || 90; console.log(y); //输入90 /* 这里我们本来的需求是如果obj.num.x为null或者undefined,我们就为将y赋值90, 但是从上述结果可以看到,因为obj?.num?.x返回的是0, 那么在进行||逻辑运算符的时候,会将其转换为fasle,所以导致赋默认值 这显然不符合需求 */开发者的原意是,只要属性的值为null或undefined,默认值就会生效,但是属性的值如果为空字符串或false或0,默认值也会生效。
为了避免这种情况,ES6引入了一个新的 Null 判断运算符 ?? 。它的行为类似 || ,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。
let obj = { num: { x: 0 } } const y = obj?.num?.x ?? 90; console.log(y); //0Null判断运算符经常和链判断运算符一起使用,这样就可以实现为一个值为null或者undefined的变量赋值默认值了。
关于优先级:
因为null判断运算符本质上也是逻辑运算符,所以和&& || 一起使用的时候就会面临一个优先级的问题,那么ES6也是规定这几个一起使用的时候一定要加括号,否则报错。
x ?? y || z && t //🙅♀️上述报错 (x ?? y ) || (z && t)