JavaScript 一些小特性:让你的代码更优雅高效
作为前端开发者,掌握这些 JavaScript 高级特性能大幅提升你的开发效率和代码质量。本文整理了日常开发中最实用的技巧,每个都配有实战代码示例。
1. 解构赋值 (Destructuring)
解构赋值让你从数组或对象中快速提取值,告别繁琐的逐个赋值。
对象解构
// 基础用法
const user = { name: '张三', age: 25, city: '北京' };
const { name, age } = user;
console.log(name, age); // 张三 25
// 重命名 + 默认值
const { name: userName, gender = '未知' } = user;
console.log(userName, gender); // 张三 未知
// 嵌套解构
const response = {
data: {
user: { id: 1, name: 'John' },
token: 'abc123'
}
};
const { data: { user: { id }, token } } = response;
console.log(id, token); // 1 abc123
数组解构
// 交换变量(不需要临时变量)
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
// 跳过某些元素
const [first, , third] = [1, 2, 3];
console.log(first, third); // 1 3
// 剩余元素
const [head, ...tail] = [1, 2, 3, 4];
console.log(head, tail); // 1 [2, 3, 4]
2. 展开运算符与剩余参数 (Spread & Rest)
展开运算符
// 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
// 浅拷贝对象(常用于 React/Vue 状态更新)
const original = { a: 1, b: 2 };
const copied = { ...original, c: 3 }; // { a: 1, b: 2, c: 3 }
// 合并对象(后面的属性覆盖前面的)
const defaults = { theme: 'light', lang: 'zh' };
const userConfig = { theme: 'dark' };
const config = { ...defaults, ...userConfig }; // { theme: 'dark', lang: 'zh' }
剩余参数
// 函数接收不定数量参数
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
// 解构时收集剩余属性
const { id, ...rest } = { id: 1, name: 'test', value: 100 };
console.log(rest); // { name: 'test', value: 100 }
3. 可选链与空值合并 (?. 和 ??)
这两个操作符是处理 null/undefined 的神器,能大幅减少代码中的条件判断。
可选链操作符 (?.)
const user = {
profile: {
address: {
city: '上海'
}
}
};
// 传统写法(繁琐且易出错)
const city = user && user.profile && user.profile.address && user.profile.address.city;
// 可选链写法(简洁优雅)
const city = user?.profile?.address?.city; // '上海'
const country = user?.profile?.address?.country; // undefined(不会报错)
// 用于方法调用
const result = user?.getName?.(); // 如果 getName 不存在,返回 undefined
// 用于数组索引
const firstItem = arr?.[0];
空值合并操作符 (??)
// ?? 只在值为 null 或 undefined 时使用默认值
const value1 = null ?? '默认值'; // '默认值'
const value2 = undefined ?? '默认值'; // '默认值'
const value3 = 0 ?? '默认值'; // 0(0 是有效值)
const value4 = '' ?? '默认值'; // ''(空字符串是有效值)
// 对比 || 运算符(会把 0、''、false 也当作假值)
const value5 = 0 || '默认值'; // '默认值'(可能不是你想要的)
// 实际应用:配置项处理
function createConfig(options) {
return {
timeout: options?.timeout ?? 3000,
retries: options?.retries ?? 3,
debug: options?.debug ?? false
};
}
4. 数组高阶方法
这些方法让你告别 for 循环,写出更声明式、更易读的代码。
map - 数据转换
const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 }
];
// 提取所有用户名
const names = users.map(user => user.name); // ['Alice', 'Bob']
// 数据格式转换
const options = users.map(({ id, name }) => ({
value: id,
label: name
}));
// [{ value: 1, label: 'Alice' }, { value: 2, label: 'Bob' }]
filter - 数据过滤
const numbers = [1, 2, 3, 4, 5, 6];
// 过滤偶数
const evens = numbers.filter(n => n % 2 === 0); // [2, 4, 6]
// 过滤空值
const items = ['a', '', null, 'b', undefined, 'c'];
const validItems = items.filter(Boolean); // ['a', 'b', 'c']
reduce - 数据聚合
// 求和
const sum = [1, 2, 3, 4].reduce((acc, num) => acc + num, 0); // 10
// 数组转对象(非常实用!)
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
const userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
// { 1: { id: 1, name: 'Alice' }, 2: { id: 2, name: 'Bob' } }
// 数组分组
const items = [
{ type: 'fruit', name: '苹果' },
{ type: 'vegetable', name: '白菜' },
{ type: 'fruit', name: '香蕉' }
];
const grouped = items.reduce((acc, item) => {
const key = item.type;
acc[key] = acc[key] || [];
acc[key].push(item);
return acc;
}, {});
// { fruit: [...], vegetable: [...] }
find & findIndex - 查找元素
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
const user = users.find(u => u.id === 2); // { id: 2, name: 'Bob' }
const index = users.findIndex(u => u.id === 2); // 1
some & every - 条件判断
const numbers = [1, 2, 3, 4, 5];
numbers.some(n => n > 4); // true(至少有一个大于4)
numbers.every(n => n > 0); // true(所有都大于0)
flatMap - 扁平化映射
const sentences = ['Hello World', 'Good Morning'];
const words = sentences.flatMap(s => s.split(' '));
// ['Hello', 'World', 'Good', 'Morning']
5. Promise 与 async/await
Promise 基础
// 创建 Promise
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ data: 'success' });
}, 1000);
});
};
// Promise 链式调用
fetchData()
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('完成'));
async/await - 同步风格写异步
// 基础用法
async function getData() {
try {
const response = await fetch('/api/users');
const data = await response.json();
return data;
} catch (error) {
console.error('请求失败:', error);
throw error;
}
}
// 并行请求(重要!避免串行等待)
async function fetchAll() {
// ❌ 串行执行,慢
const user = await fetchUser();
const posts = await fetchPosts();
// ✅ 并行执行,快
const [user, posts] = await Promise.all([
fetchUser(),
fetchPosts()
]);
}
// Promise.allSettled - 等待所有完成(不管成功失败)
const results = await Promise.allSettled([
fetch('/api/a'),
fetch('/api/b'),
fetch('/api/c')
]);
// 每个结果包含 status: 'fulfilled' | 'rejected'
6. 模板字符串高级用法
多行字符串与表达式
const name = '张三';
const score = 95;
// 多行 + 表达式
const message = `
学生: ${name}
成绩: ${score}
等级: ${score >= 90 ? '优秀' : '良好'}
`;
// 嵌套模板字符串
const items = ['苹果', '香蕉', '橙子'];
const html = `
<ul>
${items.map(item => `<li>${item}</li>`).join('')}
</ul>
`;
标签模板(Tagged Templates)
// 自定义模板处理函数
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] ? `<mark>${values[i]}</mark>` : '';
return result + str + value;
}, '');
}
const name = '张三';
const city = '北京';
const result = highlight`${name} 来自 ${city}`;
// '<mark>张三</mark> 来自 <mark>北京</mark>'
7. 对象属性简写与计算属性名
// 属性简写
const name = '张三';
const age = 25;
const user = { name, age }; // 等同于 { name: name, age: age }
// 方法简写
const obj = {
sayHello() {
console.log('Hello');
}
};
// 计算属性名(动态 key)
const key = 'dynamicKey';
const obj = {
[key]: 'value',
[`prefix_${key}`]: 'prefixed value',
[`method_${key}`]() {
return 'dynamic method';
}
};
console.log(obj.dynamicKey); // 'value'
// 实际应用:动态设置状态
function updateState(key, value) {
setState(prev => ({
...prev,
[key]: value
}));
}
8. 短路求值与逻辑赋值
短路求值
// && 短路:第一个为真才执行第二个
const result = condition && doSomething();
isLoggedIn && showDashboard();
// || 短路:第一个为假才执行第二个
const name = userName || '匿名用户';
// 条件渲染(React 常用)
{isLoading && <Spinner />}
{error || <SuccessMessage />}
逻辑赋值运算符(ES2021)
// ||= 空值时赋值
let name = '';
name ||= '默认名称'; // name = '默认名称'
// &&= 有值时赋值
let user = { name: '张三' };
user &&= { ...user, updated: true };
// ??= null/undefined 时赋值
let config = null;
config ??= { theme: 'dark' };
// 实际应用:懒初始化
class Cache {
data = null;
getData() {
return this.data ??= this.loadData();
}
}
9. 对象与数组新方法
Object 方法
// Object.entries - 对象转键值对数组
const obj = { a: 1, b: 2, c: 3 };
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
// Object.fromEntries - 键值对数组转对象
const entries = [['a', 1], ['b', 2]];
const obj = Object.fromEntries(entries); // { a: 1, b: 2 }
// 实用技巧:过滤对象属性
const original = { a: 1, b: 2, c: 3, d: 4 };
const filtered = Object.fromEntries(
Object.entries(original).filter(([key, value]) => value > 2)
);
// { c: 3, d: 4 }
// Object.keys / Object.values
const keys = Object.keys(obj); // ['a', 'b', 'c']
const values = Object.values(obj); // [1, 2, 3]
Array 新方法
// Array.from - 类数组转真数组
const nodeList = document.querySelectorAll('div');
const divArray = Array.from(nodeList);
// 带映射函数
const numbers = Array.from({ length: 5 }, (_, i) => i + 1);
// [1, 2, 3, 4, 5]
// Array.isArray - 判断是否为数组
Array.isArray([1, 2, 3]); // true
Array.isArray('hello'); // false
// at() - 支持负索引
const arr = [1, 2, 3, 4, 5];
arr.at(-1); // 5(最后一个元素)
arr.at(-2); // 4
10. Map 与 Set
Map - 键值对集合
const map = new Map();
// 任意类型作为 key(比普通对象强大)
const objKey = { id: 1 };
map.set(objKey, 'object value');
map.set('string', 'string value');
map.set(123, 'number value');
// 常用操作
map.get(objKey); // 'object value'
map.has('string'); // true
map.delete('string');
map.size; // 2
// 遍历
map.forEach((value, key) => {
console.log(key, value);
});
// 实际应用:缓存函数结果
const cache = new Map();
function memoizedFetch(url) {
if (cache.has(url)) {
return cache.get(url);
}
const result = fetch(url).then(r => r.json());
cache.set(url, result);
return result;
}
Set - 唯一值集合
const set = new Set([1, 2, 3, 3, 4, 4]);
console.log([...set]); // [1, 2, 3, 4](自动去重)
// 数组去重(最简洁的方式)
const unique = [...new Set(array)];
// 交集、并集、差集
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
const union = new Set([...a, ...b]); // 并集 {1, 2, 3, 4}
const intersection = new Set([...a].filter(x => b.has(x))); // 交集 {2, 3}
const difference = new Set([...a].filter(x => !b.has(x))); // 差集 {1}
11. Proxy 与 Reflect
Proxy 可以拦截对象的操作,是 Vue 3 响应式系统的核心。
const target = { name: '张三', age: 25 };
const handler = {
get(obj, prop) {
console.log(`读取属性: ${prop}`);
return Reflect.get(obj, prop);
},
set(obj, prop, value) {
console.log(`设置属性: ${prop} = ${value}`);
return Reflect.set(obj, prop, value);
}
};
const proxy = new Proxy(target, handler);
proxy.name; // 控制台: 读取属性: name
proxy.age = 26; // 控制台: 设置属性: age = 26
// 实际应用:数据验证
const validator = {
set(obj, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
return Reflect.set(obj, prop, value);
}
};
12. Class 语法与私有属性
class User {
// 私有属性(ES2022)
#password;
// 静态属性
static count = 0;
constructor(name, password) {
this.name = name;
this.#password = password;
User.count++;
}
// 私有方法
#hashPassword(pwd) {
return `hashed_${pwd}`;
}
// getter/setter
get password() {
return '******';
}
set password(value) {
this.#password = this.#hashPassword(value);
}
// 静态方法
static getCount() {
return User.count;
}
}
const user = new User('张三', '123456');
console.log(user.#password); // SyntaxError: 私有属性不可访问
console.log(User.getCount()); // 1
13. 模块化 (ES Modules)
// utils.js - 命名导出
export const formatDate = (date) => { /* ... */ };
export const formatNumber = (num) => { /* ... */ };
// user.js - 默认导出
export default class User { /* ... */ }
// main.js - 导入
import User from './user.js'; // 默认导入
import { formatDate, formatNumber } from './utils.js'; // 命名导入
import { formatDate as fd } from './utils.js'; // 重命名导入
import * as utils from './utils.js'; // 全部导入
// 动态导入(代码分割、按需加载)
const module = await import('./heavy-module.js');
// 条件导入
if (needFeature) {
const { feature } = await import('./feature.js');
feature();
}
14. 实用技巧汇总
数字分隔符
const billion = 1_000_000_000; // 更易读
const bytes = 0xFF_FF_FF_FF;
对象属性存在性检查
// 使用 in 操作符
if ('name' in user) { /* ... */ }
// 使用 hasOwnProperty
if (Object.hasOwn(user, 'name')) { /* ... */ } // ES2022
快速数组操作
// 快速创建数组
const arr = Array(5).fill(0); // [0, 0, 0, 0, 0]
const range = [...Array(5).keys()]; // [0, 1, 2, 3, 4]
// 数组最后一项
const last = arr.at(-1); // ES2022
const last = arr[arr.length - 1]; // 传统方式
对象冻结
const config = Object.freeze({
API_URL: 'https://api.example.com',
TIMEOUT: 5000
});
config.API_URL = 'new url'; // 静默失败(严格模式下报错)
总结
掌握这些 JavaScript 高级特性,能让你:
- 📝 代码更简洁 - 解构、展开运算符、箭头函数
- 🛡️ 代码更安全 - 可选链、空值合并避免空指针
- 🚀 开发更高效 - 数组高阶方法、async/await
- 🏗️ 架构更清晰 - Class、模块化、Proxy
这些特性都是现代 JavaScript 开发的标配,建议在日常开发中多加练习!
📌 文章持续更新中,欢迎评论区交流更多实用技巧!
如果觉得有帮助,请点赞👍收藏⭐关注➕,你的支持是我更新的动力!