共计 2338 个字符,预计需要花费 6 分钟才能阅读完成。
为什么这些算法问题值得关注
在日常 JavaScript 开发中,算法优化和特殊场景处理是提升代码质量的关键。无论是面试高频题两数之和,还是实际业务中频繁出现的防抖需求,亦或是数据操作时的深拷贝痛点,都需要我们掌握系统化的解决方案。本文将从四个典型场景出发,提供可直接落地的实现方案。

1. 两数之和:哈希表优化
问题背景
给定一个整数数组 nums 和一个目标值 target,需要在数组中找出和为目标值的两个整数。
暴力解法(O(n²))
双循环遍历所有组合,最直观但效率最低:
function twoSumNaive(nums, target) {for (let i = 0; i < nums.length; i++) {for (let j = i + 1; j < nums.length; j++) {if (nums[i] + nums[j] === target) {return [i, j];
}
}
}
return [];}
哈希表优化(O(n))
通过空间换时间,存储遍历过的数值及其索引:
function twoSum(nums, target) {const map = new Map();
for (let i = 0; i < nums.length; i++) {const complement = target - nums[i];
if (map.has(complement)) {return [map.get(complement), i];
}
map.set(nums[i], i); // 存储当前值和索引
}
return [];}
性能对比
在 10,000 个元素的数组测试中:
– 暴力解法:~250ms
– 哈希表解法:~5ms
2. 防抖函数:控制执行频率
核心原理
在事件频繁触发时,只执行最后一次或第一次调用。
基础实现
function debounce(fn, delay) {
let timer = null;
return function(...args) {clearTimeout(timer); // 清除之前的调用
timer = setTimeout(() => {fn.apply(this, args);
}, delay);
};
}
立即执行版
function debounceImmediate(fn, delay, immediate = true) {
let timer = null;
return function(...args) {if (timer) clearTimeout(timer);
if (immediate && !timer) {fn.apply(this, args);
}
timer = setTimeout(() => {
timer = null;
if (!immediate) fn.apply(this, args);
}, delay);
};
}
典型应用场景
- 搜索框输入联想
- 窗口 resize 事件
- 按钮频繁点击防护
3. 深拷贝:处理复杂对象
JSON 方法的局限性
const obj = {a: 1};
obj.self = obj;
JSON.parse(JSON.stringify(obj)); // 报错:循环引用
完整实现方案
function deepClone(target, map = new WeakMap()) {
// 处理循环引用
if (map.get(target)) return map.get(target);
// 处理 Symbol 类型
if (typeof target === 'symbol') return Symbol.for(target.description);
// 基础类型直接返回
if (typeof target !== 'object' || target === null) {return target;}
const cloneTarget = Array.isArray(target) ? [] : {};
map.set(target, cloneTarget);
// 处理 Map/Set 等特殊对象(篇幅限制省略)// 遍历属性
Reflect.ownKeys(target).forEach(key => {cloneTarget[key] = deepClone(target[key], map);
});
return cloneTarget;
}
4. 数组扁平化与去重
递归扁平化
function flattenDeep(arr) {return arr.reduce((acc, val) =>
Array.isArray(val)
? acc.concat(flattenDeep(val))
: acc.concat(val),
[]);
}
迭代方案(ES6)
function flatten(arr) {while (arr.some(Array.isArray)) {arr = [].concat(...arr);
}
return arr;
}
最优去重方案
const unique = arr => [...new Set(arr)];
// 或处理对象数组
const uniqueBy = (arr, key) => {const seen = new Map();
return arr.filter(item => {const k = key ? item[key] : JSON.stringify(item);
return seen.has(k) ? false : seen.set(k, true);
});
};
生产环境注意事项
- 内存泄漏风险
- 防抖函数中注意清除定时器
-
深拷贝时 WeakMap 自动释放内存
-
极端 case 测试
- 空数组 / 超大数组处理
- 特殊类型如 NaN、Infinity
-
循环引用深度超过调用栈
-
性能监控
- 使用 console.time/performance API
- 避免在渲染关键路径执行复杂操作
延伸思考
- 如何实现支持取消的防抖函数?
- WeakMap 在深拷贝中相比 Map 有何优势?
- 对于超大规模数组,如何优化扁平化性能?
希望这些方案能帮助你在实际开发中写出更健壮的代码。记住,理解原理比记住实现更重要!
正文完
发表至: 未分类
近三天内
