← 返回目录

16. 可选链 ?. / 逻辑赋值

两个"少写判断"的省力语法:?."装了防撞条的自动扶梯"——链条中间任何一环是空的(null/undefined),它就直接停住返回 undefined,不会撞墙报错;??="只在真正没放东西时才放默认"——0 和空字符串这种"真放了东西"的不会被换掉。

16.1 互动演示

点按钮看 ?. 和 ??= 的行为。

16.2 知识点讲解

一、可选链 ?.

① 层层判断 → ?. 直接往下找

以前读深层数据 config.db.host,得每一层都问一句"有没有",写一长串 &&?. 一放,自动帮你问。

let config = { db: { host: "localhost" } }; // 一个套娃对象:config 里有 db,db 里有 host

// 以前:每层都要 && 判断"这一层有没有"
let host = config && config.db && config.db.host; // 一层层 && 问过去,空了就断
//     ^^^^^^  ^^^^^^^^^^^^  ^^^^^^^^^^^^^^
//     config有吗? db有吗?  db.host 有吗?
console.log(host); // localhost

// 现在:?. 一放,中间任何一环是空的就返回 undefined
let host = config?.db?.host; // ?. 像防撞条:中间哪层空了就直接停,不报错
//       ^^  ^^ 中间没有就停下,不报错
console.log(host); // localhost
console.log(config?.x?.y); // undefined(x 不存在,但不报错!)

?. 是"安全访问":链上任何一环是 null / undefined,就短路返回 undefined,继续往下读会直接报错。解析接口返回的深层数据(res.data.user.name)时最常用,不用写一长串 &&。

② 调方法也能用:obj.fn?.

不仅读属性,调方法也能加 ?.:方法不存在就不调,返回 undefined。

let obj = {}; // 空对象,里面啥方法都没有
// obj.fn?. —— fn 不存在就不调用,返回 undefined,不报错
// obj.fn  —— fn 不存在会报错:fn is not a function

二、逻辑赋值:||= &&= ??=

③ 基本对比:先判断再赋值 → 一步到位

||= 是"左边是假值就换成右边",&&= 是"左边是真值就换成右边",??= 是"左边是 null/undefined 才换"。都是 a = a 运算 b 的简写。

// 以前
let a = false;       // 左边是假值
a = a || true; // 左边是假 → 赋 true

// 现在
let a = false;       // 再来一遍
a ||= true; // 等价于 a = a || true

④ 关键区别:||= 会误伤 0 和 "",??= 不会

|| 看的是"是不是假值"(0、""、false、NaN 都算假),?? 只看"是不是 null / undefined"。

let n = 0;        // 0 是有效值
n ||= 100;        // || 把 0 当假,换成 100
console.log(n); // 100(0 被当成"假",被换掉了!)

let m = 0;        // 0 是有效值
m ??= 100;        // ?? 只认 null/undefined,0 不动
console.log(m); // 0(0 是有效值,保留)

如果你用 ||= 给用户配置兜底,用户传了 0(表示"不限制")也会被悄悄换成默认值。这就是线上常见的隐性 bug——给默认值时优先用 ??=,它只在 null/undefined 时才动手。

16.3 实战:用在哪 / 常见坑 / 怎么解决

① 可能在什么地方用:读接口深层字段(res?.data?.user?.name)、调用可选方法(obj.fn?.)、组件 props 兜底(props.title ?? "默认")、给配置对象补默认值。

② 常见的问题:||= 误伤 0 和 "";把 ?. 写成 ? ? 中间有空格;对整个表达式误加 ?. 导致可读性差。

③ 解决思路:兜底一律用 ?? / ??=,别用 ||?. 紧跟属性名无空格;链式访问控制在 3 层以内,太深就先把中间变量取出来。

一句话:?. 安全往下找,??= 安全给默认值;取默认值别用 ||=,会误伤 0 和 ""。