在本教程中,您将了解可选的链接运算符 ?.,它简化已连接对象的访问值的方式。

JavaScript 可选链运算符介绍

可选的链接运算符 ?. 允许您访问位于对象链深处的属性值,而无需显式检查链中的每个引用是否为null 或者是 undefined

如果链中的引用对象之一是 null 或者 undefined,则可选的链接运算符 ?. 将短路并返回 undefined

假设您有一个返回 user 对象的函数:

function getUser(id) {

    if(id <= 0) {
        return null;
    }

    // 获取用户从数据库
    // 如果用户不存在则返回null
    // ...
    
    // 如果用户找到,则返回用户
    return {
        id: id,
        username: 'admin',
        profile: {
            avatar: '/avatar.png',
            language: 'English'
        }
    }
}

然后使用 getUser() 函数访问用户配置文件:

let user = getUser(1);
let profile = user.profile;

但是,如果您传递 id 小于或等于零或 id 在数据库不存在 ,getUser() 函数将返回 null

因此,在访问 avatar 属性之前,您需要检查 user 是否是null,使用逻辑运算符

let user = getUser(2);
let profile = user && user.profile;

在此示例中,我们确认 user 不是 undefinednull 之前访问 user.profile 属性的值。它可以避免直接访问 user.profile 引发的错误。

ES2020 引入可选的链接运算符,由问号后跟一个点表示:

?.

要使用可选的链接运算符访问对象的属性,您可以使用以下语法之一:

objectName ?. propertyName
objectName ?. [expression]

可选的链接运算符在尝试访问 user.profile 之前,隐式检查 user 是否是 nullundefined

let user = getUser(2);
let profile = user ?. profile;

在此示例中,如果 usernull 或者 undefined,则可选的链接运算符 ?. 会立即返回 undefined

从技术上讲,它等同于下面的语句:

let user = getUser(2);
let profile = (user !== null || user !== undefined)
            ? user.profile
            : undefined;

堆叠可选的链接运算符

如果 getUser() 返回的 user 对象没有 profile 属性。在不检查第一个的情况下尝试访问 user.profileavatar 属性将会导致错误。

为避免错误,您可以多次使用可选的链接运算符,如下所示:

let user = getUser(-1);
let avatar = user ?. profile ?. avatar;

在这种情况下,avatarundefined.

组合空值合并运算符

如果要将默认配置文件分配给 user,可以将可选的链接运算符 ?. 与空值合并运算符 ?? 组合使用,如下所示:

let defaultProfile =  { default: '/default.png', language: 'English'};

let user = getUser(2);
let profile = user ?. profile ?? defaultProfile;

在此示例中,如果 user.profilenull 或者是 undefined,则配置文件将因为空值合并运算符而采用 defaultProfile

在函数使用可选的链接运算符

假设您有一个文件 API,如下所示:

let file = {
    read() {
        return 'file content';
    },
    write(content) {
        console.log(`Writing ${content} to file...`);
        return true;
    }
};

本例调用 file 对象的 read() 方法:

let data = file.read();
console.log(data);

如果您调用 file 对象中不存在的方法,您将得到 TypeError

let compressedData = file.compress();

错误:

Uncaught TypeError: file.compress is not a function

但是,如果您在方法调用中使用可选的链接运算符,表达式将返回 undefined 而不是抛出错误:

let compressedData = file.compress?.();

compressedData 现在是 undefined

当您使用的 API 中的方法可能由于某种原因(例如,特定的浏览器或设备)不可用时,这很有用。

下面展示在函数或方法调用中使用可选链接运算符的语法:

functionName ?. (args)

如果您有一个带有可选回调的函数,则可选的链接运算符 ?. 也很有用:

function getUser(id, callback) {
    // 获取用户
    // ...

    let user = {
        id: id,
        username: 'admin'
    };

    // 判断回调是否存在
    if ( callback ) {
        callback(user);
    }

    return user;
}

通过使用可选的链接运算符,如果回调存在,您可以跳过判断:

function getUser(id, callback) {
    // 获取用户
    // ...

    let user = {
        id: id,
        username: 'admin'
    };

    // test if the callback exists
    callback ?. (user);


    return user;
}

结论

如果您尝试访问一个为 null 或者 undefined 的对象属性,可选的链接运算符 ?. 将返回 undefined 而不是抛出错误:。

将可选的链接运算符 ?. 与空值合并运算符 ?? 组合使用可以分配默认值。

functionName ?. (args) 用于在调用 functionName 之前,避免显式检查 functionName 是否存在undefinednull