现代 JavaScript 开发:12个实用技巧让你写出更简洁的代码
你是不是还在用 for 循环遍历数组?还在写一堆 if-else 判断空值?还在 setTimeout 里套 setTimeout 处理异步?
说实话,2026 年了,有些 JS 代码真的可以不用再那么写了。那些曾经要写 10 行才能搞定的逻辑,现在 2 行就够了。
这篇文章整理了最近在项目里真正用上的 12 个现代 JS 技巧,每条都有新旧对比代码,复制粘贴就能用。
01 可选链操作符 ?.:告别嵌套判空
痛点:访问深层对象属性时,随时可能报错 Cannot read properties of undefined
老写法
// 每一层都要判断,写到手抽筋
const city = user && user.address && user.address.city;新写法
const city = user?.address?.city;
// 任意一层为 null/undefined,直接返回 undefined,不报错
// 可选链调用方法
user?.getName?.();
// 可选链访问数组
const first = list?.[0]?.title;一行解决,链路再深也不怕。
02 空值合并运算符 ??:精准处理 null/undefined
痛点:用 || 兜底时,0、false、"" 这些合法值会被意外覆盖
// 老写法:data.count 为 0 时,结果错误
const count = data.count || 0;
// 新写法:只有 null 和 undefined 才触发默认值
const count = data.count ?? 0;
// 搭配可选链,更香
const name = user?.profile?.name ?? '匿名用户';03 逻辑赋值运算符:三合一简写
这三个操作符很多人不知道,但用了就回不去:
a ||= 'default'; // 左侧为 falsy 时才赋值
a &&= transform(a); // 左侧为 truthy 时才赋值
a ??= 'fallback'; // 左侧为 null/undefined 时才赋值
// 实际场景对比
// 老写法
if (config.timeout === null || config.timeout === undefined) {
config.timeout = 5000;
}
// 新写法
config.timeout ??= 5000;04 Array.at():优雅访问末尾元素
痛点:取数组最后一个元素时,arr[arr.length - 1] 又臭又长
// 老写法
const last = arr[arr.length - 1];
// 新写法
const last = arr.at(-1);
const lastChar = 'Hello'.at(-1); // 字符串同样适用,返回 'o'05 Object.hasOwn():替代 hasOwnProperty
// 老写法(有坑)
if (obj.hasOwnProperty('key')) { ... }
// 更安全的老写法(太啰嗦)
if (Object.prototype.hasOwnProperty.call(obj, 'key')) { ... }
// 新写法
if (Object.hasOwn(obj, 'key')) { ... }06 structuredClone():原生深拷贝,终于来了
痛点:JSON.parse(JSON.stringify()) 会丢失函数和 Date,引入 lodash 又嫌重
// 老写法(Date 变字符串,函数丢失)
const copy = JSON.parse(JSON.stringify(original));
// 新写法(原生支持 Date/Map/Set/RegExp,还能处理循环引用)
const copy = structuredClone(original);2026 年还在用 JSON 深拷贝的,该更新知识库了。
07 Promise.allSettled():不再因一个失败全军覆没
痛点:Promise.all 一旦有一个 reject,整个链就断了,其他结果全部丢失
// 每个结果独立返回,不互相影响
const results = await Promise.allSettled([fetchA(), fetchB(), fetchC()]);
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功', result.value);
} else {
console.log('失败', result.reason);
}
});08 顶层 await:不再包一层 async 函数
// 老写法
(async () => {
const data = await fetch('/api/config').then(r => r.json());
})();
// 新写法(ES Module 顶层直接用)
const data = await fetch('/api/config').then(r => r.json());09 解构 + 默认值:一行处理复杂取值
// 解构同时设默认值
const { name = '匿名', age = 18, role = 'user' } = userInfo;
// 解构并重命名
const { data: userList, total: count } = response;
// 嵌套解构
const { address: { city, street = '未知街道' } } = user;
// 忽略部分数组元素
const [first, , third] = [1, 2, 3];10 Array.flatMap():map + flat 一步到位
// 老写法
const result = arr.map(fn).flat();
// 新写法
const result = arr.flatMap(fn);
// 把句子数组拆成单词数组
const words = ['hello world', 'foo bar'].flatMap(s => s.split(' '));
// ['hello', 'world', 'foo', 'bar']11 Object.entries() + fromEntries():对象转换神器
// 一行转换对象所有值
const result = Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, v.toUpperCase()])
);
// 优雅删除不需要的字段
const filtered = Object.fromEntries(
Object.entries(obj).filter(([k]) => k !== 'password')
);12 Error Cause:更清晰的错误链路
痛点:捕获底层错误后重新 throw,原始错误信息丢失,排查问题一脸懵
// 老写法(底层原因吞掉了)
throw new Error('获取用户信息失败');
// 新写法(保留完整错误链路)
throw new Error('获取用户信息失败', { cause: e });
// 捕获时查看
console.log(e.message); // '获取用户信息失败'
console.log(e.cause); // 原始错误对象,排查神器四组黄金组合,直接拿走用
组合一:安全取值链
const value = obj?.deeply?.nested?.value ?? '默认值';组合二:并发请求 + 容错
const results = await Promise.allSettled(ids.map(id => fetchItem(id)));
const items = results.filter(r => r.status === 'fulfilled').map(r => r.value);组合三:对象字段过滤 + 转换
const clean = Object.fromEntries(
Object.entries(raw)
.filter(([k]) => allowedKeys.includes(k))
.map(([k, v]) => [k, String(v).trim()])
);组合四:深拷贝 + 安全修改
const draft = structuredClone(original);
draft.user?.settings ??= {};
draft.user.settings.theme = 'dark';写在最后
现代 JavaScript 的进化速度,已经超过了大多数人学习它的速度。这 12 个技巧不是什么新奇花哨的东西,而是已经被广泛支持、可以放心用在生产环境的成熟特性。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!