各位代码世界的探险家们,早上好!今天咱们聊聊JavaScript里一个不起眼,但关键时刻能救命的小英雄——空值合并运算符(Nullish Coalescing Operator,简称??
)。
啥是空值合并运算符?(别怕,名字唬人!)
简单来说,??
就像一个精明的守门员,专门负责拦截 null
和 undefined
这两个捣蛋鬼。它的作用是:如果左边的表达式是 null
或 undefined
,那就返回右边的表达式;否则,直接返回左边的表达式。
用人话说:左边是空的(null
或 undefined
),就用右边的值;否则,用左边的值。
举个栗子:
const name = null;
const defaultName = "匿名用户";
const displayName = name ?? defaultName;
console.log(displayName); // 输出: "匿名用户"
在这个例子里,name
是 null
,所以 ??
立刻让 displayName
变成了 "匿名用户"。
为啥我们需要它?它和 ||
有啥区别?
你可能会想:这玩意儿看起来和逻辑或运算符 ||
差不多嘛!都是给变量设置默认值,有啥区别?
区别大了!
||
会把左边的表达式转换成布尔值,只要是 false
,0
,""
(空字符串),NaN
,null
,undefined
,都会返回右边的表达式。也就是说,||
认为这些都是“假”的,都要被替换掉。
而 ??
就不一样了,它只认 null
和 undefined
,其他的都放行。
再举个栗子,让你明白透彻:
const count = 0;
// 使用 ||
const result1 = count || 10;
console.log(result1); // 输出: 10 (因为 0 被认为是 false)
// 使用 ??
const result2 = count ?? 10;
console.log(result2); // 输出: 0 (因为 0 不是 null 或 undefined)
看到没? ||
粗暴地把 0
当成了 "假" 值,直接替换成了 10
。而 ??
则坚守原则,认为 0
是一个有效值,所以 result2
仍然是 0
。
表格对比,一目了然:
运算符 | 作用 | 判定“空”的标准 |
---|---|---|
|| |
返回第一个真值,否则返回最后一个值 (常用于提供默认值) | false , 0 , "" , NaN , null , undefined |
?? |
返回第一个不是 null 或 undefined 的值,否则返回最后一个值 (专门用于处理 null 和 undefined 的默认值) |
null , undefined |
使用场景:
??
最擅长处理那些可能为 null
或 undefined
的变量,并提供一个合理的默认值。
-
读取对象属性:
const user = { profile: { age: 30, }, }; // 使用 ?? 安全地获取 age,如果 profile 或 age 不存在,则返回 0 const age = user?.profile?.age ?? 0; console.log(age); // 输出: 30 const user2 = { }; const age2 = user2?.profile?.age ?? 0; console.log(age2); // 输出: 0
这里用到了可选链操作符
?.
,它可以安全地访问深层嵌套的属性,如果中间有任何一个属性不存在,就会返回undefined
。然后,??
再接手,如果?.
返回的是null
或undefined
,就返回默认值0
。 -
处理函数参数:
function greet(name) { const displayName = name ?? "Guest"; console.log(`Hello, ${displayName}!`); } greet("Alice"); // 输出: Hello, Alice! greet(null); // 输出: Hello, Guest! greet(); // 输出: Hello, Guest! (因为没传参数,name 是 undefined)
如果调用
greet
函数时没有传递name
参数,或者传递的是null
,displayName
就会使用默认值 "Guest"。 -
配置项:
const config = { timeout: null, // 用户故意设置为null maxRetries: undefined, // 用户没配置 }; const finalTimeout = config.timeout ?? 5000; const finalMaxRetries = config.maxRetries ?? 3; console.log(finalTimeout); // 输出: null (用户设置的值应该保留) console.log(finalMaxRetries); // 输出: 3 (使用默认值)
这里,我们希望允许用户显式地将
timeout
设置为null
,表示禁用超时。如果使用||
,就会错误地将timeout
替换为5000
。而??
则可以正确地处理这种情况。
优先级问题:
??
的优先级比较低,所以在使用时要特别注意,必要时要加上括号。
举个反面教材:
// 错误示例:
// const result = false || null ?? "default"; // 报错:SyntaxError: Unexpected token '??'
// 正确示例:
const result1 = false || (null ?? "default");
console.log(result1); // 输出: false
const result2 = (false || null) ?? "default";
console.log(result2); // 输出: "default"
因为 ??
的优先级低于 ||
和 &&
,所以不能直接混合使用,必须用括号明确优先级。
和可选链操作符 ?.
配合使用,威力无穷!
前面已经提到,??
经常和 ?.
一起使用,?.
负责安全地访问对象属性,??
负责提供默认值。
const user = {};
const city = user?.address?.city ?? "Unknown";
console.log(city); // 输出: "Unknown"
即使 user
对象没有 address
属性,或者 address
对象没有 city
属性,这段代码也不会报错,而是会安全地返回 "Unknown"。
不支持的浏览器:
需要注意的是,??
是 ES2020 引入的特性,一些老旧的浏览器可能不支持。因此,在使用时要做好兼容性处理。可以使用 Babel 等工具进行代码转换,使其能在旧版本浏览器上运行。
一个更复杂的例子:
假设我们有一个用户对象,包含各种信息,有些信息可能缺失:
const user1 = {
name: "小明",
age: 18,
address: {
city: "北京",
},
preferences: {
theme: "dark",
},
};
const user2 = {
name: "小红",
age: null, // 年龄未知
address: null, // 地址未知
preferences: {}, // 偏好设置存在,但为空
};
const user3 = {
name: "小刚",
preferences: undefined, // 偏好设置不存在
};
现在,我们要编写一个函数,根据用户对象生成一段描述:
function generateDescription(user) {
const name = user.name ?? "匿名用户";
const age = user.age ?? "年龄未知";
const city = user?.address?.city ?? "未知城市";
const theme = user?.preferences?.theme ?? "light"; // 默认主题是 light
return `
用户姓名:${name}
年龄:${age}
城市:${city}
主题:${theme}
`;
}
console.log(generateDescription(user1));
/*
用户姓名:小明
年龄:18
城市:北京
主题:dark
*/
console.log(generateDescription(user2));
/*
用户姓名:小红
年龄:年龄未知
城市:未知城市
主题:light
*/
console.log(generateDescription(user3));
/*
用户姓名:小刚
年龄:年龄未知
城市:未知城市
主题:light
*/
在这个例子中,我们使用了 ??
和 ?.
来处理各种可能为空的情况,保证了代码的健壮性。
总结:
??
空值合并运算符是一个小而美的工具,它可以帮助我们更优雅地处理 null
和 undefined
,避免一些潜在的错误。记住,它只认 null
和 undefined
,其他的都放行。在合适的场景下使用 ??
,可以使你的代码更加简洁、安全、易读。
希望今天的讲解对你有所帮助!记住,代码世界充满了乐趣,保持好奇心,不断探索,你就能成为一名真正的代码探险家!下次有机会再和大家分享其他的编程技巧。祝大家编码愉快!