前端常见数组分析

一、底层原理与性能陷阱

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]]
相关推荐
开发者小天几秒前
React中使用 Ant Design Charts 图表
前端·javascript·react.js
shenyan~34 分钟前
关于 Web 漏洞原理与利用:2. XSS(跨站脚本攻击)
前端·xss
雪山上的小灰熊1 小时前
如何使用Antv X6使用拖拽布局?
开发语言·前端·javascript
爱吃小白兔的猫1 小时前
从零开始创建React项目及制作页面
前端·react.js·前端框架
steven~~~2 小时前
elementui初学1
前端·javascript·elementui
幽冥雪羽2 小时前
vue3中RouterView配合KeepAlive实现组件缓存
前端·vue.js·缓存
Clownseven5 小时前
[Web服务器对决] Nginx vs. Apache vs. LiteSpeed:2025年性能、功能与适用场景深度对比
服务器·前端·nginx
TE-茶叶蛋6 小时前
React的合成事件(SyntheticEventt)
前端·javascript·react.js
GISer_Jing8 小时前
CSS-in-JS:现代前端样式管理的革新
前端·javascript·css
咖啡の猫10 小时前
JavaScript基础-作用域链
开发语言·javascript