前端常见数组分析

一、底层原理与性能陷阱

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]]
相关推荐
撰卢13 分钟前
总结一下vue3的组件之间数据转递,子组件传父组件,父组件传子组件
前端·javascript·vue.js
前端开发爱好者1 小时前
Vue3 超强“积木”组件!5 分钟搞定可交互 3D 机房蓝图!
前端·javascript·vue.js
前端开发爱好者1 小时前
尤雨溪力荐!Vue3 专属!100+ 动效组件!
前端·javascript·vue.js
前端开发爱好者1 小时前
尤雨溪力荐!Vue3 生态最强大的 14 个 UI 组件库!
前端·javascript·vue.js
lb29171 小时前
关于多个el-input的自动聚焦,每输入完一个el-input,自动聚焦到下一个
前端·javascript·vue.js
lingliang1 小时前
使用 JS 渲染页面并导出为PDF 常见问题与修复
javascript·pdf·vue
sorryhc2 小时前
【AI解读源码系列】ant design mobile——Divider分割线
前端·javascript·react.js
前端进阶者2 小时前
electron-vite_20配置依赖包运行时区外部加载commonjsExternals_vite-plugin-commonjs-externals
前端·electron
anyup2 小时前
🔥 uView Pro 全新升级来啦!一行配置,解锁 uView Pro 全局 TS 类型提示与校验
前端·vue.js·uni-app
Jimmy2 小时前
使用 Electron 在 5 分钟内创建一个桌面的 React 应用
前端·javascript·electron