两个"少写判断"的省力语法:?. 像"装了防撞条的自动扶梯"——链条中间任何一环是空的(null/undefined),它就直接停住返回 undefined,不会撞墙报错;??= 像"只在真正没放东西时才放默认"——0 和空字符串这种"真放了东西"的不会被换掉。
以前读深层数据 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)时最常用,不用写一长串 &&。
不仅读属性,调方法也能加 ?.:方法不存在就不调,返回 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、""、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 时才动手。
① 可能在什么地方用:读接口深层字段(res?.data?.user?.name)、调用可选方法(obj.fn?.)、组件 props 兜底(props.title ?? "默认")、给配置对象补默认值。
② 常见的问题:用 ||= 误伤 0 和 "";把 ?. 写成 ? ? 中间有空格;对整个表达式误加 ?. 导致可读性差。
③ 解决思路:兜底一律用 ?? / ??=,别用 ||;?. 紧跟属性名无空格;链式访问控制在 3 层以内,太深就先把中间变量取出来。