目录
可选链
可选链操作符( ?.
)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?.
操作符的功能类似于 .
链式操作符,不同之处在于,在引用为空(nullish
) (null
) 或者 undefined
) 的情况下不会引起错误,该表达式短路返回值是 undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined
。
示例:
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
},
};
const dogName = adventurer.dog?.name;
console.log(dogName); // undefined
console.log(adventurer.someNonExistentMethod?.()); // undefined
当尝试访问可能不存在的对象属性时,可选链运算符将会使表达式更短、更简明。通过连接的对象的引用或函数可能是 undefined
或 null
时,可选链运算符提供了一种方法来简化被连接对象的值访问。
const user = {
details: {
name: 'sunrise',
contact: {
email: 'sunrise@example.com'
}
}
};
// 传统方法访问 email
let email = '';
if (user && user.details && user.details.contact) {
email = user.details.contact.email;
} else {
email = 'No email available';
}
console.log(email); // 输出: sunrise@example.com
// 使用可选链访问 email
const email = user?.details?.contact?.email || 'No email available';
console.log(email); // 输出: sunrise@example.com
语法:
obj.val?.prop
obj.val?.[expr]
obj.func?.(args)
更多:
可选链和表达式:
当使用方括号与属性名的形式来访问属性时,你也可以使用可选链运算符:
let nestedProp = obj?.["prop" + "Name"];
可选链不能用于赋值:
let object = {};
object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment
可选链访问数组元素:
let arrayItem = arr?.[42];
短路计算
当在表达式中使用可选链时,如果左操作数是 null
或 undefined
,表达式将不会被计算
let potentiallyNullObj = null;
let x = 0;
let prop = potentiallyNullObj?.[x++];
console.log(x); // x 将不会被递增,依旧输出 0
连用可选链运算符
可以连续使用可选链读取多层嵌套结构:
let customer = {
name: "sunrise",
details: {
age: 21,
location: "Paradise Falls", // details 的 address 属性未有定义
},
};
let customerCity = customer.details?.address?.city;
// … 可选链也可以和函数调用一起使用
let duration = vacations.trip?.getTime?.();
可选链与函数调用
const utils = {
stringUtils: {
toUpperCase(str) {
return str.toUpperCase();
}
}
};
// 使用可选链进行函数调用
const result = utils?.stringUtils?.toUpperCase('hello world');
console.log(result); // 输出: HELLO WORLD
如果
utils.stringUtils
不存在,utils?.stringUtils.toUpperCase('hello world')
将返回undefined
而不是抛出错误。
可选链与 nullish
合并运算符:
const user = {
details: {
name: 'sunrise',
// contact属性可能不存在
}
};
// 使用可选链和 nullish 合并运算符
const contactEmail = user?.details?.contact?.email ?? 'No email available';
console.log(contactEmail); // 输出: No email available
如果
user.details.contact.email
链中的任何部分不存在,将返回'No email available'
。
总结:
可选链是一种强大的工具,它允许你编写更少的代码来处理深层对象属性,同时避免了可能的错误。它使代码更干净、更易于维护,特别是在处理复杂的数据结构时。
空值合并
空值合并运算符(??
)是一个逻辑运算符,当左侧的操作数为 null
或者 undefined
时,返回其右侧操作数,否则返回左侧操作数。
与逻辑或运算符(||
)不同,逻辑或运算符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 ||
来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,''
或 0
)时
示例:
const foo = null ?? 'default string';
console.log(foo);// "default string"
const baz = 0 ?? 42;
console.log(baz);// 0
语法:
let value = a ?? b;
如果
a
为null
或undefined
,则value
将被赋值为b
。如果a
为任何其他假值(如0
、false
或空字符串''
),则value
将被赋值为a
。
在没有空值运算符之前,我们通常使用逻辑或 ||
运算符来提供一个默认值,但逻辑或运算符会将所有假值(如 0
、false
、''
、NaN
)都替换为默认值。
使用空值运算符,我们可以确保仅在值为 null
或 undefined
时才使用默认值。
const user = {
name: 'sunrise',
age: 0,
contact: {
email: undefined
}
};
// 传统方法访问 email
let email = user.contact && user.contact.email || 'default@email.com';
console.log(email); // 输出: default@email.com
// 使用空值运算符访问 email
const email = user.contact?.email ?? 'default@email.com';
console.log(email); // 输出: default@email.com
由于
user.contact.email
是undefined
,因此使用了默认值。但是,如果
如果
user.contact.email
是null
或undefined
,'default@email.com'
。但是,如果user.contact.email
是一个空字符串,它将被保留,因为空字符串不是null
也不是undefined
。
为变量赋默认值
以前,如果想为一个变量赋默认值,通常的做法是使用逻辑或运算符(||):
let foo;
// foo is never assigned any value so it is still undefined
let someDummyText = foo || "Hello!";
然而,由于 ||
是一个布尔逻辑运算符,左侧的操作数会被强制转换成布尔值用于求值。任何假值(0
, ''
, NaN
, null
, undefined
)都不会被返回。这导致如果你使用0
,''
或NaN
作为有效值,就会出现不可预料的后果。
let count = 0;
let text = "";
let qty = count || 42;
let message = text || "hi!";
console.log(qty); // 42,而不是 0
console.log(message); // "hi!",而不是 ""
空值合并运算符可以避免这种陷阱,其只在第一个操作数为null
或 undefined
时(而不是其他假值)返回第二个操作数:
let myText = ""; // An empty string (which is also a falsy value)
let notFalsyText = myText || "Hello world";
console.log(notFalsyText); // Hello world
let preservingFalsy = myText ?? "Hi neighborhood";
console.log(preservingFalsy); // '' (as myText is neither undefined nor null)
短路
与 OR 和 AND 逻辑运算符相似,当左表达式不为 null
或 undefined
时,不会对右表达式进行求值。
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)
// 所以右侧表达式没有被执行
不能与 AND 或 OR 运算符共用
将 ??
直接与 AND(&&
)和 OR(||
)运算符组合使用是不可取的。(应当是因为空值合并运算符和其他逻辑运算符之间的运算优先级/运算顺序是未定义的)这种情况下会抛出 SyntaxError
。
null || undefined ?? "foo"; // 抛出 SyntaxError
true || undefined ?? "foo"; // 抛出 SyntaxError
但是,如果使用括号来显式表明运算优先级,是没有问题的:
(null || undefined) ?? "foo"; // 返回 "foo"
总结:
空值合并运算符提供了一种更精确的方法来处理可能为 null
或 undefined
的值,同时保留其他假值。这使得它在处理具有默认值的变量时比逻辑或运算符更安全、更灵活。