JS数组(超详细)_js 数组C++ 函数
JavaScript 数组(Array)——超详细指南
JavaScript 中的数组是一种可变、动态、基于索引的列表结构,底层实现为对象(Object)的特殊形式,既具备序列特征,也保留普通对象的灵活性。下面从基础概念、创建方式、核心属性与方法、遍历、性能与底层机制、进阶技巧等方面做全面解析。
一、数组的基本概念
- 索引与长度
- 数组索引从
0
开始,到length - 1
结束。 length
属性始终比最大索引值大 1;写入arr[length]
会自动增长长度;手动设置length
可截断或扩展数组。
- 数组索引从
- 稀疏数组
- 数组可以包含“空位”(empty slots),即跳过某些索引。
- 空位与
undefined
不同:arr = [1, , 3]
,1 in arr === false
,但arr[1] === undefined
。
- 数组即对象
- 底层类型:
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
与数组方法链式调用,保持简洁、可读。
五、底层机制与性能
- 连续内存 vs. 稀疏
- V8 对“连续元素”(dense)数组做优化,存储在连续内存块;
- 稀疏数组或含混合类型(integer key 与非整数 key)会降级为哈希表(Dictionary mode),访问速度变慢。
- 隐藏类(Hidden Class)与 Inline Caches
- JS 引擎通过隐藏类优化对象访问;频繁改变数组形态(如切换稠密/稀疏模式、加入自定义属性)会触发隐藏类转变,影响性能。
- 避免频繁扩大
- 使用
push
比直接修改高索引(如arr[10000] = x
)更友好; - 预先设置初始长度
new Array(n)
,然后填充,也有助于性能。
- 使用
- TypedArray
- 针对数值密集型场景,可使用
Int8Array
、Float64Array
等固定长度、同质化的数组视图,性能与内存表现优异。
- 针对数值密集型场景,可使用
六、 Array-like 对象 与 转换
- Array-like:拥有
length
属性与数值键,但不具备数组方法,如 DOM NodeList、函数的arguments
。 - 转为真数组:
const list = document.querySelectorAll('div'); // NodeList const arr = Array.from(list); // 或者 const arr2 = [].slice.call(list);
七、进阶技巧
- 链式调用
const result = arr .filter(n => n % 2 === 0) .map(n => n * 2) .reduce((acc, x) => acc + x, 0);
- 去重
const uniq = [...new Set(arr)];
- 分组
const groups = arr.reduce((acc, item) => { const key = keyFn(item); (acc[key] ||= []).push(item); return acc; }, {});
- 数组填充
const a = Array(5).fill(0); // [0,0,0,0,0] const b = Array.from({length:5}, (_,i)=>i); // [0,1,2,3,4]
- 并行控制(Promise 数组合并)
const promises = urls.map(fetch); const responses = await Promise.all(promises);
- 多维数组
const matrix = Array.from({ length: m }, () => Array(n).fill(0));
八、小结
- JavaScript 数组表面看“简单”,底层却兼具对象通用性与列表特征。
- 熟练掌握创建、遍历与常用方法,注意“稠密/稀疏”与“隐藏类”对性能的影响。
- 结合 ES6+ 特性(
for…of
、拓展运算符、解构、Array.from
、Set
等),可写出优雅、高效、可读的数组逻辑。
希望这份“超详细”指南能帮助你全面理解和高效使用 JavaScript 数组。若有更深入的场景或疑问,欢迎继续交流!