你以为JavaScript已经玩透了?这些隐藏的新API能让你的代码质量提升一个档次!
前言:为什么你要关注这些API?
在日常开发中,我们往往只使用最基础的JavaScript特性,却忽略了语言本身提供的强大工具。这些新API不仅能让代码更简洁高效,还能解决很多传统方法的痛点。今天我就带你解锁20个被严重低估的JavaScript隐藏技巧!
1. Intl API - 国际化处理的终极方案
// 传统方式 vs Intl API
const number = 1234567.89;
// 传统方式(复杂且容易出错)
const formattedOld = number.toLocaleString('zh-CN', {
style: 'currency',
currency: 'CNY'
});
// Intl API(简洁专业)
const formatter = new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
});
console.log(formatter.format(number)); // ¥1,234,567.89
// 日期格式化
const dateFormatter = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long'
});
console.log(dateFormatter.format(new Date())); // 2024年1月15日星期一
应用场景:多语言网站、金融应用、国际化产品
2. Intersection Observer - 懒加载和无限滚动的神器
// 监听元素进入视口
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('元素进入视口:', entry.target);
// 加载图片或执行动画
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.1, // 10%可见时触发
rootMargin: '50px' // 提前50px检测
});
// 监听所有需要懒加载的图片
document.querySelectorAll('.lazy-image').forEach(img => {
observer.observe(img);
});
3. Optional Chaining (?.) - 告别繁琐的空值检查
// 传统嵌套检查
const userName = user && user.profile && user.profile.name;
// Optional Chaining
const userName = user?.profile?.name;
// 函数调用安全
const result = obj.someMethod?.();
// 数组访问安全
const firstItem = arr?.[0];
// 配合Nullish Coalescing提供默认值
const name = user?.profile?.name ?? '匿名用户';
4. Promise.allSettled - 处理多个异步请求的最佳实践
// 传统Promise.all会在一个失败时全部失败
// Promise.allSettled会等待所有Promise完成
const promises = [
fetch('/api/user'),
fetch('/api/posts'),
fetch('/api/comments')
];
const results = await Promise.allSettled(promises);
const successfulResults = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value);
const errors = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
5. Dynamic Import - 按需加载代码
// 传统导入(全部加载)
// import { heavyFunction } from './heavy-module';
// 动态导入(按需加载)
button.addEventListener('click', async () => {
const { heavyFunction } = await import('./heavy-module');
heavyFunction();
});
// 配合webpack的魔法注释
const module = await import(
/* webpackChunkName: "my-chunk" */
/* webpackPrefetch: true */
'./module'
);
6. Resize Observer - 响应式设计的完美搭档
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
console.log(`元素尺寸变化: ${width}x${height}`);
if (width < 768) {
// 移动端布局
entry.target.classList.add('mobile');
} else {
entry.target.classList.remove('mobile');
}
}
});
// 监听元素尺寸变化
resizeObserver.observe(document.getElementById('responsive-element'));
7. Object.fromEntries - 数组和对象的无缝转换
// 对象转数组再转回对象
const obj = { a: 1, b: 2, c: 3 };
// 传统方式
const entries = Object.entries(obj);
const newObj = entries.reduce((acc, [key, value]) => {
acc[key] = value * 2;
return acc;
}, {});
// 使用Object.fromEntries
const newObj = Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, value * 2])
);
// URL查询参数转换
const params = new URLSearchParams('name=张三&age=25');
const data = Object.fromEntries(params);
// { name: "张三", age: "25" }
8. BigInt - 处理超大数字
// 传统数字有精度限制
console.log(2 ** 53 === 2 ** 53 + 1); // true!
// 使用BigInt
const bigNumber = 9007199254740991n; // 后缀n表示BigInt
const biggerNumber = bigNumber + 1n;
console.log(bigNumber === biggerNumber); // false
// 金融计算、ID处理等场景非常有用
const transactionAmount = 12345678901234567890n;
9. String.prototype.replaceAll - 全局替换的简化
// 传统全局替换
const str = "hello world hello";
const replaced = str.replace(/hello/g, 'hi');
// 使用replaceAll
const replaced = str.replaceAll('hello', 'hi');
// 也支持正则表达式
const replaced = str.replaceAll(/hello/gi, 'hi');
10. Logical Assignment Operators - 逻辑赋值的简洁写法
// 设置默认值
let options = {};
// 传统方式
if (!options.timeout) {
options.timeout = 3000;
}
// 逻辑或赋值
options.timeout ||= 3000;
// 逻辑空赋值(只对null/undefined生效)
options.retry ??= 3;
// 逻辑与赋值
user.isAdmin &&= await checkAdminStatus();
11. Flat/FlatMap - 数组扁平化的现代解决方案
// 多维数组扁平化
const nestedArray = [1, [2, [3, [4]]], 5];
// 传统方式递归扁平化
function flatten(arr) {
return arr.reduce((acc, val) =>
Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val), []);
}
// 使用flat
const flatArray = nestedArray.flat(Infinity);
// flatMap先map再flat(1)
const numbers = [1, 2, 3];
const doubled = numbers.flatMap(x => [x, x * 2]);
// [1, 2, 2, 4, 3, 6]
12. globalThis - 统一的全局对象访问
// 不同环境的全局对象不同
// 浏览器: window
// Node.js: global
// Web Workers: self
// 传统方式
const getGlobal = () => {
if (typeof self !== 'undefined') return self;
if (typeof window !== 'undefined') return window;
if (typeof global !== 'undefined') return global;
throw new Error('无法找到全局对象');
};
// 使用globalThis
console.log(globalThis === window); // 在浏览器中为true
console.log(globalThis === global); // 在Node.js中为true
// 安全地设置全局变量
globalThis.MY_APP = { version: '1.0.0' };
13. Promise.any - 获取第一个成功的Promise
// 与Promise.race不同,Promise.any等待第一个成功的Promise
const promises = [
fetch('/api/primary').then(() => 'primary'),
fetch('/api/backup1').then(() => 'backup1'),
fetch('/api/backup2').then(() => 'backup2')
];
try {
const firstSuccess = await Promise.any(promises);
console.log('第一个成功的API:', firstSuccess);
} catch (error) {
// 所有Promise都失败时进入这里
console.error('所有API请求都失败了', error.errors);
}
14. Private Class Fields - 真正的私有属性
class User {
// 公有属性
name = '';
// 私有属性(以#开头)
#password = '';
#token = null;
constructor(name, password) {
this.name = name;
this.#password = password;
}
// 私有方法
#validatePassword(password) {
return this.#password === password;
}
login(password) {
if (this.#validatePassword(password)) {
this.#token = generateToken();
return true;
}
return false;
}
// 外部无法访问私有属性
// user.#password // SyntaxError
}
15. FinalizationRegistry - 对象垃圾回收的监听
// 监听对象何时被垃圾回收
const registry = new FinalizationRegistry(heldValue => {
console.log(`${heldValue} 被垃圾回收了`);
});
let obj = { data: '重要数据' };
// 注册监听
registry.register(obj, '我的重要对象');
// 当obj不再被引用时,回调函数会被触发
obj = null; // 可能在下一次垃圾回收时触发回调
16. Top-level await - 模块顶层的异步魔法
// 传统方式:需要在async函数内部使用await
async function init() {
const config = await fetch('/api/config');
const data = await fetch('/api/data');
// 初始化逻辑
}
init();
// Top-level await(仅在ES模块中可用)
const config = await fetch('/api/config');
const data = await fetch('/api/data');
// 直接导出异步结果
export const userData = await fetch('/api/user');
// 条件导入
const shouldLoadHeavyModule = await checkFeatureFlag();
if (shouldLoadHeavyModule) {
await import('./heavy-feature.js');
}
console.log('应用初始化完成!');
应用场景:应用初始化、配置加载、条件模块导入
17. Array.prototype.at() - 更人性化的数组索引
const array = ['a', 'b', 'c', 'd', 'e'];
// 传统正向索引
console.log(array[0]); // 'a'
console.log(array[array.length - 1]); // 'e'(需要计算)
// 传统负索引(需要自己处理)
function getAt(arr, index) {
return index >= 0 ? arr[index] : arr[arr.length + index];
}
// 使用at()方法
console.log(array.at(0)); // 'a'
console.log(array.at(-1)); // 'e'(直接支持负索引)
console.log(array.at(-2)); // 'd'
console.log(array.at(5)); // undefined(安全访问)
// 字符串也支持at()
const str = 'Hello';
console.log(str.at(-1)); // 'o'
优势:代码更简洁,避免复杂的length计算,提高可读性
18. Error.cause - 完整的错误链式传递
// 传统错误处理(丢失原始错误信息)
function processUserData(userId) {
try {
const user = getUserFromDB(userId);
return processUser(user);
} catch (error) {
throw new Error(`处理用户数据失败: ${error.message}`);
// 原始错误信息丢失!
}
}
// 使用Error.cause保持完整的错误链
function processUserData(userId) {
try {
const user = getUserFromDB(userId);
return processUser(user);
} catch (error) {
throw new Error('处理用户数据失败', { cause: error });
}
}
// 捕获时可以访问完整的错误链
try {
processUserData(123);
} catch (error) {
console.error('新错误:', error.message);
console.error('根本原因:', error.cause.message);
console.error('完整堆栈:', error.cause.stack);
}
// 自定义错误类
class ValidationError extends Error {
constructor(message, cause) {
super(message);
this.cause = cause;
this.name = 'ValidationError';
}
}
19. Object.hasOwn() - 更安全的属性检查
const obj = { name: '张三', age: 25 };
// 传统方式的问题
console.log(obj.hasOwnProperty('name')); // true
console.log('toString' in obj); // true(原型链上的)
// hasOwnProperty的安全问题
const obj2 = Object.create(null);
obj2.name = '李四';
// obj2.hasOwnProperty('name'); // TypeError!
// 使用Object.hasOwn()
console.log(Object.hasOwn(obj, 'name')); // true
console.log(Object.hasOwn(obj, 'toString')); // false
console.log(Object.hasOwn(obj2, 'name')); // true(安全!)
// 实际应用:过滤对象自有属性
function getOwnProperties(obj) {
return Object.keys(obj).filter(key => Object.hasOwn(obj, key));
}
// 或者使用Object.entries
const ownProperties = Object.entries(obj)
.filter(([key]) => Object.hasOwn(obj, key))
.reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
20. Temporal API - 下一代日期时间处理
// 当前Date对象的问题
const now = new Date();
console.log(now.getMonth()); // 0-11(反人类)
console.log(now.getYear()); // 124(1900+124)
console.log(now.toISOString()); // 时区问题
// Temporal API(提案阶段,但非常值得期待)
// 安装polyfill: npm install @js-temporal/polyfill
import { Temporal } from '@js-temporal/polyfill';
// 创建时间
const plainDate = Temporal.PlainDate.from('2024-01-15');
const plainTime = Temporal.PlainTime.from('14:30:00');
const zonedDateTime = Temporal.ZonedDateTime.from('2024-01-15T14:30:00+08:00[Asia/Shanghai]');
// 人性化的操作
console.log(plainDate.month); // 1(1-12,符合直觉)
console.log(plainDate.dayOfWeek); // 1(星期一)
// 时间运算
const nextWeek = plainDate.add({ days: 7 });
const duration = nextWeek.since(plainDate);
console.log(duration.days); // 7
// 时区处理
const shanghaiTime = zonedDateTime.withTimeZone('Asia/Shanghai');
const newYorkTime = zonedDateTime.withTimeZone('America/New_York');
// 格式化
console.log(plainDate.toString()); // "2024-01-15"
console.log(plainDate.toLocaleString('zh-CN')); // "2024年1月15日"
进阶技巧:组合使用这些API
// 组合示例:安全的异步数据处理
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
// 使用Object.hasOwn进行安全的数据验证
if (!Object.hasOwn(data, 'user') || !Object.hasOwn(data.user, 'profile')) {
throw new Error('无效的用户数据结构', {
cause: new Error('缺少必要字段')
});
}
// 使用at()安全访问数组数据
const recentPosts = data.user.posts?.at(-5) ?? [];
return {
user: data.user,
recentPosts,
fetchedAt: Temporal.Now.plainDateTimeISO()
};
} catch (error) {
throw new Error(`获取用户数据失败: ${userId}`, { cause: error });
}
}
学习建议
- 实践为主:在个人项目或demo中尝试这些新特性
- 关注提案:关注TC39提案进程,了解JavaScript的未来发展方向
- 渐进式采用:根据项目需求和团队技术栈选择合适的特性
- 工具链配置:合理配置Babel、TypeScript等工具支持新特性
总结
这20个JavaScript隐藏API覆盖了现代开发的各个场景,从异步处理到数据操作,从错误处理到日期时间。掌握它们不仅能提升你的开发效率,更能让你写出更健壮、更可维护的代码。
技术成长的秘诀:不在于知道多少,而在于能否在合适的场景运用合适的工具!
这20个隐藏技巧你掌握了几个?在评论区分享你的使用经验吧!