JavaScript数组高级玩法:从入门到放弃再到精通

前言

都2025年了,你还在用for循环遍历数组?是时候升级你的数组技能树了! 今天咱们就来深挖一下JavaScript数组的那些"不为人知"的秘密,保证让你大呼过瘾!

重新认识数组:不只是"盒子"那么简单

首先,我们要颠覆一个认知:JavaScript的数组并不是传统意义上的数组,它更像是一个"可遍历的对象"。这话怎么理解呢?

数组的两种初始化方式

js 复制代码
// 方式一:new Array() - C++/Java程序员的最爱
const arr = new Array(5);  // 指定了大小
console.log(arr); // [empty × 5]

// 方式二:数组字面量 - JavaScript原住民的选择  
const arr2 = [1, 2, 3, 4, 5];

这里就有意思了,new Array(5)创建的数组虽然有5个位置,但这些位置是"empty"状态,不是undefined

Empty vs Undefined:一个让人头大的区别

js 复制代码
const arr = new Array(5);
console.log(arr[0]); // undefined(访问时返回undefined)

// 但是用for...in遍历时
for(let key in arr){
    console.log(key, arr[key]); // 什么都不会打印!
}

为什么会这样?因为"empty"意味着这个索引键根本不存在!就像你家里有5个房间的钥匙位置,但钥匙还没造出来。

要解决这个问题,我们需要显式填充:

js 复制代码
const arr2 = new Array(5).fill(undefined);
arr2[8] = undefined; // 动态扩容,JavaScript数组的超能力!
console.log(arr2); // [undefined, undefined, undefined, undefined, undefined, empty × 3, undefined]

// 现在for...in可以遍历有值的索引了
for(let key in arr2){
    console.log(key, arr2[key]); // 0,1,2,3,4,8
}

Array的静态方法:造数组的艺术

Array.of() - 优雅的数组创建

js 复制代码
// 不用再纠结new Array的奇怪行为了
console.log(Array.of(1,2,3)); // [1, 2, 3]
// 对比一下new Array的迷惑行为
console.log(new Array(3)); // [empty × 3] 
console.log(new Array(1,2,3)); // [1, 2, 3]

Array.from() - 数组界的变形金刚

这个方法绝对是我的心头好,能把任何"类数组"转换成真正的数组:

js 复制代码
// 一行代码生成字母表,是不是很酷?
console.log(Array.from(new Array(26), (val, index) => String.fromCharCode(index + 65)));
// ['A', 'B', 'C', ..., 'Z']

第二个参数是个回调函数,类似于map,可以在转换的同时进行计算。这比先创建再map要优雅多了!

🔍 对象原型链与数组的关系

这里要聊一个容易被忽略的点:JavaScript中一切皆对象,数组也不例外。

js 复制代码
let obj = {
    name: '迪迦',
}
let obj2 = {
    skill: '迪拉修姆光流',
}
obj.__proto__ = obj2;

console.log(obj.skill); // '迪拉修姆光流'

// 遍历时会包含原型链上的属性
for(let key in obj){
    console.log(obj[key]); // '迪迦' 和 '迪拉修姆光流'
}

// 判断是否为自有属性
console.log(obj.hasOwnProperty('name')); // true
console.log(obj.hasOwnProperty('skill')); // false

// 数组也是对象,也有这个问题
const arr = new Array(5);
console.log(arr.hasOwnProperty(0)); // false(因为是empty)

这就是为什么在遍历对象时,我们经常要用hasOwnProperty来过滤原型链上的属性。

数组遍历:告别无脑for循环

forEach:最常用但有局限

js 复制代码
const names = Array.of('Alice','Bob','Charlie','David','Eve');

names.forEach(name => {
    if (name === 'Charlie'){
        console.log('Charlie is here, stop...');
        return; // 只能跳过当前迭代,不能中断整个循环
    }
    console.log('Processing:', name);
});

forEach的问题是无法中断循环,return只是跳过当前项,不是真正的break

for...of:现代JavaScript的优雅选择

js 复制代码
const arr = [1,2,3];

// 简单遍历值
for(let item of arr){
    console.log(item);
}

// 需要索引?用entries()
for(const item of arr.entries()){
    console.log(item); // [0, 1], [1, 2], [2, 3]
}

// 解构赋值,同时拿到索引和值
for(const [index, item] of arr.entries()){
    console.log(index, item);
}

for...offorEach的优势在于:

  1. 可以使用breakcontinue
  2. 可读性更好
  3. 性能略优于forEach

reduce:数组界的瑞士军刀

如果说JavaScript数组方法中只能选一个MVP,我会毫不犹豫选择reduce

js 复制代码
// 基础用法:累加
console.log([1,2,3,4,5,6].reduce((prev, curr) => {
    return prev + curr;
}, 0)); // 21

reduce的精髓在于"消灭数组,留下一个"。它在处理复杂的状态转换时特别有用,新的状态总是基于上一个状态产生。

想象一下用reduce实现:

  • 数组去重
  • 分组统计
  • 数据格式转换
  • 状态机...

简直就是函数式编程的利器!

实战建议

  1. 性能考虑:普通的计数for循环性能最好,但可读性差。在性能敏感的场景(如大数据处理)可以考虑
  2. 可读性优先 :大多数情况下,for...ofmapfilter等方法的可读性远超传统for循环
  3. 避免混用:不要在同一个项目中混用多种遍历方式,保持代码风格一致

总结

JavaScript数组远比我们想象的复杂和强大:

  • 它既有类似HashMap的key-value特性,又支持动态扩容
  • new Array()和字面量[]在某些场景下行为不同
  • 现代JavaScript提供了丰富的数组方法,告别无脑for循环
  • reduce是处理复杂数据转换的神器

下次面试官问你数组相关问题时,记得展示这些深度理解,绝对能让你脱颖而出!

相关推荐
Lanwarf-前端开发26 分钟前
turbo-monorepo中自定义脚本运行项目下的包
javascript
Kiri霧35 分钟前
Kotlin抽象类
android·前端·javascript·kotlin
阿星做前端1 小时前
聊聊前端请求拦截那些事
前端·javascript·面试
讨厌吃蛋黄酥1 小时前
深入 JavaScript 事件循环:单线程如何掌控异步世界
javascript
多啦C梦a2 小时前
React 表单界的宫斗大戏:受控组件 VS 非受控组件,谁才是正宫娘娘?
前端·javascript·react.js
今晚一定早睡4 小时前
new操作符
前端·javascript·typescript
骑驴看星星a4 小时前
定时器与间歇函数
javascript·redis·学习·mysql·oracle
拉不动的猪4 小时前
针对初学者的JS八种类型实用小技巧总结
javascript·css·面试
4 小时前
Android本地浏览PDF(Android PDF.js 简要学习手册)
android·javascript·pdf