深入浅出 JavaScript 数组:从基础到高级玩法

大家好,我是你们的老朋友FogLetter,今天我们来聊聊JavaScript中最基础却又最强大的数据结构之一------数组(Array)。很多同学觉得数组很简单,但实际上它藏着不少有趣的秘密和高级玩法。让我们一起来探索吧!

一、数组的本质:不只是简单的列表

1.1 数组是什么?

在JavaScript中,数组是一种特殊的对象,它是有序的数据集合。与普通对象不同,数组的元素是通过数字索引(从0开始)来访问的。

javascript 复制代码
const fruits = ['苹果', '香蕉', '橙子'];
console.log(fruits[0]); // "苹果"

但有趣的是,JavaScript数组远比这复杂得多。它不像C++或Java那样是严格的内存连续分配结构,而是更灵活的动态结构。

1.2 数组的创建方式

创建数组有三种主要方式:

  1. 数组字面量(最常用):

    javascript 复制代码
    const arr = [1, 2, 3];
  2. Array上的静态方法

    javascript 复制代码
    const arr = new Array(5); // 创建长度为5的空数组
    const arr2 = new Array(1, 2, 3); // [1,2,3]
  3. Array上的静态方法

    javascript 复制代码
    const arr = Array.of(1, 2, 3); // [1, 2, 3]
    const arr2 = Array.from(new Array(3),(val,index) => String.fromCharCode(index+65)); // ['A','B','C']

这里有个坑需要注意:当你使用new Array(5)时,创建的并不是包含5个undefined的数组,而是5个"空槽"(empty slots)。

javascript 复制代码
const arr = new Array(5);
console.log(arr); // [empty × 5]

这种数组在使用for...in循环时会有问题,因为空槽实际上是没有属性的:

javascript 复制代码
for(let key in arr) {
  console.log(key, arr[key]); // 什么都不会输出
}

解决办法是用fill方法填充:

javascript 复制代码
const arr = new Array(5).fill(undefined);
for(let key in arr) {
  console.log(key, arr[key]); // 现在可以正常输出了
}

二、数组的高级创建方式

2.1 Array.of() 方法

Array.of()方法解决了Array构造函数参数行为不一致的问题:

javascript 复制代码
Array.of(7);       // [7] 
Array.of(1, 2, 3); // [1, 2, 3]

new Array(7);      // [empty × 7]
new Array(1, 2, 3); // [1, 2, 3]

2.2 Array.from() 方法

Array.from()是更强大的数组创建方法,它可以将类数组对象或可迭代对象转换为真正的数组:

javascript 复制代码
// 从字符串创建数组
Array.from('foo'); // ["f", "o", "o"]

// 从Set创建数组
Array.from(new Set(['a', 'b'])); // ['a', 'b']

// 从Map创建数组
Array.from(new Map([[1, 2], [3, 4]])); // [[1, 2], [3, 4]]

// 创建字母表
const alphabet = Array.from(new Array(26),
(val,index) => String.fromCharCode(index+65)
);
console.log(alphabet); // ["A", "B", "C", ..., "Z"]

三、数组遍历的多种方式

遍历数组是日常开发中最常见的操作之一,JavaScript提供了多种遍历方式:

3.1 传统的for循环(计数循环)

javascript 复制代码
const arr = [1, 2, 3];
for(let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

优点:性能最好,可以随时中断循环(使用breakreturn

缺点:代码略显冗长

类似的还有while循环

3.2 forEach方法

javascript 复制代码
arr.forEach(item => {
  console.log(item);
});

优点:简洁明了

缺点:无法中途跳出循环(即使使用return也只是跳出当前回调)

3.3 for...of循环

ES6引入的for...of循环让数组遍历更加优雅:

javascript 复制代码
for(const item of arr) {
  console.log(item);
}

如果需要同时获取索引和值,可以结合entries()方法:

javascript 复制代码
for(const [index, value] of arr.entries()) {
  console.log(index, value);
}

3.4 其他迭代方法

JavaScript数组还提供了丰富的迭代方法:

  • map() - 映射新数组
  • filter() - 过滤数组
  • find() - 查找元素
  • some() - 是否有元素满足条件
  • every() - 是否所有元素都满足条件

四、数组的reduce方法:函数式编程的利器

reduce()方法是数组最强大的方法之一,它可以将数组"缩减"为单个值:

javascript 复制代码
const sum = [1, 2, 3, 4].reduce((prev, curr) => prev + curr, 0);
console.log(sum); // 10

reduce的能力远不止于此,它可以实现很多复杂的功能:

4.1 数组转对象

javascript 复制代码
const fruits = ['apple', 'banana', 'orange'];
const fruitMap = fruits.reduce((obj, fruit) => {
  obj[fruit] = true;
  return obj;
}, {});
console.log(fruitMap); // {apple: true, banana: true, orange: true}

4.2 实现分组

javascript 复制代码
const people = [
  {name: 'Alice', age: 21},
  {name: 'Bob', age: 20},
  {name: 'Charlie', age: 21}
];

const groupedByAge = people.reduce((acc, person) => {
  const age = person.age;
  if(!acc[age]) acc[age] = [];
  acc[age].push(person);
  return acc;
}, {});

console.log(groupedByAge);
// {
//   20: [{name: 'Bob', age: 20}],
//   21: [{name: 'Alice', age: 21}, {name: 'Charlie', age: 21}]
// }

五、数组的扩展运算符:简洁而强大

ES6的扩展运算符...让数组操作变得更加简洁:

5.1 复制数组

javascript 复制代码
const original = [1, 2, 3];
const copy = [...original];

5.2 合并数组

javascript 复制代码
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4]

5.3 函数参数传递

javascript 复制代码
const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 3

5.4 替代apply方法

javascript 复制代码
// 旧方式
function foo(x, y, z) {}
const args = [1, 2, 3];
foo.apply(null, args);

// 新方式
foo(...args);

六、数组的性能优化技巧

虽然JavaScript数组非常灵活,但在处理大数据量时,性能问题不容忽视:

6.1 预分配数组大小

javascript 复制代码
// 不推荐:动态扩容
const arr = [];
for(let i = 0; i < 1000000; i++) {
  arr[i] = i;
}

// 推荐:预分配大小
const arr = new Array(1000000);
for(let i = 0; i < 1000000; i++) {
  arr[i] = i;
}

6.2 避免在循环中修改数组长度

javascript 复制代码
// 不推荐
for(let i = 0; i < arr.length; i++) {
  if(someCondition) {
    arr.splice(i, 1); // 修改了数组长度
    i--; // 需要手动调整索引
  }
}

// 推荐:从后往前遍历
for(let i = arr.length - 1; i >= 0; i--) {
  if(someCondition) {
    arr.splice(i, 1); // 不影响前面的索引
  }
}

七、数组的"怪异"行为

JavaScript数组有一些看似奇怪的行为,了解它们可以避免踩坑:

7.1 稀疏数组

javascript 复制代码
const arr = [1, , 3]; // 注意中间的空位
console.log(arr.length); // 3 
console.log(arr[1]); // undefined
console.log(1 in arr); // false - 说明这个位置没有元素

7.2 length属性的特殊性

javascript 复制代码
const arr = [1, 2, 3];
arr.length = 5;
console.log(arr); // [1, 2, 3, empty × 2]

arr.length = 2;
console.log(arr); // [1, 2] - 后面的元素被删除了

7.3 数组也是对象

javascript 复制代码
const arr = [1, 2, 3];
arr.foo = 'bar';
console.log(arr.foo); // 'bar'
console.log(arr.length); // 3 - 非数字属性不影响length

八、现代JavaScript中的数组新特性

8.1 flat()和flatMap()

javascript 复制代码
// 扁平化数组
const arr = [1, [2, [3]]];
console.log(arr.flat(2)); // [1, 2, 3]

// flatMap = map + flat(1)
const sentences = ["Hello world", "Good morning"];
const words = sentences.flatMap(s => s.split(' '));
console.log(words); // ["Hello", "world", "Good", "morning"]

8.2 Array.prototype.at()

javascript 复制代码
const arr = [1, 2, 3];
console.log(arr.at(-1)); // 3 - 支持负索引

8.3 findLast()和findLastIndex()

javascript 复制代码
const arr = [1, 2, 3, 2, 1];
console.log(arr.findLast(x => x === 2)); // 2 (最后一个2)
console.log(arr.findLastIndex(x => x === 2)); // 3

结语

JavaScript数组看似简单,实则内涵丰富。从基本的创建和遍历,到高级的reduce操作和性能优化,数组在JavaScript中扮演着至关重要的角色。希望这篇文章能帮助你更深入地理解和掌握JavaScript数组的各种特性和技巧。

记住,数组是JavaScript中最常用的数据结构之一,熟练掌握它的各种用法,能让你的代码更加简洁高效。如果你有任何问题或想法,欢迎在评论区留言讨论!

相关推荐
拼图2093 分钟前
element-plus——图标推荐
javascript·vue.js·elementui
期待のcode11 分钟前
图片上传实现
java·前端·javascript·数据库·servlet·交互
koooo~1 小时前
JavaScript中的Window对象
开发语言·javascript·ecmascript
hbrown1 小时前
Flask+LayUI开发手记(十一):选项集合的数据库扩展类
前端·数据库·python·layui
猫头虎1 小时前
什么是 npm、Yarn、pnpm? 有什么区别? 分别适应什么场景?
前端·python·scrapy·arcgis·npm·beautifulsoup·pip
迷曳1 小时前
27、鸿蒙Harmony Next开发:ArkTS并发(Promise和async/await和多线程并发TaskPool和Worker的使用)
前端·华为·多线程·harmonyos
安心不心安2 小时前
React hooks——useReducer
前端·javascript·react.js
像风一样自由20202 小时前
原生前端JavaScript/CSS与现代框架(Vue、React)的联系与区别(详细版)
前端·javascript·css
啃火龙果的兔子2 小时前
react19+nextjs+antd切换主题颜色
前端·javascript·react.js
_pengliang2 小时前
小程序按住说话
开发语言·javascript·小程序