掌握 JavaScript 的核心运算符:短路运算符 || 和 &&,可选链 ?.,空值合并 ??,算数、比较、相等及赋值运算符,助你高效编写简洁、可读的代码!
||和&&与短路运算符
假值:
- false
- null
- undefined
- NaN
- 0(包括+0,-0,0n)
''空字符串
||(一真则真)
|| 先依次进行布尔判定,返回第一个真值,而且不会再进行后面的比较与计算;如果都是假值则返回最后一个值
应用:默认值设置 ||
let value = possiblyUndefinedVariable || 'default value';
// 如果possiblyUndefinedVariable的值为真,那就把possiblyUndefinedVariable的值赋给value
// 但如果possiblyUndefinedVariable的值为假,那就把默认值'default value'赋给value
&&(一假则假)
先依次进行布尔判定,返回第一个假值,而且不会再进行后面的比较与计算;如果都是真值则返回最后一个值
应用:&&短路运算代替 if
if (condition) {
executeFunction();
}
//简化为
condition && executeFunction();
// 如果condition为假则直接返回condition不会执行executeFunction()
// 如果condition为真才会执行executeFunction()
应用:安全的对象属性访问
使用 && 运算符可以避免在访问嵌套属性时由于中间的对象不存在而抛出的 TypeError。 这就是所谓的"安全的对象属性访问"。(访问不到也不会报错)
let value = obj && obj.prop && obj.prop.subProp;
// 首先检查 obj 是否存在(即 obj 不是 undefined 或 null)。
// 如果存在,那么就继续检查 obj.prop;
// 如果 obj 不存在,那么 obj && obj.prop && obj.prop.subProp 的结果就是 undefined,
// 并且由于 && 的短路特性,不会尝试访问 obj.prop。
// 同样,obj.prop && obj.prop.subProp 首先检查 obj.prop 是否存在。
// 如果 obj.prop 存在,那么就返回 obj.prop.subProp;如果 obj.prop 不存在,
// 那么就返回 undefined,并且不会尝试访问 obj.prop.subProp。
?.Optional Chaining (可选链)
对于上面“安全的对象属性访问”的例子,?.提供了一种更简洁的方式来安全地访问深层次的对象属性。
let value = obj?.prop?.subProp || 'default value';
// 如果 obj 或 obj.prop 为 undefined 或 null,那么整个表达式的值就会是 'default value'。
// 只有 obj 或 obj.prop都为真的时候,才会返回obj.prop.subProp。
??空值合并运算符
??类似于 ||运算符,但只在左边的操作数为 null 或 undefined 时,才会返回右边的操作数。
它比||更加细化,||只能看真假,而??具体到null 或 undefined。
let value = possiblyZeroOrEmptyString ?? 'default value';
// 只有当 possiblyZeroOrEmptyString 为 null 或 undefined 时,value 才会被设置为 'default value'。
// 如果 possiblyZeroOrEmptyString 为 0 或空字符串,value 的值就会是 0 或空字符串。
// 如果使用||,那么如果 possiblyZeroOrEmptyString 为 0 或空字符串,value的值也会是undefined。
算数运算符
+, -, *, /, %, **。
数字运算
纯数字之间正常数学运算,但要注意number和BigInt之间不能混合运算,而且BigInt的运算与number相同,但除法会省略小数部分。
console.log(3 + 7); //10
console.log(3 - 7); //-4
console.log(3 * 7); //21
console.log(3 / 7); //0.42857142857142855
console.log(3 % 7); //3
console.log(2 ** 2); //4
console.log(4 + 5n); //报错:Cannot mix BigInt and other types, use explicit conversions
console.log(3n + 7n); //10n
console.log(3n - 7n); //-4n
console.log(3n * 7n); //21n
console.log(7n / 3n); //2n
console.log(3n % 7n); //3n
console.log(2n ** 2n); //4n
特殊字面量参与运算
NaN
与任何值进行算术运算(除了字符串连接),结果都是 NaN 。 NaN 与任何值(包括 NaN 自身)进行比较的结果都是 false 。
console.log(NaN + 3); //NaN
console.log(NaN - 3); //NaN
console.log(NaN * 3); //NaN
console.log(NaN / 3); //NaN
console.log(NaN % 3); //NaN
console.log(NaN ** 3); //NaN
console.log(NaN + ''); //'NaN'
Infinity与-Infinity
当超过 JavaScript 数字的最大值时,会产生 Infinity,当超过最小值时,会产生 -Infinity。 它们参与的运算,视情况而定。
console.log(Infinity + 3); //Infinity
console.log(Infinity - 3); //Infinity
console.log(Infinity * 3); //Infinity
console.log(Infinity / 3); //Infinity
console.log(Infinity % 3); //NaN
//
onsole.log(-Infinity + 3); //-Infinity
console.log(-Infinity - 3); //-Infinity
console.log(-Infinity * 3); //-Infinity
console.log(-Infinity / 3); //-Infinity
console.log(-Infinity % 3); // NaN
//
console.log(3 / Infinity); // 0
console.log(3 % Infinity); // 3
console.log(3 / -Infinity); // -0
console.log(3 % -Infinity); // 3
//
console.log(Infinity + Infinity); //Infinity
console.log(-Infinity - Infinity) //-Infinity
console.log(Infinity - Infinity); //NaN
console.log(-Infinity - Infinity) //NaN
console.log(Infinity * Infinity); //Infinity
console.log(Infinity / Infinity); //NaN
console.log(Infinity % Infinity); //NaN
undefined
与任何值进行算术运算时,结果都是 NaN。除了字符串连接外
在 JavaScript 中,undefined 隐式转换为以下值:
- 当
undefined参与数学运算时,会被转换为NaN(非数字) 。 - 当
undefined与字符串拼接时,会被转换为字符串"undefined"。 - 当
undefined与布尔运算时,会被转换为false。 - 当
undefined与对象比较时,通常会被转换为null。
console.log(undefined + 3); //NaN
console.log(undefined - 3); //NaN
console.log(undefined * 3); //NaN
console.log(undefined / 3); //NaN
console.log(undefined % 3); //NaN
console.log(undefined ** 3); //NaN
console.log(undefined + undefined); //NaN
console.log(undefined - undefined); //NaN
console.log(undefined * undefined); //NaN
console.log(undefined / undefined); //NaN
console.log(undefined % undefined); //NaN
console.log(undefined ** undefined); //NaN
console.log(undefined + null); //NaN
console.log(undefined - null); //NaN
console.log(undefined * null); //NaN
console.log(undefined / null); //NaN
console.log(undefined % null); //NaN
console.log(undefined ** null); //1 在JS中任何数的0次幂都为1,包括0,null,NaN,undefined,Infinity,-Infinity
console.log(undefined + ''); //'undefined'
null
与任何值进行算术运算时, null 会被转换为数字 0。
console.log(null + 3); //3
console.log(null - 3); //-3
console.log(null * 3); //0
console.log(null / 3); //0
console.log(null % 3); //0
console.log(null ** 3); //0
console.log(null + null); //0
console.log(null - null); //0
console.log(null * null); //0
console.log(null / null); //NaN
console.log(null % null); //NaN
console.log(null ** null); //1 在JS中任何数的0次幂都为1,包括0,null,NaN,undefined,Infinity,-Infinity
其他类型的数据参与数学运算
加法
如果有一个操作数是NaN,那么结果为NaN。除了字符串会变成字符串(只有加法会这样——拼接)外,其他都会先进行隐式类型传唤再进行计算。
console.log('10' + 1); // '101'
console.log('' + 1); // '1'
console.log(1 + 2 + '3'); // '33'
console.log('' + true) // 'true'
console.log('' + NaN) // 'NaN'
console.log('' + undefined) // 'undefined'
console.log('' + null) // 'null'
console.log('' + Infinity) // 'Infinity'
console.log('' + -Infinity) // '-Infinity'
console.log('' + [1,2,3]) // '1,2,3'
console.log('' + {a:1,b:2}) // '[object Object]'
console.log(1 + NaN); // NaN (Number)
console.log(1 + true); // 2 (Number)
console.log(1 + false); // 1 (Number)
console.log(1 + null); // 1 (Number)
console.log(1 + undefined); // NaN (Number)
console.log(1 + []); // '1' 数组会先转成用,连接的字符串再进行拼接
console.log([] + 1); // '1'
console.log([] + []); // ''
console.log(1 + [1,2,3]); // '11,2,3'
console.log(1 + {}); // '1[object Object]' 对象会先转成用字符串'[object Object]'再进行拼接
console.log({} + 1); // '[object Object]1'
console.log({} + {}); // '[object Object][object Object]'
console.log(1 + {name:'li'}); // '1[object Object]'
console.log([] + {}); // '[object Object]'
console.log({} + []); // '[object Object]'
console.log([1,2,3] + {name:'li'}); // '1,2,3[object Object]'
console.log(new Date() + 1); // 'Fri Jul 09 2021 10:36:29 GMT+0800 (中国标准时间)1'
// JavaScript 会尝试将日期对象转换为字符串。
// 对象的 toString() 方法会被调用,返回日期对象的字符串表示形式,例如 "Fri Jul 09 2021 10:36:29 GMT+0800 (中国标准时间)"。
// 然后,JavaScript 将这个字符串与数字进行字符串拼接。
减法
先将其他类型隐式转成number类型再进行计算,如果转不了则变成NaN。
console.log(1 - NaN); // NaN (Number)
console.log(1 - true); // 0 (Number)
console.log(1 - false); // 1 (Number)
console.log(1 - null); // 1 (Number)
console.log(1 - undefined); // NaN (Number)
console.log(1 - []); // 1 空数组会转成0
console.log([] - 1); // -1
console.log([] - []); // 0
console.log(1 - [12]); // -11 数组中元素只有1个时,会将其隐式转化成number类型再进行运算
console.log(1 - [null]); // 1
console.log(1 - [undefined]); // 1
console.log(1 - [NaN]); // NaN
console.log(1 - [Infinity]); // -Infinity
console.log(1 - [-Infinity]); // Infinity
console.log(1 - [[]]); // 1
console.log(1 - [[1]]); // 0
console.log(1 - [{}]); // NaN
console.log(1 - [1,2,3]); // NaN 数组中元素数量超过1(>=2)时会转成NaN
console.log(1 - {}); // NaN 对象隐式转化后都是NaN,不论是空还是非空对象
console.log({} - 1); // NaN
console.log({} - {}); // NaN
console.log(1 - {name:'li'}); // NaN
console.log([] - {}); // NaN
console.log({} - []); // NaN
console.log([1,2,3] - {name:'li'}); // NaN
console.log(new Date() - 1);
// 从一个日期对象中减去一个数字时,JavaScript 会将该数字解释为毫秒数,并将这些毫秒数从日期对象的时间戳中减去。
乘法
和减法一样:先将其他类型隐式转成number类型再进行计算,如果转不了则变成NaN。
除法
也和减法一样:先将其他类型隐式转成number类型再进行计算,如果转不了则变成NaN。
但要注意:除以0(或隐式转化成0)为Infinity ,除以Infinity为0,除以-Infinity为-0
取模
- 如果左右两侧均是数值,则进行除法计算,结果返回余数
- 任何一侧为NaN,则结果返回NaN
- 只要除数是0(或隐式转化成0) ,则结果返回NaN
console.log(1 % NaN); // NaN (Number)
console.log(1 % true); // 0 (Number)
console.log(1 % false); // NaN
console.log(1 % null); // NaN
console.log(1 % undefined); // NaN
console.log(1 % []); // 1 NaN
console.log([] % 1); // 0
console.log([] % []); // NaN
console.log(1 % [12]); // 1
console.log(1 % [null]); // NaN
console.log(1 % [undefined]); // NaN
console.log(1 % [NaN]); // NaN
console.log(1 % [Infinity]); // 1
console.log(1 % [-Infinity]); // 1
console.log(1 % [[]]); // NaN
console.log(1 % [[1]]); // 0
console.log(1 % [{}]); // NaN
console.log(1 % [1,2,3]); // NaN
console.log(1 % {}); // NaN
console.log({} % 1); // NaN
console.log({} % {}); // NaN
console.log(1 % {name:'li'}); // NaN
console.log([] % {}); // NaN
console.log({} % []); // NaN
console.log([1,2,3] % {name:'li'}); // NaN
console.log(new Date() % 1);
// 对一个日期对象取模时,JavaScript 会隐式地将日期对象转换为其对应的时间戳(毫秒数)。
// 然后,JavaScript 会计算时间戳对给定数字的余数。
加号+与减号-
- 操作数是Number类型,无影响(不论是正号还是负号,对NaN都是无影响)
- 操作数是其他类型,它具有转化成Number类型的功能,
+a效果与Number(a)相同。
console.log(+'21') // 21
console.log(-'21') // 21
console.log(+[]); // 0
console.log(+[1]); // 1
console.log(+[1,2]); // NaN [1,2]原始值为'1,2',转化为Number类型为NaN
console.log(+{}); // NaN
console.log(-undefined) // NaN
let str = '123';
console.log(str + 2); // 1232
console.log(+str + 2); // 125 => '123'转化为Number类型
let num1 = 10;
let num2 = 20;
console.log(num1 + num2); // 30
console.log(num1 + '' + num2); // 1020 => 转化为字符串拼接
自增与自减
- 前置型
++a:先自增再使用 - 后置型
a++:先使用再自增 - 如果有一侧不为Number类型,则(根据对应的规则)转为数字类型后,再进行计算。
比较运算符
| 字符 | 说明 |
|---|---|
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
规则:
- 如果左右两侧都是数值,则数值之间进行比较
- 如果左右两侧都是字符串,则根据字符串对应的字符编码值
- 如果有一侧是NaN,则结果得到false
- 如果是
null,undefined,true,false,则(会根据对应的规则)进行转换,然后进行比较 undefined用>,<,<=,>=与任何值比较时都为false- 任何与 NaN 进行的比较运算(无论是使用
>、<、>=、<=还是==和===)都会返回false。这是因为 NaN 不等于任何值,包括它自己。
console.log(1 > 2) // false
console.log(1 > NaN) // 遇到NaN就是false
console.log('hello' > 'hey' ) // 依次按照字符编码来比较(l < y),返回false
console.log(1 > 'hello') // 'hello'转不了数字就转成了NaN,故是false
console.log(2 >'1') // 变成2 > 1,返回true
console.log(1 > null) // null转成0,结果是true
console.log(null > null) // false
console.log(null >= undefined) // 因为在和undefined比较,故为false
console.log(null <= undefined)// false
console.log(null >= 0) // true
console.log(null == 0) //false 因为null只==undefined
console.log(1 > false) // false转成0,故true
console.log(NaN >= NaN) // false
console.log('hello' < false) // false
// 字符串和布尔值之间的比较,字符串会被转换为数字。
// 字符串会尝试被解析成数字,如果可以解析成有效的数字,则会进行比较;
// 否则,字符串会被转换为 NaN
相等操作符
| 字符 | 说明 |
|---|---|
== | 相等 |
!= | 不等 |
=== | 全等 |
!== | 全不等 |
==和!=不会比较类型,如果类型不一致,会先进行隐式类型转换再进行比较===和!==是严格相等,会先比较类型是否相同,如果类型不同直接false,类型相同再比较值是否相同
- NaN不等于任何类型的数值,包括自己本身,所以都返回
false; - 如果都是String类型的话,比较字符编码值,如果完全一致,则返回
true,否则返回false; - 如果两侧都是Number类型数值,比较值是否相同;
- 如果两边都是Object类型,则比较地址是否一致;
null == undefined返回true- 如果一侧是
String,一侧是Number,将String转换为NaN之后,进行比较(如果String是数值类型则将String转换为Number进行比较) - 如果一侧是Boolean,则将布尔值转换为Number类型后,再根据上述规则进行比较;
console.log(null == undefined) // true
console.log(null == null) // true
console.log(undefined == undefined) // true
console.log(1 == '1') // true
console.log(null == NaN) // false
console.log(null === undefined) // false
console.log(null === null) // true
console.log(undefined === undefined) // true
console.log(1 === '1') // false
console.log(null === NaN) // false
注意:全等===有个缺陷: NaN === NaN的结果是false ,可以用Object.is():
Object.is(NaN, NaN)结果是trueObject.is(0, -0)结果是false
赋值运算符
=:普通赋值运算符+=:加法赋值运算符-=:减法赋值运算符*=:除法赋值运算符/=:乘法赋值运算符%=:取模赋值运算符**=:指数赋值运算符<<=:左移赋值运算符>>=:右移赋值运算符>>>=:无符号右移赋值运算符,将左侧的变量向右移动右侧的值所指定的位数,且用 0 填充左侧的空位,并将结果赋给左侧的变量。&&=:与赋值运算符,如果左侧的操作数为true,则将右侧的值赋给左侧的变量;否则将false赋给左侧的变量。||=:或赋值运算符,如果左侧的操作数为false,则将右侧的值赋给左侧的变量;否则将true赋给左侧的变量。??=:空值合并赋值运算符,x ??= y等同于x ?? (x = y),只有运算符左侧的值为null或undefined时 才会继续右侧的运算