JS数组(超详细)_js 数组C++ 函数

JavaScript 数组(Array)——超详细指南

JavaScript 中的数组是一种可变、动态、基于索引的列表结构,底层实现为对象(Object)的特殊形式,既具备序列特征,也保留普通对象的灵活性。下面从基础概念、创建方式、核心属性与方法、遍历、性能与底层机制、进阶技巧等方面做全面解析。


一、数组的基本概念

  1. 索引与长度
    • 数组索引从 0 开始,到 length - 1 结束。
    • length 属性始终比最大索引值大 1;写入 arr[length]会自动增长长度;手动设置 length 可截断或扩展数组。
  2. 稀疏数组
    • 数组可以包含“空位”(empty slots),即跳过某些索引。
    • 空位与 undefined 不同:arr = [1, , 3]1 in arr === false,但 arr[1] === undefined
  3. 数组即对象
    • 底层类型:typeof [] === 'object'
    • 可视为具备数字键和 length 属性的对象,索引键会被自动更新 length

二、创建与初始化

// 字面量
let a = [];                  // 空数组
let b = [1, 2, 3];           // 有初始值
let sparse = [1, , 3];       // 稀疏数组

// 构造函数
let c = new Array();         // 等价于 []
let d = new Array(5);        // [ <5 empty slots> ]
let e = new Array(1, 2, 3);  // [1,2,3]

// Array.from / Array.of
let fromStr = Array.from('abc');          // ['a','b','c']
let fromMap = Array.from({0:'x',1:'y', length:2});
let ofArr = Array.of(5);                  // [5]
  • 注意new Array(5) 会创建长度为 5 的稀疏数组;而 Array.of(5) 则创建含一个元素 5 的普通数组。

三、核心属性与方法

3.1 属性

  • length:只读表象,可写会改变数组大小。

3.2 通用方法

方法作用
push(...items)尾部插入元素,返回新长度
pop()移除尾部元素,返回该元素
unshift(...items)头部插入元素,返回新长度
shift()移除头部元素,返回该元素
splice(start, deleteCount, ...items)任意位置增删元素,返回被删除的子数组
slice(begin?, end?)返回浅拷贝子数组
concat(...arrays_or_values)合并数组/值,返回新数组
indexOf(item, fromIndex?)查找元素首次索引,未找到返回 -1
lastIndexOf(item, fromIndex?)查找元素最后索引
includes(item, fromIndex?)是否包含该元素,返回布尔

3.3 迭代与映射

方法作用
forEach(fn)遍历(无返回值)
map(fn)返回映射后新数组
filter(fn)返回满足条件子集新数组
reduce(fn, initialValue?)累积计算,返回单一值
reduceRight(fn, initialValue?)从右向左累积计算
find(fn)返回第一个满足条件的元素
findIndex(fn)返回第一个满足条件元素的索引
some(fn)是否存在满足条件的元素
every(fn)是否所有元素都满足条件
flat(depth=1)展平多维数组
flatMap(fn)等同于先 map 再 flat(1)

3.4 排序与组合

  • sort(compareFn?):原地排序;默认将元素转为字符串按 Unicode 排序。
  • reverse():原地反转数组。

四、遍历方式对比

let arr = ['a','b','c'];

// 1. 经典 for
for (let i = 0; i < arr.length; i++) { /* arr[i] */ }

// 2. for…of(ES6)
for (const item of arr) { /* item */ }

// 3. forEach
arr.forEach((item, idx) => { /* … */ });

// 4. for…in (不推荐)
for (const key in arr) { /* key: 索引或自定义属性名 */ }

// 5. 迭代器与展开
const iterator = arr[Symbol.iterator]();
iterator.next(); // {value:'a', done:false}

const copy = [...arr];   // 解构复制
  • 推荐 for…of 与数组方法链式调用,保持简洁、可读。

五、底层机制与性能

  1. 连续内存 vs. 稀疏
    • V8 对“连续元素”(dense)数组做优化,存储在连续内存块;
    • 稀疏数组或含混合类型(integer key 与非整数 key)会降级为哈希表(Dictionary mode),访问速度变慢。
  2. 隐藏类(Hidden Class)与 Inline Caches
    • JS 引擎通过隐藏类优化对象访问;频繁改变数组形态(如切换稠密/稀疏模式、加入自定义属性)会触发隐藏类转变,影响性能。
  3. 避免频繁扩大
    • 使用 push 比直接修改高索引(如 arr[10000] = x)更友好;
    • 预先设置初始长度 new Array(n),然后填充,也有助于性能。
  4. TypedArray
    • 针对数值密集型场景,可使用 Int8ArrayFloat64Array 等固定长度、同质化的数组视图,性能与内存表现优异。

六、 Array-like 对象 与 转换

  • Array-like:拥有 length 属性与数值键,但不具备数组方法,如 DOM NodeList、函数的 arguments
  • 转为真数组:const list = document.querySelectorAll('div'); // NodeList const arr = Array.from(list); // 或者 const arr2 = [].slice.call(list);

七、进阶技巧

  1. 链式调用const result = arr .filter(n => n % 2 === 0) .map(n => n * 2) .reduce((acc, x) => acc + x, 0);
  2. 去重const uniq = [...new Set(arr)];
  3. 分组const groups = arr.reduce((acc, item) => { const key = keyFn(item); (acc[key] ||= []).push(item); return acc; }, {});
  4. 数组填充const a = Array(5).fill(0); // [0,0,0,0,0] const b = Array.from({length:5}, (_,i)=>i); // [0,1,2,3,4]
  5. 并行控制(Promise 数组合并)const promises = urls.map(fetch); const responses = await Promise.all(promises);
  6. 多维数组const matrix = Array.from({ length: m }, () => Array(n).fill(0));

八、小结

  • JavaScript 数组表面看“简单”,底层却兼具对象通用性与列表特征。
  • 熟练掌握创建、遍历与常用方法,注意“稠密/稀疏”与“隐藏类”对性能的影响。
  • 结合 ES6+ 特性(for…of、拓展运算符、解构、Array.fromSet 等),可写出优雅、高效、可读的数组逻辑。

希望这份“超详细”指南能帮助你全面理解和高效使用 JavaScript 数组。若有更深入的场景或疑问,欢迎继续交流!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注