1. 可选链操作符 —— ?.
可选链操作符 (
?.
) 允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?.
操作符的功能类似于.
链式操作符,不同之处在于,在引用为空 (nullish ) (null
或者undefined
) 的情况下不会引起错误,该表达式短路返回值是undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回undefined
。
1.1 ?.
语法
obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
1.2 ?.
描述
假设我们需要引用一个多层嵌套或单层的object,最原始的做法是:
human.action
human.action.play
-
如果
human == null
,对于读取单层Obj,控制台会报:Uncaught TypeError: Cannot read properties of undefined (reading 'action')
-
如果
human == {}
,对于读取多层嵌套,控制台也会同样报上述错误
如何解决?很好解决,加上判断即可
// 写法一
const human = null;
if (human) {
console.log(human.action);
}
// 写法二 - 三目
const human = null;
human ? console.log(human.action) : '';
// 写法三
const human = null;
human && console.log(human.action);
单层Obj
还好,但如果是多层嵌套的Obj
,那么这也判断是相当的麻烦
let play = human.action && human.action.play;
为了避免报错,在访问
human.action.play
之前,要保证human.action
的值既不是null
,也不是undefined
。如果只是直接访问human.action.play
,而不对human.action
进行校验,则有可能抛出错误。
于是我们就使用?.
来简化,在访问 human.action.play
之前,不再需要明确地校验 human.action
的状态,再并用短路计算获取最终结果。
let play = human.action?.play
- JavaScript在访问
human.action.play
会先隐式的检查human.action
是不是null
orundefined
。若是,则会直接返回undefined
const human = {}; //若后端接口返回数据为{},而正常是有数据的
console.log(human.action.play); // 直接访问报Uncaught TypeError错误
console.log(human.action?.play); // 使用?.不报错,但会输出undefined
//其等价于
console.log(human.action === null || human.action === undefined ? undefined : human.action.play);
1.3 ?.
应用
1. ?.
与函数调用
const human = {};
human.eat(); // Uncaught TypeError: human.eat is not a function
human.eat?.(); // 不报错
2. ?.
处理回调函数或事件处理器
// 使用可选链进行函数调用 - 引用自MDN
function doSomething(onContent, onError) {
try {
// ... do something with the data
}
catch (err) {
onError?.(err.message); // 如果 onError 是 undefined 也不会有异常
}
}
3. ?.
和表达式
const human = {
a: 'a',
};
let variable = 'a';
console.log(human?.['a']); // 输出 a
console.log(human?.[variable]); // 输出 a
4. ?.
和访问数组元素
const human = ['a', 'b', 'c'];
console.log(human?.[2]); // 输出 c
const human = [];
console.log(human?.[2]); // undefined
5. ?.
和Map
const human = new Map([
['first', { content: 'abcdefg' }],
['second', { content: 'hijklmn' }],
]);
console.log(human.get('first')?.content); // 输出 abcdefg
console.log(human.get('third')?.content); // undefined
6. ?.
和短路计算
// 引用自MDN
let potentiallyNullObj = null;
let x = 0;
let prop = potentiallyNullObj?.[x++];
console.log(x); // x 将不会被递增,依旧输出 0
7. 连用?.
const person = {
name: 'News',
action: {
play: {
game: 'csgo',
},
watch: '',
},
cat: {
name: 'lemon',
},
};
console.log(person.action?.play?.game); // 输出 csgo
8. ?.
和空值合并操作符??
const human = null;
console.log(human?.name ?? 'human 为null'); // 输出 human 为 null
2.4 注意
?.
不能用于赋值
let object = {};
object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment
2. 空值合并运算符 —— ??
空值合并操作符(
??
)是一个逻辑操作符,当左侧的操作数为null
或者undefined
时,返回其右侧操作数,否则返回左侧操作数。
与逻辑或操作符(
||
)不同,逻辑或操作符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用||
来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,''
或0
)时。见下面的例子。
console.log(null ?? 'hello'); // 输出 hello
console.log('' ?? 'hello'); // 输出 空
console.log(null || 'hello'); // 输出 hello
console.log('' || 'hello'); // 输出 hello
2.1 ??
语法
leftExpr ?? rightExpr
2.2 ??
应用
1. 保证常量/变量不为 null
或者 undefined
let variable;
const val = variable ?? 'val默认值';
2. ??
和短路
// 引用自MDN
function A() { console.log('函数 A 被调用了'); return undefined; }
function B() { console.log('函数 B 被调用了'); return false; }
function C() { console.log('函数 C 被调用了'); return "foo"; }
console.log( A() ?? C() );
// 依次打印 "函数 A 被调用了"、"函数 C 被调用了"、"foo"
// A() 返回了 undefined,所以操作符两边的表达式都被执行了
console.log( B() ?? C() );
// 依次打印 "函数 B 被调用了"、"false"
// B() 返回了 false(既不是 null 也不是 undefined)
// 所以右侧表达式没有被执行
2.3 注意
将
??
直接与 AND(&&
)和 OR(||
)操作符组合使用是不可取的。
null || undefined ?? "foo"; // 抛出 SyntaxError
true || undefined ?? "foo"; // 抛出 SyntaxError
但是,如果使用括号来显式表明运算优先级,是没有问题的:
(null || undefined ) ?? "foo"; // 返回 "foo"