前端常见数组分析

一、底层原理与性能陷阱

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]]
相关推荐
Mr.Jessy1 天前
JavaScript高级:构造函数与原型
开发语言·前端·javascript·学习·ecmascript
白兰地空瓶1 天前
🚀你以为你在写 React?其实你在“搭一套前端操作系统”
前端·react.js
爱上妖精的尾巴1 天前
6-4 WPS JS宏 不重复随机取值应用
开发语言·前端·javascript
似水流年QC1 天前
深入探索 WebHID:Web 标准下的硬件交互实现
前端·交互·webhid
陪我去看海1 天前
测试 mcp
前端
speedoooo1 天前
在现有App里嵌入一个AI协作者
前端·ui·小程序·前端框架·web app
全栈胖叔叔-瓜州1 天前
关于llamasharp 大模型多轮对话,模型对话无法终止,或者输出角色标识User:,或者System等角色标识问题。
前端·人工智能
三七吃山漆1 天前
攻防世界——wife_wife
前端·javascript·web安全·网络安全·ctf
用户47949283569151 天前
面试官问"try-catch影响性能吗",我用数据打脸
前端·javascript·面试
GISer_Jing1 天前
前端营销技术实战:数据+AI实战指南
前端·javascript·人工智能