从创建到遍历,带你全面了解 JavaScript 数组
在 JavaScript 中,数组是最基础、也最容易被“低估”的数据结构之一。表面上看,它只是用来存储一组数据的容器;但深入探索后你会发现,数组的背后隐藏着 JavaScript 引擎的优化机制、稀疏数组的陷阱、以及函数式编程思想的体现。
一、数组的特性与创建
JavaScript数组的特性
-
js数组是动态数组,不需要预先定义数组的大小,可以随意添加或删除元素
const arr = [1,2,3] arr.push(4) // [1,2,3,4] arr.pop() // [1,2,3] -
数组中的元素类型不限,js数组中可以存放任意类型的数据
const arr = [1,'str',[5,5],{name:"864"},function(){console.log('486')}]虽然 JavaScript 语言本身不规定数组元素的大小,但现代 JavaScript 引擎(如 V8)为了性能优化,会根据数组中元素的类型进行内部优化。
V8 引擎会对数组进行优化,将其存储为更高效的结构。常见的优化方式有:
数组类型 内部表示 元素大小(大致) 特点 PACKED_SMI_ELEMENTS存储小整数(Small Integer) 4 字节 最快的整数数组 PACKED_DOUBLE_ELEMENTS存储浮点数 8 字节 支持数字类型 PACKED_ELEMENTS一般对象数组 8 字节(引用) 存储任意对象引用 DICTIONARY_ELEMENTS稀疏数组 不连续 性能较差 -
可以通过索引来获取元素或修改元素,通过length属性来获取数组的长度
let arr = [10, 20, 30]; console.log(arr[0]); // 输出 10 arr[1] = 25; console.log(arr); // [10, 25, 30] -
如果跳过索引直接赋值,就会创建稀疏数组。稀疏数组中某些索引没有值,但
length仍会计算这些位置。const arr = [] arr[8] = '86' console.log(arr) // [ <8 empty items>, '86' ] -
new Array(n)与new Array(n).fill()的区别const arr1 = new Array(5); // 创建一个长度为5的空数组,元素是 empty slots [ <5 empty items> ] const arr2 = new Array(5).fill(0); // 创建一个长度为5的数组,每个元素是 0 console.log(arr1[1]) // undefined-
new Array(n)会创建一个稀疏数组(sparse array),其中的元素是“空槽”,不是undefined。let arr = new Array(3); console.log(arr.map(() => 'hello')); // [ <3 empty items> ]map()不会遍历空槽。如果是undefined,则map会执行对应的回调函数,但若是“空槽”,则map会跳过这些位置。使用
forEach()也可以证明,forEach也不会遍历“空槽”。 -
fill()方法会将数组“致密化(densify)”,填充所有位置为指定值。
-
-
JavaScript 数组是对象,具有对象的特性,比如可以动态添加属性:
const arr = [1, 2, 3]; arr.foo = 'bar';虽然这种特性在某些场景下有用,但应避免滥用,因为这可能会影响数组的性能和行为。
JavaScript数组的创建
-
字面量创建
这是最常见、最简洁的创建方式,这种方式适合手动初始化数组内容。
const arr = [1, 'hello', true, { name: 'Tom' }]; -
使用
new Array()创建使用
new关键字配合Array构造函数也可以创建数组:const arr = new Array(10); // 创建一个长度为10的空数组此时数组中的元素是“空槽(empty slots)”,不是
undefined,在遍历时会被跳过。如果希望初始化数组元素,可以结合
.fill()方法:const arr = new Array(10).fill(0); // 创建一个长度为10的数组,每个元素都是0此外,也可以直接传入参数:
const arr = new Array(1, 2, 3); // 等价于 [1, 2, 3] -
使用
Array.of()Array.of()是 Array 类上的静态方法,用于创建一个新数组,其参数将直接作为数组元素:const arr = Array.of(1, 2, 3); // [1, 2, 3]它与
new Array()的区别在于,当传入一个数字时,Array.of()会将其视为数组元素,而不是数组长度:Array.of(3); // [3] new Array(3); // 创建长度为3的空数组 -
使用
Array.from()Array.from()可以将类数组对象或可迭代对象(如Set、Map、arguments、DOM NodeList)转换为真正的数组:const arr = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']它还可以接受一个映射函数作为第二个参数:
const arr = Array.from(new Array(26), (val, index) => String.fromCharCode(index + 65)); // ['A', 'B', ..., 'Z'] -
使用扩展运算符
...扩展运算符也是创建数组的一种现代方式,尤其适合复制或合并数组:
const arr = [...[1, 2, 3]]; // 复制数组 const combined = [...arr1, ...arr2]; // 合并两个数组
二、数组的遍历方式
JavaScript 提供了多种数组遍历方式,开发者可以根据需求选择不同的方式。
-
普通
for循环这是最传统的数组遍历方式,性能较好,适合对索引有控制需求的场景:
for (let i = 0; i < arr.length; i++) { console.log(arr[i]); } -
forEach()方法forEach()是数组原型上的方法,用于执行对每个元素的操作:arr.forEach((item, index) => { console.log(item, index); });注意:
forEach()不能使用break或continue,因为它本质上是一个回调函数。 -
for...of循环ES6 引入了
for...of,用于遍历可迭代对象(包括数组):for (let val of arr) { console.log(val); }结合
entries()可以同时获取索引和值:for (const [index, item] of arr.entries()) { console.log(index, item); } -
for...in循环虽然
for...in也可以遍历数组,但它实际上是遍历对象的可枚举属性,不推荐用于数组:for (let key in arr) { console.log(key, arr[key]); }注意: 遍历顺序可能不是数字顺序,也不保证顺序。
-
reduce()方法reduce()用于将数组“归并”为一个值,常用于求和、扁平化等场景:const sum = arr.reduce((prev, curr) => prev + curr, 0); -
map()、filter()、some()、every()等函数式方法这些方法不仅遍历数组,还返回新的数组或布尔值,是函数式编程中的常用工具:
const doubled = arr.map(x => x * 2); const filtered = arr.filter(x => x > 10); const hasEven = arr.some(x => x % 2 === 0); const allPositive = arr.every(x => x > 0);