目录
1、指数运算符
ES6新增指数运算符:**
大多数运算符是左结合,指数运算度是又结合,多个指数运算符连用时是从左右边开始计算的。
// 指数运算符
console.log(2 ** 2); // 4
console.log(2 ** 3); // 8
// 从右边开始计算 相当于 2 ** (2 ** 3)
console.log(2 ** 2 ** 3); // 256
// 从左计算需要加括号
console.log((2 ** 2) ** 3); // 64
指数运算符和等号结合,形成一个新的赋值运算符( **= )。
let num = 2;
// 相当于 num = num * num; num = num ** 2;
num **= 2;
console.log(num); // 4
2、可选链运算符
写法:?.
开发过程中,想要读取一个对象中可能不存在的值,往往需要判断一下。没有可选链操作符之前是这样写。
let animal = {
cat: {
name: "Tom",
sex: "男",
age: 1,
},
};
let dog = (animal && animal.dog && animal.dog.name) || "default";
上面代码,name属性在对象中的第三层,需要每一层判断一下属性是否存在。
使用可选链就会方便很多。如果左侧对象是null或者undefined时,表达式将会短路计算直接返回undefined。
let dog = animal.dog?.name || "default";
animal.dog?.name 相当于
(animal.dog === undefined || animal.dog === null) ? undefined : animal.dog.name;
调用函数也是一样的,如果函数不存在,自动返回undefined,而不是报错。
animal.eat(); // TypeError: animal.eat is not a function
animal.eat?.(); // undefined
注意:如果存在一个属性名但不是函数,使用可选链依然会报错。
let animal = {
cat: {
name: "Tom",
sex: "男",
age: 1,
eat: "干饭",
},
};
animal.cat.eat?.(); // TypeError: animal.cat.eat is not a function
可选链不能用于赋值
let cat = {};
cat?.name = "Tom"; // SyntaxError: Invalid left-hand side in assignment
可选链可访问数组
let arr = ["Tom"];
let arrayItem = arr?.[0];
console.log(arrayItem); // Tom
短路计算
当在表达式中使用可选链时,如果左操作数是null或undefined时,表达式将不会被计算。
let cat = {
name: "Tom",
age: 1,
};
cat.sex?.[cat.age++];
console.log(cat.age); // 1
括号的影响
对括号外部没影响,只对括号内部有影响。一般情况,使用?.的场合最好不要使用括号。
// 不管animal对象是否存在,都会执行.name
(animal?.cat).name;
// 等于
(animal === undefined || animal === null ? undefined : animal.cat).name;
3、空值合并运算符
空值合并运算符(??)是一个逻辑运算符,与 || 运算符类似,当 ?? 运算符左侧返回 null 或 undefined 时,则返回 ?? 操作符右侧的内容。
与 || 运算符不同的是, || 运算符左侧返回 假 值时,返回右侧内容。?? 运算符只有左侧返回 null 或 undefined 时才返回右侧内容。
基本用法
举个例子:开发者的需求是,只要属性值为 null 或 undefined 时,就给变量一个默认值。
let cat = {
name: "",
};
// 使用 || 运行算符,name属性的值是 "" 、false 、0 、NaN 的时候,默认值也会生效
let name = cat.name || "Tom";
console.log(name); // Tom
// 使用 ?? 运算符,完美解决需求
let name1 = cat.name ?? "Tom";
console.log(name1); // ""
// 删除 name 属性
delete cat.name;
console.log(cat.name); // undefined
let name2 = cat.name ?? "Tom";
console.log(name2); // Tom
与 ?. 一起使用
配合可选链(?.)一起使用,为 null 或 undefined 的值设置默认值。
// 配合可选链一起使用
let cat = {};
let name = cat?.name ?? "Tom";
console.log(name); // Tom
短路
与 || 和 && 运算符相似,左侧表达式不为 null 或 undefined 时,不对右侧表达式进行运算。
function a() {
console.log("我是a函数");
return false;
}
function b() {
console.log("我是b函数");
return undefined;
}
function c() {
console.log("我是c函数");
return "c";
}
console.log(a() ?? c());
// 依次打印 我是a函数 false
// a() 返回 false, 既不是 null 也不是 undefined
// 所以右侧表达式没有执行
console.log(b() ?? c());
// 依次打印了 我是b函数 、 我是c函数 、 c
// b() 返回 undefined,所以左右两侧表达式都执行了
与 || 或 && 一起使用
?? 本质上是逻辑运算符,但是 ?? 和 || 和 && 运算符 的优先级没有被定义,现在的规则是,如果多个逻辑运算符一起使用,必须用括号指定优先级,否则会报错。
left && middle ?? right
left ?? middle && right
left || middle ?? right
left ?? middle || right
// SyntaxError
// 上边4个表达式全部报错
// 用括号指定优先级
console.log((undefined && null) ?? "Tom"); // Tom
console.log(undefined && (null ?? "Tom")); // undefined
console.log((undefined ?? null) && "Tom"); // null
console.log(undefined ?? (null && "Tom")); // null
console.log((undefined || null) ?? "Tom"); // Tom
console.log(undefined || (null ?? "Tom")); // Tom
console.log((undefined ?? null) || "Tom"); // Tom
console.log(undefined ?? (null || "Tom")); // Tom
4、逻辑赋值运算符
ES2021 引入了三个新的逻辑赋值运算符(logical assignment operators),将逻辑运算符与赋值运算符进行结合。
基本用法
let name = "";
// 或赋值运算符
name ||= "||Tom";
// 等同于
name || (name = "||Tom");
// 与赋值运算符
name &&= "&Tom";
// 等同于
name && (name = "&Tom");
// 逻辑空赋值运算符
name ??= "??Tom";
// 等同于
name ?? (name = "??Tom");
这三个运算符 ||= 、&&= 、??=,相当于先进行左侧逻辑运算,然后根据运算结果再进行赋值运算。
用途:为属性设置默认值。
let cat = {
name: "",
};
// 如果cat对象的name属性为逻辑假,则赋值为 Tom
cat.name ||= "Tom";
// 如果cat对象的age属性不存在,则赋值为 1
cat.age ??= 1;
console.log(cat); // {name: 'Tom', age: 1}
另一个例子:
// 旧写法
function setData(opts) {
opts.name = opts.name ?? "Tom";
opts.age ?? (opts.age = 1);
}
// 新写法
function setData(opts) {
opts.name ??= "Tom";
opts.age ??= 1;
}
很明显,新写法比旧写法更紧凑。