一、底层原理与性能陷阱
1. **Array.prototype.sort
的诡异行为**
-
V8引擎的排序优化 :Chrome 对短数组(≤10)用插入排序,长数组用快速排序 + 插入排序混合,但 比较函数不传时会强制转为字符串比较:
css[1, 2, 10].sort(); // [1, 10, 2] 😱 [1, 2, 10].sort((a, b) => a - b); // 正确写法
-
稳定性问题 :ES2019 规定
sort
必须稳定(相同元素保持原始顺序),但旧版浏览器可能存在差异。
2. **reduce
的滥用与性能**
-
链式调用 vs reduce :多次
map/filter
链式调用可能比单次reduce
更慢,但代码更易读:ini// 链式调用(可读性高) arr.map(x => x * 2).filter(x => x > 10); // reduce 单次遍历(性能略优但难维护) arr.reduce((acc, x) => { const doubled = x * 2; if (doubled > 10) acc.push(doubled); return acc; }, []);
3. **Array.from
的隐藏技能**
-
第二个参数 :相当于
map
的快捷方式:cssArray.from({ length: 3 }, (_, i) => i * 2); // [0, 2, 4]
-
类数组转换 :可转换
arguments
、NodeList
等,比[...nodeList]
更安全(避免某些IE异常)。
二、高阶函数与函数式编程
1. **flatMap
的魔法**
-
一步展开+映射 :比先
map
后flat
更高效:iniconst arr = [1, 2, 3]; arr.flatMap(x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]
-
过滤空值 :可替代
filter
+map
:iniarr.flatMap(x => x % 2 === 0 ? [] : [x * 10]); // 奇数保留并乘10
2. **reduce
实现复杂逻辑**
-
**实现
groupBy
**:iniconst groupBy = (arr, keyFunc) => arr.reduce((acc, item) => { const key = keyFunc(item); (acc[key] || (acc[key] = [])).push(item); return acc; }, {}); groupBy(['a', 'bb', 'ccc'], str => str.length); // {1: ['a'], 2: ['bb'], 3: ['ccc']}
-
**模拟
Promise.all
**:javascriptconst asyncReduce = (arr, fn, init) => arr.reduce((promise, item) => promise.then(acc => fn(acc, item)), Promise.resolve(init));
三、手写源码实现
1. **实现 Array.prototype.map
**
ini
Array.prototype.myMap = function(callback, thisArg) {
const result = [];
for (let i = 0; i < this.length; i++) {
// 处理稀疏数组(跳过 empty 项)
if (i in this) {
result[i] = callback.call(thisArg, this[i], i, this);
}
}
return result;
};
2. 实现 Array.prototype.filter
(带异步支持)
javascript
Array.prototype.asyncFilter = async function(predicate) {
const results = await Promise.all(
this.map(async (item, index) => ({
item,
include: await predicate(item, index)
}))
);
return results.filter(r => r.include).map(r => r.item);
};
// 使用示例
await [1, 2, 3].asyncFilter(async x => {
await sleep(100);
return x % 2 === 0;
});
四、性能优化冷知识
1. **for
vs forEach
vs for...of
**
- **
for
循环最快**:但代码更冗长。 - **
forEach
无法中断**:内部用try-catch
实现,比for
慢约 30%。 - **
for...of
可迭代任意对象**:但比for
慢 50%(需调用迭代器协议)。
2. 避免修改数组长度
-
直接设置
length
会截断数组且 不可逆:iniconst arr = [1, 2, 3]; arr.length = 1; // [1] arr.length = 3; // [1, empty × 2] 😨
3. **new Array(n)
的坑**
-
创建的数组是 稀疏数组 (非连续内存),
map
会跳过空位:javascriptconst arr = new Array(3); // [empty × 3] arr.map(() => 1); // [empty × 3] // 正确初始化:Array.from({ length: 3 }, () => 1)
**五、终极挑战:实现 Lodash 的 _.chunk
**
matlab
function chunk(arr, size) {
return Array.from(
{ length: Math.ceil(arr.length / size) },
(_, i) => arr.slice(i * size, i * size + size)
);
}
// chunk([1, 2, 3, 4], 2) → [[1, 2], [3, 4]]