前端常见数组分析

一、底层原理与性能陷阱

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 的快捷方式:

    css 复制代码
    Array.from({ length: 3 }, (_, i) => i * 2); // [0, 2, 4]
  • 类数组转换 ‌:可转换 argumentsNodeList 等,比 [...nodeList] 更安全(避免某些IE异常)。


二、高阶函数与函数式编程

1. ‌**flatMap 的魔法**‌

  • 一步展开+映射 ‌:比先 mapflat 更高效:

    ini 复制代码
    const arr = [1, 2, 3];
    arr.flatMap(x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]
  • 过滤空值 ‌:可替代 filter + map

    ini 复制代码
    arr.flatMap(x => x % 2 === 0 ? [] : [x * 10]); // 奇数保留并乘10

2. ‌**reduce 实现复杂逻辑**‌

  • ‌**实现 groupBy**‌:

    ini 复制代码
    const 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**‌:

    javascript 复制代码
    const 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 会截断数组且 ‌不可逆‌:

    ini 复制代码
    const arr = [1, 2, 3];
    arr.length = 1; // [1]
    arr.length = 3; // [1, empty × 2] 😨

3. ‌**new Array(n) 的坑**‌

  • 创建的数组是 ‌稀疏数组 ‌(非连续内存),map 会跳过空位:

    javascript 复制代码
    const 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]]
相关推荐
ZK_H43 分钟前
嵌入式c语言——关键字其6
c语言·开发语言·计算机网络·面试·职场和发展
GreenTea1 小时前
一文搞懂Harness Engineering与Meta-Harness
前端·人工智能·后端
fei_sun2 小时前
面经、笔试(持续更新中)
fpga开发·面试
killerbasd3 小时前
牧苏苏传 我不装了 4/7
前端·javascript·vue.js
吴声子夜歌3 小时前
ES6——二进制数组详解
前端·ecmascript·es6
码事漫谈3 小时前
手把手带你部署本地模型,让你Token自由(小白专属)
前端·后端
ZC跨境爬虫3 小时前
【爬虫实战对比】Requests vs Scrapy 笔趣阁小说爬虫,从单线程到高效并发的全方位升级
前端·爬虫·scrapy·html
爱上好庆祝3 小时前
svg图片
前端·css·学习·html·css3
橘子编程4 小时前
JavaScript与TypeScript终极指南
javascript·ubuntu·typescript
王夏奇4 小时前
python中的__all__ 具体用法
java·前端·python