可选链

可选链 ?. 可以让我们安全地访问对象的属性,即使中间的属性不存在,也不会出现错误。

不存在属性的问题

当访问对象的内部对象的属性时,如果内部对象未定义,则访问其属性时会出错,因此此时等同于访问一个 undefined 中的属性。

$$jsdemo$$
$$edit$$
let mingren = {
    name: "鸣人",
    wife: {
        name: "雏田",
    },
}

let kenan = {
    name: "柯南",
}

console.log(mingren.wife.name) // 雏田

// Uncaught TypeError: Cannot read properties of undefined (reading 'name')
// 因为 kenan.wife == undefined
// 等同于 undefined.name
console.log(kenan.wife.name)

当然也可以事先用 if 去判断,不过略显累赘。

$$jsdemo$$
$$edit$$
let mingren = {
    name: "鸣人",
    wife: {
        name: "雏田",
    },
}

let kenan = {
    name: "柯南",
}

if (mingren.wife) {
    console.log(mingren.wife.name)
}

if (kenan.wife) {
    console.log(kenan.wife.name)
}

特别是层级很深的时候。

$$jsdemo$$
$$edit$$
let mingren = {
    name: "鸣人",
    wife: {
        name: "雏田",
        brother: {
            name: "宁次",
        },
    },
}

// 恐怖的多层级嵌套
if (mingren.wife) {
    if (mingren.wife.brother) {
        console.log(mingren.wife.brother.name)
    }
}

此时我们更期望任一层级中的对象不存在时就返回 undefined ,而不是出现错误。

可选链

如果可选链 ?.前面的值为 undefined或者 null,它会停止运算并返回 undefined

$$jsdemo$$
$$edit$$
let mingren = {
    name: "鸣人",
    wife: {
        name: "雏田",
        brother: {
            name: "宁次",
        },
    },
}

let kenan = null

console.log(mingren?.wife?.brother?.name) // 宁次
console.log(kenan?.wife?.name) // undefined,kenan 不存在
console.log(mingren?.brother?.name) // undefined, mingren.brother 不存在

$$warning

可选链不能用在赋值语句的左侧。

$$

$$jsdemo$$
$$edit$$
let mingren = {
    name: "鸣人",
}

mingren?.friend = "小樱" // Uncaught SyntaxError: Invalid left-hand side in assignment
console.log(mingren.friend)

其他形式

method?.()

如果 method 存在则执行 method() ,否则返回 undefined

$$edit$$
$$jsdemo$$
let mingren = {
    name: "鸣人",
    sayHello() {
        alert("你好~")
    },
}

let kenan = {
    name: "柯南",
}

mingren.sayHello?.() // 你好
kenan.sayHello?.() // 没有执行

obj?.[]

如果 obj 存在则返回 obj[prop] ,否则返回 undefined

$$edit$$
$$jsdemo$$
let mingren = {
    name: "鸣人",
    friends: ["小樱", "佐助"],
}

let kenan = {
    name: "柯南",
}

alert(mingren.friends?.[0]) // 小樱
alert(kenan.friends?.[0]) // undefined

练习

  1. 现某学术期刊收到上传的论文数据,根据以下标准展示。
  • 存在年龄字段并且成年(大于等于 18 岁),否则不显示论文。
  • 存在姓名则显示,否则显示为匿名。
  • 存在发布时间则显示,否则不显示。
// 论文数据
const articles = [
    {
        title: "蟹黄堡的营销发展。",
        author: {
            name: "蟹老板",
            age: 34,
        },
        info: {
            publishTime: "2022年7月18日",
        },
    },
    {
        title: "海洋生物与陆地生物的差异。",
    },
    {
        title: "海绵宝宝存在的意义。",
        author: {
            name: "海绵宝宝",
            age: 18,
        },
    },
    {
        title: "派大星的私密生活。",
        author: {
            name: "派大星",
            age: 16,
        },
    },
    {
        title: "蟹黄堡配方调研。",
        author: {
            name: "痞老板",
            age: 32,
        },
        info: {
            publishTime: null,
        },
    },
    {
        title: "如何在海底培育植物。",
        author: {
            age: 20,
        },
        info: {
            publishTime: "2022年10月20日",
        },
    },
]

$$demo <button onclick="show()">显示论文</button> <script> // 论文数据 const articles = [ { title: "蟹黄堡的营销发展。", author: { name: "蟹老板", age: 34, }, info: { publishTime: "2022年7月18日", }, }, { title: "海洋生物与陆地生物的差异。", }, { title: "海绵宝宝存在的意义。", author: { name: "海绵宝宝", age: 18, }, }, { title: "派大星的私密生活。", author: { name: "派大星", age: 16, }, }, { title: "蟹黄堡配方调研。", author: { name: "痞老板", age: 32, }, info: { publishTime: null, }, }, { title: "如何在海底培育植物。", author: { age: 20, }, info: { publishTime: "2022年10月20日", }, }, ]

function show() {
    for (const article of articles) {
        if (!article.author?.age) {
            // 未标注年龄不允许发布
            continue
        }

        if (article.author.age < 18) {
            // 年龄小于 18 不允许发布
            continue
        }

        let infoList = [`选题:${article.title}`]

        infoList.push(`署名:${article.author.name ?? "匿名"} `)

        if (article.info?.publishTime) {
            infoList.push(`发布时间:${article.info.publishTime}`)
        }

        alert(infoList.join("\n"))
    }
}

</script>

$$

$$answer

$$jsdemo$$
$$edit$$
<script>
    // 论文数据
    const articles = [
        {
            title: "蟹黄堡的营销发展。",
            author: {
                name: "蟹老板",
                age: 34,
            },
            info: {
                publishTime: "2022年7月18日",
            },
        },
        {
            title: "海洋生物与陆地生物的差异。",
        },
        {
            title: "海绵宝宝存在的意义。",
            author: {
                name: "海绵宝宝",
                age: 18,
            },
        },
        {
            title: "派大星的私密生活。",
            author: {
                name: "派大星",
                age: 16,
            },
        },
        {
            title: "蟹黄堡配方调研。",
            author: {
                name: "痞老板",
                age: 32,
            },
            info: {
                publishTime: null,
            },
        },
        {
            title: "如何在海底培育植物。",
            author: {
                age: 20,
            },
            info: {
                publishTime: "2022年10月20日",
            },
        },
    ]

    for (const article of articles) {
        if (!article.author?.age) {
            // 未标注年龄不允许发布
            continue
        }

        if (article.author.age < 18) {
            // 年龄小于 18 不允许发布
            continue
        }

        let infoList = [`选题:${article.title}`]

        infoList.push(`署名:${article.author.name ?? "匿名"} `)

        if (article.info?.publishTime) {
            infoList.push(`发布时间:${article.info.publishTime}`)
        }

        alert(infoList.join("\n"))
    }
</script>

$$