从创建到遍历,带你全面了解 JavaScript 数组

86 阅读5分钟

从创建到遍历,带你全面了解 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数组的创建

  1. 字面量创建

    这是最常见、最简洁的创建方式,这种方式适合手动初始化数组内容。

    const arr = [1, 'hello', true, { name: 'Tom' }];
    
  2. 使用 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]
    
  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的空数组
    
  4. 使用 Array.from()

    Array.from() 可以将类数组对象或可迭代对象(如 SetMaparguments、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']
    
  5. 使用扩展运算符 ...

    扩展运算符也是创建数组的一种现代方式,尤其适合复制或合并数组:

    const arr = [...[1, 2, 3]]; // 复制数组
    const combined = [...arr1, ...arr2]; // 合并两个数组
    

二、数组的遍历方式

JavaScript 提供了多种数组遍历方式,开发者可以根据需求选择不同的方式。

  1. 普通 for 循环

    这是最传统的数组遍历方式,性能较好,适合对索引有控制需求的场景:

    for (let i = 0; i < arr.length; i++) {
        console.log(arr[i]);
    }
    
  2. forEach() 方法

    forEach() 是数组原型上的方法,用于执行对每个元素的操作:

    arr.forEach((item, index) => {
        console.log(item, index);
    });
    

    注意: forEach() 不能使用 breakcontinue,因为它本质上是一个回调函数。

  3. 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);
    }
    
  4. for...in 循环

    虽然 for...in 也可以遍历数组,但它实际上是遍历对象的可枚举属性,不推荐用于数组:

    for (let key in arr) {
        console.log(key, arr[key]);
    }
    

    注意: 遍历顺序可能不是数字顺序,也不保证顺序。

  5. reduce() 方法

    reduce() 用于将数组“归并”为一个值,常用于求和、扁平化等场景:

    const sum = arr.reduce((prev, curr) => prev + curr, 0);
    
  6. 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);