JavaScript数组:轻松愉快地玩透它

在JavaScript的世界里,数组就像神奇的魔法口袋 - 看似简单,却暗藏玄机。今天我们一起揭开数组的神秘面纱,看看这些"魔法口袋"的奇妙特性!

🧩 空槽数组:幽灵般的空房间

javascript 复制代码
const arr = new Array(5)  // 创建幽灵公寓
console.log(arr) // 显示: [empty × 5] 

for (let key in arr) {
    console.log(key, arr[key]) // 无人回应!
}

当我们用new Array(5)创建数组时,就像建造了一栋5层楼的公寓,但里面没有任何住户。这些"空房间"(empty slots)非常特殊 - 它们像幽灵般存在,却无法被for...in循环探测到!

比喻时刻 :想象你有一栋5层楼的公寓,每层都有门牌号(索引),但里面没有住人。物业(for...in)来检查时,因为没人应门,就认为整栋楼都是空的!

🪄 唤醒幽灵:给空房间派发居民

javascript 复制代码
const arr2 = new Array(5).fill(undefined) // 给每个房间派发"幽灵"

arr2[8] = undefined // 在9楼也放个幽灵
console.log(arr2) // 显示: [undefined, undefined, ... , undefined]

for (let key in arr2) {
    console.log(key, arr2[key]) // 现在能看到幽灵了!
}

通过.fill(undefined),我们给每个空房间都分配了"幽灵居民"。有趣的是,当我们直接给索引8赋值时,JavaScript会自动扩建公寓到9层,中间楼层(5-7)仍然是空槽!

比喻时刻:这就像给空公寓每个房间都安排了"幽灵管家"(undefined)。现在物业检查时,每个房间都有人应门了。当你突然在9楼安排管家时,大楼自动扩建,但6-8楼还是空置状态。

🧰 数组创建三剑客

JavaScript提供了多种创建数组的魔法工具:

javascript 复制代码
// 1. 对象字面量
const arr=[] 
// 2. 传统方式 - 可能产生幽灵公寓
const ghostBuilding = new Array(5) 

// 3. 安全方式 - 预装管家
const managedBuilding = new Array(5).fill(undefined)

// 4. 精确控制 - 智能建造师
const smartBuilding = Array.of(1, 2, 3) // [1, 2, 3]

// 5. 变形大师 - 魔法改造
const alphabetTower = Array.from(new Array(26), (_, index) => 
    String.fromCodePoint(65 + index)) // A-Z字母塔

Array.from尤其强大,它能将任何"类数组"对象转化为真正的数组,同时进行魔法加工!

我们打印alphabetTower看看结果:

比喻时刻Array.of像精准的3D打印机,你要什么就打印什么;Array.from则是回收改造大师,能把旧家具(类数组对象)改造成全新智能家具(真数组)。

🔍 空槽 vs undefined:幽灵与管家的区别

javascript 复制代码
const ghostHotel = new Array(5) // 幽灵酒店
console.log(ghostHotel[0]) // undefined - 但这是假象!
console.log(ghostHotel.hasOwnProperty(0)) // false - 没有0号房间!

const staffedHotel = [undefined, undefined] // 管家酒店
console.log(staffedHotel.hasOwnProperty(0)) // true - 真有0号房间

关键区别在于:空槽数组的房间根本不存在(没有属性),而显式设置为undefined的房间是存在的,只是里面住着"幽灵管家"。

hasOwnProperty()可以用来判断非原型链上有无该属性,有返回true,没有返回false,来看下述代码的演示

ini 复制代码
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'), obj.hasOwnProperty('skill'));

skill我们是可以通过原型链访问到的,但是我们本身并没有足够属性,所以obj.hasOwnProperty('skill')返回的是false

比喻时刻:空槽酒店是海市蜃楼 - 看着有5层,实际连地基都没有;而管家酒店是真实建筑,每层都有管家(即使是透明的幽灵管家)。

🚶 数组遍历:不同的观光方式

JavaScript提供多种数组"观光巴士":

javascript 复制代码
const names = ['Alice', 'Bob', 'Charlie', 'David']

// 1. 传统巴士 - 每站都停 计数循环
for(let i=0; i<names.length; i++) {
    console.log(`停靠站 ${i}: ${names[i]}`)
}

// 2. 自动观光车 - 不能中途下车 return是不生效地,break和continue是不能使用的
names.forEach(name => {
    if(name === 'Charlie') {
        console.log('查理在此!但车不能停...')
        return // 只能跳过当前,不能停车!
    }
    console.log(`观光: ${name}`)
})

// 3. 智能导游 - 精准控制
for(const [index, name] of names.entries()) {
    console.log(`第${index}位游客: ${name}`)
    if(name === 'Charlie') break // 随时可以下车!
}

注意:在forEach中,是不允许中断的,return是无效的,break、continue是报错的

比喻时刻forEach像观光缆车 - 路线固定不能中途停;for...of+entries()则是私人导游,随时可调整行程;传统for循环是自驾游,完全自主但操作复杂。

详解Array.prototype.entries()

在 JavaScript 中,Array.prototype.entries() 是数组的一个方法,它返回一个 迭代器(iterator)对象 ,用于遍历数组的 键值对(key-value pairs)

📌 基本语法:

c 复制代码
array.entries()
  • 返回值 :一个新的 迭代器对象 ,每次调用 .next() 会返回一个 [index, element] 的数组,表示数组中元素的索引和值。

✅ 示例讲解:

示例1:基础使用

lua 复制代码
const arr = ['a', 'b', 'c'];
const iterator = arr.entries();

console.log(iterator.next()); // { value: [0, 'a'], done: false }
console.log(iterator.next()); // { value: [1, 'b'], done: false }
console.log(iterator.next()); // { value: [2, 'c'], done: false }
console.log(iterator.next()); // { value: undefined, done: true }

示例2:使用 for...of 遍历 entries

arduino 复制代码
const arr = ['apple', 'banana', 'cherry'];

for (const [index, value] of arr.entries()) {
  console.log(index, value);
}

// 输出:
// 0 apple
// 1 banana
// 2 cherry

🧠 特点总结:

特性 说明
返回值 一个迭代器对象
每个元素形式 [index, element]
是否修改原数组 ❌ 不会修改原数组
是否可迭代 ✅ 可以用 for...of 遍历
用途 遍历数组的索引和值,常用于需要同时获取索引和元素的场景

🆚 与其他方法对比:

方法 返回内容 示例输出
arr.keys() 所有元素的索引 0, 1, 2
arr.values() 所有元素的值 'a', 'b', 'c'
arr.entries() 索引和值的键值对 [0, 'a'], [1, 'b'], [2, 'c']

🔍 实际应用场景:

  1. 同时需要索引和值 的时候,比 forEach((value, index) => {}) 更灵活(尤其是结合解构)。
  2. 配合 for...of 使用,代码更清晰。
  3. 实现自定义迭代逻辑,比如封装遍历器。

💡 小技巧:将 entries 转为对象或 Map

css 复制代码
Javascript
浅色版本
const arr = ['a', 'b', 'c'];
const entries = [...arr.entries()];
console.log(entries); // [[0, 'a'], [1, 'b'], [2, 'c']]

// 转成 Map
const map = new Map(arr.entries());
console.log(map.get(1)); // 'b'

// 转成对象(需处理)
const obj = Object.fromEntries(arr.entries());
console.log(obj); // { '0': 'a', '1': 'b', '2': 'c' }

🧮 Reduce:数组炼金术

javascript 复制代码
const magicCauldron = [1, 2, 3, 4, 5, 6]
const philosopherStone = magicCauldron.reduce((elixir, ingredient) => {
    return elixir + ingredient // 炼金配方
}, 0) // 初始原料

console.log(philosopherStone) // 21 - 点石成金!

reduce就像魔法坩埚,把数组元素按配方炼制出最终产物。它的强大之处在于可以追踪"炼制状态",实现复杂转换。

比喻时刻:reduce是炼金术士的工作台 - 你放入各种原料(数组元素),定义配方(回调函数),设置初始材料(初始值),最终得到魔法产物(返回值)。

🌉 数组的桥梁特性

JavaScript数组的独特之处在于它融合了多种数据结构特性:

arduino 复制代码
// 作为栈使用
const stack = []
stack.push('书1') // 入栈
stack.pop()      // 出栈

// 作为队列使用
const queue = []
queue.push('顾客1') // 入队
queue.shift()      // 出队

// 甚至作为字典!
const dict = []
dict['name'] = '数组字典'
console.log(dict.name) // '数组字典' - 神奇!

比喻时刻:JavaScript数组像瑞士军刀 - 可以是栈(叠盘子)、队列(排队)、列表(购物单),甚至字典(电话本)。但这把军刀太灵活,有时会割到手 - 所以特定场景最好用专用工具(如Map、Set)。

💡 最佳实践指南

  1. 创建数组 :优先使用[]Array.of(),避免new Array(n)的空槽陷阱

    javascript 复制代码
    // 👍 推荐
    const safeArray = [1, 2, 3]
    const sizedArray = Array.from({length: 5}) // 无空槽
    
    // 👎 避免
    const riskyArray = new Array(5) // 幽灵公寓!
  2. 遍历选择

    • 需要索引 → for...of + entries()
    • 需要提前终止 → for循环
    • 简单迭代 → forEach
  3. 空槽检测 :使用in操作符或hasOwnProperty

    javascript 复制代码
    const phantomHotel = new Array(3)
    console.log(0 in phantomHotel) // false - 幽灵房间
  4. 高级转换 :复杂数组操作优先考虑Array.from+映射函数

🎓 总结:数组的哲学

JavaScript数组就像量子态容器 - 同时具备列表的秩序和字典的混沌。理解其双重特性:

  1. 有序列表面:通过索引组织元素,保持插入顺序
  2. 字典面 :可以添加任意属性,for...in会遍历所有可枚举属性

这种双重性既是JavaScript数组的强大之处,也是困惑之源。掌握空槽(empty slot)与undefined的区别,了解不同遍历方式的特性,才能驾驭这个"魔法口袋"。

当然数组的api不止这些,还有filter、find、some、every...

需要大家去属性,学习这些api,这些api在MDN文档都是有介绍的:MDN

相关推荐
吉吉6110 分钟前
Xss-labs攻关1-8
前端·xss
拾光拾趣录12 分钟前
HTML行内元素与块级元素
前端·css·html
小飞悟19 分钟前
JavaScript 数组精讲:创建与遍历全解析
前端·javascript
喝拿铁写前端33 分钟前
技术是决策与代价的平衡 —— 超大系统从 Vue 2 向 Vue 3 演进的思考
前端·vue.js·架构
拾光拾趣录40 分钟前
虚拟滚动 + 加载:让万级列表丝般顺滑
前端·javascript
然我1 小时前
数组的创建与遍历:从入门到精通,这些坑你踩过吗? 🧐
前端·javascript·面试
豆豆(设计前端)1 小时前
如何成为高级前端开发者:系统化成长路径。
前端·javascript·vue.js·面试·electron
今天你写算法了吗1 小时前
ScratchCard刮刮卡交互元素的实现
前端·javascript
谢尔登1 小时前
【React Native】布局和 Stack 、Slot
javascript·react native·react.js
FogLetter1 小时前
深入浅出 JavaScript 数组:从基础到高级玩法
前端·javascript