🔥 30 个 JavaScript 实用技巧:覆盖 80% 开发场景,效率狂飙!
作为前端开发者,JavaScript 就像我们的 “瑞士军刀”—— 看似基础,却藏着无数能提升效率的 “隐藏技能”。很多时候,我们写的代码能跑通,但不够简洁;能实现功能,但不够优雅。
本文整理了 30 个高频实用的 JS 技巧,覆盖变量声明、数组操作、字符串处理、函数优化、异步编程等 8 大场景,每个技巧都附带 “普通写法 vs 优化写法” 对比和完整代码示例,新手能快速上手,老手能查漏补缺~
一、变量声明与赋值(3 个技巧)
1. 多变量快速赋值(解构赋值进阶)
javascript
// ❌ 普通写法:重复赋值,代码冗余
let a = 1;
let b = 2;
let c = 3;
// ✅ 优化写法:数组解构快速赋值
const [a, b, c] = [1, 2, 3];
// 🌟 进阶:交换变量(无需临时变量)
[a, b] = [b, a]; // a=2, b=1
// 🌟 进阶:对象解构赋值(提取接口返回数据)
const { name, age, address = '未知' } = userInfo; // 缺省值兜底
2. 变量默认值(优雅处理 undefined)
javascript
// ❌ 普通写法:三元运算符繁琐
const username = params.username ? params.username : '游客';
// ✅ 优化写法:逻辑或运算符(简洁)
const username = params.username || '游客';
// ✅ 更严谨:空值合并运算符(仅当值为null/undefined时生效)
const username = params.username ?? '游客';
// 区别:''、0、false不会触发默认值,更符合实际场景
3. 常量声明(const 优先,let 兜底)
javascript
// ❌ 不推荐:变量不会修改却用let
let PI = 3.14159;
PI = 3.14; // 无意修改,不会报错
// ✅ 优化写法:不会修改的变量用const(只读保护)
const PI = 3.14159;
PI = 3.14; // 报错:Assignment to constant variable
二、数组操作(7 个技巧,开发高频!)
4. 数组去重(3 种场景全覆盖)
javascript
// 场景1:简单数组去重(数字/字符串)
const arr = [1, 2, 2, 3, 3, 3];
// ✅ 优化写法:Set+扩展运算符(简洁高效)
const uniqueArr = [...new Set(arr)]; // [1,2,3]
// 场景2:对象数组去重(根据id去重)
const objArr = [{id:1,name:'a'}, {id:2,name:'b'}, {id:1,name:'a'}];
// ✅ 优化写法:Map键值对去重
const uniqueObjArr = Array.from(
new Map(objArr.map(item => [item.id, item])).values()
);
// 场景3:超大数组去重(性能优先)
const bigArr = new Array(100000).fill(0).map((_,i) => i%1000);
// ✅ 优化写法:对象哈希表(O(n)时间复杂度)
const uniqueBigArr = (() => {
const map = {};
return bigArr.filter(item => !map[item] && (map[item] = true));
})();
5. 数组扁平化(多维数组转一维)
javascript
const nestedArr = [1, [2, [3, [4]]]];
// ✅ 技巧1:flat(depth)(简洁,depth为扁平化深度)
const flatArr1 = nestedArr.flat(Infinity); // [1,2,3,4](Infinity表示无限深度)
// ✅ 技巧2:递归+concat(兼容性好)
function flattenArr(arr) {
return arr.reduce((acc, item) =>
acc.concat(Array.isArray(item) ? flattenArr(item) : item), []
);
}
const flatArr2 = flattenArr(nestedArr);
6. 数组过滤 + 映射(链式调用高效处理)
javascript
const data = [
{name:'张三', age:18, score:80},
{name:'李四', age:20, score:60},
{name:'王五', age:19, score:90}
];
// 需求:筛选分数≥80的用户,只保留name和score字段
// ❌ 普通写法:两次遍历(效率低)
const filtered = data.filter(item => item.score ≥80);
const result = filtered.map(item => ({name: item.name, score: item.score}));
// ✅ 优化写法:reduce一次遍历(性能更优)
const result = data.reduce((acc, item) => {
if (item.score ≥80) {
acc.push({name: item.name, score: item.score});
}
return acc;
}, []);
7. 数组查找(按需选择高效方法)
javascript
const users = [
{id:1, name:'张三'},
{id:2, name:'李四'},
{id:3, name:'王五'}
];
// 场景1:查找第一个匹配项(返回对象)
// ✅ find()(找到即停止,效率高)
const user = users.find(item => item.id === 2); // {id:2, name:'李四'}
// 场景2:查找匹配项的索引
// ✅ findIndex()(直接返回索引,无需手动遍历)
const userIndex = users.findIndex(item => item.id === 2); // 1
// 场景3:判断数组是否包含某个值
// ✅ includes()(简洁,支持基本类型)
const hasUser = users.some(item => item.id === 2); // true(对象数组用some)
const hasNum = [1,2,3].includes(2); // true(基本类型用includes)
8. 数组排序(自定义排序规则)
javascript
const arr = [3, 1, 4, 1, 5, 9];
// ✅ 数字升序/降序
const ascArr = arr.sort((a, b) => a - b); // [1,1,3,4,5,9]
const descArr = arr.sort((a, b) => b - a); // [9,5,4,3,1,1]
// ✅ 对象数组排序(按age升序,age相同按name降序)
const userArr = [ {name:'张三', age:20}, {name:'李四', age:18}, {name:'王五', age:20}];
const sortedUserArr = userArr.sort((a, b) => {
if (a.age !== b.age) return a.age - b.age;
return b.name.localeCompare(a.name); // 中文排序用localeCompare
});
9. 数组拼接与截取(不修改原数组)
javascript
const arr1 = [1,2,3];
const arr2 = [4,5,6];
// ❌ 不推荐:push/splice会修改原数组
arr1.push(...arr2); // arr1变成[1,2,3,4,5,6]
// ✅ 优化写法:扩展运算符/concat(纯函数,不修改原数组)
const newArr = [...arr1, ...arr2]; // [1,2,3,4,5,6]
const newArr2 = arr1.concat(arr2); // 同上
// ✅ 数组截取(slice不修改原数组,splice会修改)
const arr = [1,2,3,4,5];
const subArr = arr.slice(1, 3); // [2,3](原数组不变)
10. 数组空值过滤(清理无效数据)
javascript
const dirtyArr = [1, null, undefined, '', 0, false, NaN, 2];
// ✅ 过滤所有空值(null/undefined/''/NaN)
const cleanArr = dirtyArr.filter(Boolean); // [1,0,false,2]
// 注意:0和false会被保留,若需过滤可自定义:
const strictCleanArr = dirtyArr.filter(item =>
item !== null && item !== undefined && item !== '' && !isNaN(item)
);
三、字符串处理(5 个技巧)
11. 字符串模板(优雅拼接字符串)
javascript
const name = '张三';
const age = 18;
const score = 90;
// ❌ 普通写法:字符串拼接繁琐,易出错
const info = '姓名:' + name + ',年龄:' + age + ',分数:' + score;
// ✅ 优化写法:模板字符串(支持换行和表达式)
const info = `姓名:${name},年龄:${age},分数:${score},等级:${score≥90?'优秀':'良好'}`;
// 🌟 进阶:多行字符串(无需\n换行符)
const html = `
<div class="user-card">
<h3>${name}</h3>
<p>年龄:${age}</p>
</div>
`;
12. 字符串截取(根据需求选择方法)
javascript
const str = 'Hello, JavaScript!';
// 场景1:从索引n开始截取到末尾
const sub1 = str.slice(7); // 'JavaScript!'(推荐,支持负数索引)
const sub2 = str.substring(7); // 同上,但不支持负数
// 场景2:截取前n个字符
const sub3 = str.slice(0, 5); // 'Hello'
// 场景3:从末尾截取n个字符(负数索引)
const sub4 = str.slice(-11); // 'JavaScript!'
13. 字符串去空格(前后 / 所有空格)
javascript
const str = ' Hello JavaScript ';
// ✅ 去除前后空格(常用)
const trimStr = str.trim(); // 'Hello JavaScript'
// ✅ 去除所有空格(包括中间)
const noSpaceStr = str.replace(/\s+/g, ''); // 'HelloJavaScript'
// ✅ 去除开头空格
const trimStartStr = str.trimStart(); // 'Hello JavaScript '
// ✅ 去除结尾空格
const trimEndStr = str.trimEnd(); // ' Hello JavaScript'
14. 字符串大小写转换(快速处理)
javascript
const str = 'Hello JavaScript';
// ✅ 全部大写
const upperStr = str.toUpperCase(); // 'HELLO JAVASCRIPT'
// ✅ 全部小写
const lowerStr = str.toLowerCase(); // 'hello javascript'
// ✅ 首字母大写(其余小写)
const capitalizeStr = str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
15. 字符串是否包含某个字符(简洁判断)
javascript
const str = 'Hello, JavaScript!';
// ❌ 普通写法:indexOf判断(不直观)
const hasJs = str.indexOf('JavaScript') !== -1;
// ✅ 优化写法:includes()(语义化清晰)
const hasJs = str.includes('JavaScript'); // true
// ✅ startsWith/endsWith(判断开头/结尾)
const startsWithHello = str.startsWith('Hello'); // true
const endsWithExclamation = str.endsWith('!'); // true
四、函数优化(4 个技巧)
16. 函数默认参数(避免 undefined 报错)
javascript
// ❌ 普通写法:函数内判断默认值
function calculate(a, b) {
a = a || 0;
b = b || 0;
return a + b;
}
// ✅ 优化写法:参数默认值(简洁,语义化)
function calculate(a = 0, b = 0) {
return a + b;
}
calculate(2); // 2+0=2
calculate(); // 0+0=0
17. 箭头函数(简洁写法,绑定 this)
javascript
// ❌ 普通写法:函数表达式繁琐
const add = function(a, b) {
return a + b;
};
// ✅ 优化写法:箭头函数(单表达式可省略return和{})
const add = (a, b) => a + b;
// 🌟 注意:箭头函数不绑定this,适合非方法函数
const user = {
name: '张三',
sayHi: () => {
console.log(`Hi, ${this.name}`); // this指向外部作用域,非user对象
}
};
// 方法函数仍用普通函数:
const user = {
name: '张三',
sayHi() {
console.log(`Hi, ${this.name}`); // 正确:Hi, 张三
}
};
18. 函数柯里化(复用逻辑,延迟执行)
javascript
// 需求:实现add(1)(2)(3) = 6
// ✅ 柯里化函数
function curry(fn) {
return function curried(...args) {
// 当参数数量足够时,执行原函数
if (args.length >= fn.length) {
return fn.apply(this, args);
}
// 否则返回新函数,接收剩余参数
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
};
}
// 使用:
const add = curry((a, b, c) => a + b + c);
add(1)(2)(3); // 6
add(1, 2)(3); // 6
19. 防抖与节流(性能优化必备)
javascript
// ✅ 防抖:触发后延迟n秒执行,重复触发则重新计时(适合搜索框输入)
function debounce(fn, delay = 300) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// ✅ 节流:n秒内只执行一次(适合滚动、resize事件)
function throttle(fn, interval = 500) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
// 使用示例:
const search = debounce((keyword) => {
console.log('搜索:', keyword);
}, 500);
window.addEventListener('scroll', throttle(() => {
console.log('滚动事件触发');
}, 1000));
五、对象操作(4 个技巧)
20. 对象合并(避免修改原对象)
javascript
const obj1 = {a:1, b:2};
const obj2 = {b:3, c:4};
// ❌ 普通写法:Object.assign(浅拷贝,会修改原对象)
Object.assign(obj1, obj2); // obj1变成{a:1, b:3, c:4}
// ✅ 优化写法:扩展运算符(纯函数,不修改原对象)
const mergedObj = {...obj1, ...obj2}; // {a:1, b:3, c:4}
// ✅ 深拷贝合并(对象嵌套时使用)
const deepObj1 = {a:1, b: {c:2}};
const deepObj2 = {b: {d:3}, e:4};
const deepMergedObj = JSON.parse(JSON.stringify({...deepObj1, ...deepObj2}));
// 复杂场景推荐用lodash.cloneDeep
21. 对象属性遍历(按需选择方法)
javascript
const user = {id:1, name:'张三', age:18};
// ✅ 遍历键名
const keys = Object.keys(user); // ['id','name','age']
// ✅ 遍历值
const values = Object.values(user); // [1,'张三',18]
// ✅ 遍历键值对
const entries = Object.entries(user);
// [[1,'id'], ['张三','name'], [18,'age']]
// ✅ 遍历所有属性(包括继承属性)
for (const key in user) {
if (user.hasOwnProperty(key)) { // 过滤继承属性
console.log(`${key}: ${user[key]}`);
}
}
22. 对象属性判断(简洁方法)
javascript
const user = {id:1, name:'张三', age:18};
// ❌ 普通写法:判断属性是否存在(易出错)
const hasName = user.name !== undefined; // 若属性值为undefined,会误判
// ✅ 优化写法:hasOwnProperty(判断自身属性)
const hasName = user.hasOwnProperty('name'); // true
// ✅ 更推荐:Object.prototype.hasOwnProperty.call(避免原型污染)
const hasName = Object.prototype.hasOwnProperty.call(user, 'name');
// ✅ 简单场景:in运算符(包括继承属性)
const hasToString = 'toString' in user; // true(继承自Object.prototype)
23. 对象解构赋值(提取属性高效)
javascript
const user = {
id:1,
name:'张三',
address: {
province: '广东',
city: '深圳'
}
};
// ✅ 提取顶层属性
const {id, name} = user;
// ✅ 提取嵌套属性
const {address: {city}} = user; // city='深圳'
// ✅ 重命名属性(避免变量名冲突)
const {name: username} = user; // username='张三'
// ✅ 缺省值(属性不存在时使用默认值)
const {gender = '男'} = user; // gender='男'
六、异步编程(3 个技巧)
24. Promise 并发控制(避免请求过多)
javascript
// 需求:同时请求10个接口,限制最多3个并发
const urls = new Array(10).fill(0).map((_,i) => `/api/data/${i}`);
// ✅ 并发控制函数
async function requestWithLimit(urls, limit) {
const results = [];
const executing = []; // 存储正在执行的请求
for (const url of urls) {
const promise = fetch(url).then(res => res.json());
results.push(promise);
// 当并发数达到限制,等待其中一个完成
if (promise.length >= limit) {
await Promise.race(executing);
}
// 将请求添加到执行队列,完成后移除
const execute = promise.then(() => {
executing.splice(executing.indexOf(execute), 1);
});
executing.push(execute);
}
return Promise.all(results);
}
// 使用:
requestWithLimit(urls, 3).then(data => console.log('所有请求结果:', data));
25. async/await 错误处理(优雅捕获)
javascript
// ❌ 普通写法:多个await重复try/catch(冗余)
async function fetchData() {
try {
const user = await fetchUser();
} catch (err) {
console.error('用户数据请求失败:', err);
}
try {
const products = await fetchProducts();
} catch (err) {
console.error('商品数据请求失败:', err);
}
}
// ✅ 优化写法:封装to函数,统一处理错误
function to(promise) {
return promise.then(data => [null, data]).catch(err => [err, null]);
}
async function fetchData() {
const [userErr, user] = await to(fetchUser());
if (userErr) return console.error('用户数据请求失败:', userErr);
const [productsErr, products] = await to(fetchProducts());
if (productsErr) return console.error('商品数据请求失败:', productsErr);
}
26. 异步循环(避免串行陷阱)
javascript
const ids = [1,2,3,4,5];
// ❌ 错误写法:串行执行,总耗时=5个请求之和
async function fetchSerial() {
const results = [];
for (const id of ids) {
const data = await fetch(`/api/item/${id}`);
results.push(data);
}
return results;
}
// ✅ 优化写法:并行执行,总耗时=最慢请求时间
async function fetchParallel() {
const promises = ids.map(id => fetch(`/api/item/${id}`));
const results = await Promise.all(promises);
return results;
}
七、性能优化(3 个技巧)
27. 避免频繁 DOM 操作(批量处理)
javascript
// ❌ 普通写法:频繁操作DOM,导致重绘回流
const list = document.getElementById('list');
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `列表项 ${i}`;
list.appendChild(li); // 每次append都会触发重绘
}
// ✅ 优化写法:DocumentFragment批量处理
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `列表项 ${i}`;
fragment.appendChild(li); // 先添加到文档片段,不触发重绘
}
list.appendChild(fragment); // 一次插入,只触发一次重绘
28. 缓存计算结果(避免重复计算)
javascript
// ❌ 普通写法:重复调用会重复计算(耗时)
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n-1) + fibonacci(n-2);
}
// ✅ 优化写法:闭包缓存计算结果(记忆化)
const fibonacci = (() => {
const cache = {}; // 缓存已计算结果
return function fib(n) {
if (n <= 1) return n;
if (cache[n]) return cache[n]; // 命中缓存,直接返回
cache[n] = fib(n-1) + fib(n-2); // 计算后缓存
return cache[n];
};
})();
fibonacci(10); // 第一次计算,缓存结果
fibonacci(10); // 直接从缓存获取,速度极快
29. 避免使用 eval(安全 + 性能)
javascript
const a = 1;
const b = 2;
// ❌ 不推荐:eval执行字符串,安全风险+性能差
const result = eval('a + b');
// ✅ 优化写法:使用Function构造函数(相对安全)
const result = new Function('a', 'b', 'return a + b')(a, b);
// 🌟 更推荐:直接计算(除非动态执行不可避免)
const result = a + b;
八、边界处理与调试(2 个技巧)
30. 类型判断(精准判断数据类型)
javascript
// ❌ 普通写法:typeof判断不准确(数组/对象都返回object)
typeof []; // 'object'
typeof {}; // 'object'
// ✅ 优化写法:Object.prototype.toString.call(精准)
function getType(data) {
return Object.prototype.toString.call(data).slice(8, -1).toLowerCase();
}
getType([]); // 'array'
getType({}); // 'object'
getType(''); // 'string'
getType(123); // 'number'
getType(null); // 'null'
getType(undefined); // 'undefined'
getType(Symbol('a')); // 'symbol'
31. 调试技巧(console 高级用法)
javascript
const user = {id:1, name:'张三', age:18};
const arr = [1,2,3,4,5];
// ✅ 1. 打印对象/数组(带展开功能)
console.log('用户信息:', user);
console.table(arr); // 表格形式打印数组(更直观)
// ✅ 2. 打印带标签的日志(区分不同日志)
console.log('%c用户信息', 'color: blue; font-size: 16px;', user);
// ✅ 3. 计时功能(测试代码执行时间)
console.time('循环耗时');
for (let i = 0; i < 100000; i++) {}
console.timeEnd('循环耗时'); // 输出:循环耗时: 0.5ms
// ✅ 4. 断言功能(条件不满足时报错)
console.assert(user.age >= 18, '用户年龄必须≥18');
结语:技巧是手段,优雅是目的 🚀
JavaScript 的这些技巧,本质上都是为了让代码更简洁、更高效、更易维护。但记住:没有最好的技巧,只有最适合场景的技巧。
比如,箭头函数虽然简洁,但不能用于对象方法;扩展运算符虽然方便,但深拷贝时要注意嵌套对象。在实际开发中,要根据业务场景选择合适的写法,而不是盲目追求 “炫技”。
希望这 30 个技巧能帮你在日常开发中少写冗余代码,提升效率~ 你还有哪些珍藏的 JS 技巧?欢迎在评论区分享,点赞最高的技巧会被我补充到文章中!