JS数组(超详细)_js 数组
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); // [ ]let e = new Array(1, 2, 3); // [1,2,3]// Array.from / Array.oflet 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?)
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. 经典 forfor (let i = 0; i < arr.length; i++) { /* arr[i] */ }// 2. for…of(ES6)for (const item of arr) { /* item */ }// 3. forEacharr.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\'); // NodeListconst 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 数组。若有更深入的场景或疑问,欢迎继续交流!