各位靓仔靓女,早上好/下午好/晚上好(取决于你啥时候看这篇文章)!我是你们的老朋友,今天咱们来聊聊 JavaScript 里那些“偷懒神器”——逻辑赋值运算符。
话说程序员嘛,天生就喜欢偷懒。能少写一行代码,绝对不多写一个字。JavaScript 早就看穿了咱们的小心思,所以贴心地提供了 &&=
, ||=
, ??=
这三个逻辑赋值运算符。它们能让你的条件赋值语句变得更简洁、更优雅(逼格更高),当然也更容易理解(如果学会了的话)。
一、&&=
(逻辑与赋值)—— “短路”赋值
&&=
运算符,可以理解为“如果左边的值是真值,那么就将右边的值赋给左边”。 它的完整形式是:
a &&= b;
这等价于:
a = a && b;
再等价于:
if (a) {
a = b;
}
看起来好像也没省多少代码? 别急,想象一下更复杂的场景。 &&=
的精髓在于它的“短路”特性。 只有当 a
是真值时,才会执行赋值操作。 如果 a
是假值,比如 false
、null
、undefined
、0
、""
(空字符串) 或者 NaN
,那么 a
的值保持不变,根本不会执行 b
的求值。
示例 1:确保对象存在后再添加属性
假设你有一个对象 user
,你只想在 user
存在的情况下,才给它添加一个 age
属性。 以前你可能会这么写:
let user = { name: "张三" }; // 假设 user 可能存在,也可能不存在
if (user) {
user.age = 30;
}
console.log(user); // 输出: { name: '张三', age: 30 }
现在,有了 &&=
,一行代码搞定:
let user = { name: "张三" };
user &&= { ...user, age: 30 }; // 展开运算符...是为了创建新的对象,避免修改原对象
console.log(user); // 输出: { name: '张三', age: 30 }
如果 user
一开始是 null
或者 undefined
,那么 user
的值会保持不变。
示例 2:优化条件更新
假设你有一个变量 count
,你想在某个条件成立时,才将它乘以 2。
let count = 5;
let condition = true;
if (condition) {
count = count * 2;
}
console.log(count); // 输出: 10
使用 &&=
:
let count = 5;
let condition = true;
condition &&= count * 2; //condition会变为10
console.log(condition); // 输出: 10
注意,这里condition
的值变为了count * 2
的结果,因为condition
本身是一个变量,赋值运算会修改它的值。 如果你的目的是更新count
,你需要稍微调整一下:
let count = 5;
let condition = true;
condition && (count *= 2); // 注意这里使用了括号
console.log(count); // 输出: 10
注意事项:
&&=
的优先级低于逻辑与运算符&&
,所以要注意添加括号来改变运算顺序。&&=
主要用于条件赋值,它并不会直接返回一个布尔值。
二、||=
(逻辑或赋值)—— “备胎”赋值
||=
运算符,可以理解为“如果左边的值是假值,那么就将右边的值赋给左边”。 它的完整形式是:
a ||= b;
这等价于:
a = a || b;
再等价于:
if (!a) {
a = b;
}
同样,||=
也具有“短路”特性。 只有当 a
是假值时,才会执行赋值操作。 如果 a
是真值,那么 a
的值保持不变,不会执行 b
的求值。
示例 1:提供默认值
假设你从某个地方获取一个配置项 theme
,但如果这个配置项不存在(null
或 undefined
),你想给它设置一个默认值 "light"。
let theme = null; // 假设 theme 可能为 null
if (!theme) {
theme = "light";
}
console.log(theme); // 输出: light
使用 ||=
:
let theme = null;
theme ||= "light";
console.log(theme); // 输出: light
如果 theme
一开始是 "dark",那么 theme
的值会保持不变。
示例 2:初始化变量
假设你有一个变量 username
,你想确保它总有一个值。 如果它一开始是 undefined
,你想给它设置一个默认的用户名 "Guest"。
let username; // username 未定义
if (!username) {
username = "Guest";
}
console.log(username); // 输出: Guest
使用 ||=
:
let username;
username ||= "Guest";
console.log(username); // 输出: Guest
注意事项:
||=
的优先级低于逻辑或运算符||
,所以要注意添加括号来改变运算顺序。- 记住
||=
是基于假值进行判断的,false
、null
、undefined
、0
、""
、NaN
都会被认为是假值。
三、??=
(空值合并赋值)—— “真正”的备胎
??=
运算符,是 ||=
的一个更严格的版本。 它的含义是“只有当左边的值是 null
或者 undefined
时,才将右边的值赋给左边”。 它的完整形式是:
a ??= b;
这等价于:
a = a ?? b;
再等价于:
if (a === null || a === undefined) {
a = b;
}
??=
同样具有“短路”特性。 只有当 a
是 null
或者 undefined
时,才会执行赋值操作。 如果 a
是其他任何值(包括 false
、0
、""
),那么 a
的值保持不变,不会执行 b
的求值。
示例 1:区分 null
和 false
假设你从某个地方获取一个配置项 enabled
,它可能的值是 true
、false
、null
或 undefined
。 你希望只有在 enabled
是 null
或 undefined
时,才给它设置一个默认值 true
。
let enabled = null; // 假设 enabled 可能为 null
if (enabled === null || enabled === undefined) {
enabled = true;
}
console.log(enabled); // 输出: true
使用 ??=
:
let enabled = null;
enabled ??= true;
console.log(enabled); // 输出: true
如果 enabled
一开始是 false
,那么 enabled
的值会保持不变。 这是 ??=
和 ||=
的一个关键区别。
示例 2:处理可选属性
假设你有一个对象 options
,它可能包含一个可选的属性 timeout
。 你想确保 timeout
属性总有一个值,如果没有提供,就使用默认值 1000。
let options = {}; // options 可能没有 timeout 属性
if (options.timeout === null || options.timeout === undefined) {
options.timeout = 1000;
}
console.log(options.timeout); // 输出: 1000
使用 ??=
:
let options = {};
options.timeout ??= 1000;
console.log(options.timeout); // 输出: 1000
注意事项:
??=
的优先级低于空值合并运算符??
,所以要注意添加括号来改变运算顺序。??=
只会判断null
和undefined
,其他任何值都会被认为是有效值。 这是它与||=
的本质区别。
四、 总结与对比
为了方便大家理解,我把这三个运算符的特性总结成一个表格:
运算符 | 含义 | 短路特性 | 适用场景 |
---|---|---|---|
&&= |
如果左边的值是真值,那么就将右边的值赋给左边。 | 只有当左边的值是真值时,才会执行赋值操作。 | 条件赋值,确保对象存在后再添加属性,优化条件更新。 |
||= |
如果左边的值是假值,那么就将右边的值赋给左边。 | 只有当左边的值是假值时,才会执行赋值操作。 false 、null 、undefined 、0 、"" 、NaN 都会被认为是假值。 |
提供默认值,初始化变量。 |
??= |
只有当左边的值是 null 或者 undefined 时,才将右边的值赋给左边。 |
只有当左边的值是 null 或者 undefined 时,才会执行赋值操作。 |
区分 null 和 false ,处理可选属性。 |
选择哪个运算符?
- 如果你想在某个条件成立时才赋值,使用
&&=
。 - 如果你想提供一个默认值,但任何真值都应该保留,使用
||=
。 - 如果你想提供一个默认值,但只有
null
或undefined
应该被替换,使用??=
。
五、 实际应用案例
案例 1:React 组件中的默认 Props
在 React 组件中,我们经常需要为 props 提供默认值。 使用 ??=
可以很方便地实现:
function MyComponent(props) {
const { name, age } = props;
const defaultName = "Guest";
const defaultAge = 18;
const finalName = name ?? defaultName;
const finalAge = age ?? defaultAge;
return (
<div>
Hello, {finalName}! You are {finalAge} years old.
</div>
);
}
// 或者更简洁的方式
function MyComponent({ name ??= "Guest", age ??= 18 }) {
return (
<div>
Hello, {name}! You are {age} years old.
</div>
);
}
export default MyComponent;
案例 2:处理 API 返回的数据
假设你从 API 获取到一个用户对象,但某些字段可能为空。 使用 ??=
可以避免程序出错:
async function getUser() {
const response = await fetch("/api/user");
const user = await response.json();
const username = user.name ?? "Unknown";
const email = user.email ?? "N/A"; // 如果email为null或者undefined,就显示"N/A"
console.log(`Username: ${username}, Email: ${email}`);
}
getUser();
案例 3:缓存计算结果
如果你有一个计算量很大的函数,你可以使用 &&=
来缓存计算结果,避免重复计算:
let cachedResult = null;
function expensiveCalculation() {
console.log("Performing expensive calculation...");
// 模拟一个耗时的计算
return Math.random() * 1000;
}
function getResult() {
cachedResult &&= expensiveCalculation(); // 如果 cachedResult 已经有值,就保持不变。 否则就计算结果。
return cachedResult;
}
console.log(getResult()); // 第一次调用,会执行 expensiveCalculation()
console.log(getResult()); // 第二次调用,直接返回 cachedResult,不会执行 expensiveCalculation()
六、 注意事项和陷阱
- 优先级问题: 一定要注意这三个运算符的优先级。 如果你不确定,最好加上括号。
- 类型转换: JavaScript 是一门动态类型语言,所以要小心类型转换带来的意外结果。 例如,
0 ||= "default"
会将0
替换为 "default",因为0
是假值。 - 可读性: 虽然这些运算符可以简化代码,但过度使用可能会降低代码的可读性。 请根据实际情况权衡利弊。
- 浏览器兼容性: 虽然目前主流浏览器都已经支持这些运算符,但为了兼容旧版本的浏览器,可能需要使用 Babel 等工具进行转换。
七、 总结
&&=
, ||=
, ??=
这三个逻辑赋值运算符是 JavaScript 提供给我们的“偷懒神器”。 它们可以简化条件赋值语句,提高代码的效率和可读性(前提是理解了它们的用法)。 掌握了这些运算符,你就可以写出更简洁、更优雅的 JavaScript 代码,成为一个更优秀的程序员(至少看起来是这样)。
希望今天的讲座对大家有所帮助。 记住,编程的乐趣在于不断学习和探索。 继续加油,各位!