前端常见数组分析

一、底层原理与性能陷阱

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]]
相关推荐
天若有情67312 分钟前
新闻通稿 | 软件产业迈入“智能重构”新纪元:自主进化、人机共生与责任挑战并存
服务器·前端·后端·重构·开发·资讯·新闻
鱼干~18 分钟前
electron基础
linux·javascript·electron
香香爱编程18 分钟前
electron对于图片/视频无法加载的问题
前端·javascript·vue.js·chrome·vscode·electron·npm
程序猿_极客1 小时前
【期末网页设计作业】HTML+CSS+JavaScript 蜡笔小新 动漫主题网站设计与实现(附源码)
前端·javascript·css·html·课程设计·期末网页设计
zl_vslam1 小时前
SLAM中的非线性优-3D图优化之轴角在Opencv-PNP中的应用(一)
前端·人工智能·算法·计算机视觉·slam se2 非线性优化
CDwenhuohuo2 小时前
用spark-md5实现切片上传前端起node模拟上传文件大小,消耗时间
前端
阿桂有点桂2 小时前
React使用笔记(持续更新中)
前端·javascript·react.js·react
自由日记2 小时前
实例:跳动的心,火柴人
前端·css·css3
柯腾啊2 小时前
一文简单入门 Axios
前端·axios·apifox
im_AMBER2 小时前
React 15
前端·javascript·笔记·学习·react.js·前端框架