JS `Array.prototype.entries()` / `keys()` / `values()`:获取迭代器

Alright, buckle up everyone,因为今天我们要聊聊JavaScript数组里三个有点意思的小伙伴:entries(), keys(), 和 values()。 别被“迭代器”这个词吓到,听我慢慢道来,保证你听完之后能像用筷子一样熟练地使用它们!

迭代器是个啥玩意儿?

首先,咱们得搞清楚“迭代器”是个什么东东。 简单来说,迭代器就是一个对象,它允许你按顺序访问一个集合(比如数组、Map、Set等等)中的元素,而不需要知道这个集合内部的实现细节。

你可以把它想象成一个唱片机的唱头。 唱片就是你的数组,唱头(迭代器)沿着唱片上的音轨(数组的元素)移动,每次“读”出一个声音(一个元素)。 你不需要知道唱片是怎么制作的,只需要让唱头按顺序播放就可以了。

JavaScript中的迭代器都有一个共同的方法:next()。 每次调用next(),它会返回一个对象,这个对象包含两个属性:

  • value: 当前迭代到的元素的值。
  • done: 一个布尔值,表示迭代是否完成。 如果donetrue,就说明已经迭代到集合的末尾了,value的值通常是undefined

entries(): 键值对黄金搭档

Array.prototype.entries()方法会返回一个新的数组迭代器对象,这个迭代器会生成数组中每个索引的键值对 (index, value)。

简单来说,它会把数组的每个元素都变成一个 [索引, 值] 的小数组。

语法:

array.entries()

例子:

const fruits = ['apple', 'banana', 'orange'];
const iterator = fruits.entries();

console.log(iterator.next().value); // 输出: [0, 'apple']
console.log(iterator.next().value); // 输出: [1, 'banana']
console.log(iterator.next().value); // 输出: [2, 'orange']
console.log(iterator.next().done);  // 输出: false
console.log(iterator.next().value); // 输出: undefined
console.log(iterator.next().done);  // 输出: true

进阶用法: for...of循环的完美伴侣

entries() 最常用的场景是结合 for...of 循环来遍历数组的索引和值。 这样做比传统的 for 循环更简洁,也更易读。

const colors = ['red', 'green', 'blue'];

for (const [index, color] of colors.entries()) {
  console.log(`Color at index ${index} is ${color}`);
}

// 输出:
// Color at index 0 is red
// Color at index 1 is green
// Color at index 2 is blue

再进阶一点:解构赋值的骚操作

上面的例子中,我们使用了数组解构赋值 [index, color] 来直接获取索引和值。 如果你觉得还不够炫酷,可以试试更骚的操作:

const data = ['name', 'Alice', 'age', 30, 'city', 'New York'];

for (const [keyIndex, key] of data.filter((_, i) => i % 2 === 0).entries()) {
  const valueIndex = keyIndex * 2 + 1;
  const value = data[valueIndex];
  console.log(`${key}: ${value}`);
}

// 输出
// name: Alice
// age: 30
// city: New York

这个例子稍微复杂一点,使用了 filter 方法来筛选出所有的键,然后用 entries() 获取键的索引。 再根据键的索引计算出对应的值的索引。 虽然有点绕,但是展示了 entries() 的一些灵活的用法。

表格总结:

方法 返回值 迭代内容 适用场景
entries() 数组迭代器对象 [索引, 值] 需要同时访问数组的索引和值时

keys():索引界的扛把子

Array.prototype.keys() 方法会返回一个新的数组迭代器对象,这个迭代器会生成数组中每个元素的索引 (key)。

简单来说,它只会给你数组的索引,而忽略元素的值。

语法:

array.keys()

例子:

const animals = ['dog', 'cat', 'bird'];
const iterator = animals.keys();

console.log(iterator.next().value); // 输出: 0
console.log(iterator.next().value); // 输出: 1
console.log(iterator.next().value); // 输出: 2
console.log(iterator.next().done);  // 输出: false
console.log(iterator.next().value); // 输出: undefined
console.log(iterator.next().done);  // 输出: true

进阶用法: 配合 for...of 循环遍历索引

虽然直接用 for (let i = 0; i < array.length; i++) 也能遍历索引,但是 keys() 配合 for...of 循环可以更简洁地实现相同的功能。

const numbers = [10, 20, 30];

for (const index of numbers.keys()) {
  console.log(`Element at index ${index} is ${numbers[index]}`);
}

// 输出:
// Element at index 0 is 10
// Element at index 1 is 20
// Element at index 2 is 30

再进阶一点:创建索引数组

有时候,你可能需要一个包含数组所有索引的数组。 可以使用 Array.from() 方法将 keys() 返回的迭代器转换成数组。

const letters = ['a', 'b', 'c', 'd'];
const indices = Array.from(letters.keys());

console.log(indices); // 输出: [0, 1, 2, 3]

实用场景:处理稀疏数组

keys() 在处理稀疏数组时非常有用。 稀疏数组是指包含空槽的数组,这些空槽并没有实际的值,但仍然会占据索引位置。

const sparseArray = new Array(5); // 创建一个长度为5的稀疏数组
sparseArray[1] = 'hello';
sparseArray[3] = 'world';

console.log(sparseArray); // 输出: [ <1 empty item>, 'hello', <1 empty item>, 'world', <1 empty item> ]

for (const index of sparseArray.keys()) {
  console.log(`Index: ${index}`);
}

// 输出:
// Index: 0
// Index: 1
// Index: 2
// Index: 3
// Index: 4

即使数组中有空槽,keys() 仍然会遍历所有的索引。 这使得它成为检测稀疏数组中存在哪些索引的利器。

表格总结:

方法 返回值 迭代内容 适用场景
keys() 数组迭代器对象 索引 (Key) 需要遍历数组的索引时,尤其是在稀疏数组中

values(): 专注价值的典范

Array.prototype.values() 方法会返回一个新的数组迭代器对象,这个迭代器会生成数组中每个元素的 (value)。

顾名思义,它只会关注数组的值,而忽略索引。 实际上,在很多情况下,它可以直接被 for...of 循环替代,因为 for...of 循环默认就是遍历数组的值。

语法:

array.values()

例子:

const planets = ['Mercury', 'Venus', 'Earth'];
const iterator = planets.values();

console.log(iterator.next().value); // 输出: 'Mercury'
console.log(iterator.next().value); // 输出: 'Venus'
console.log(iterator.next().value); // 输出: 'Earth'
console.log(iterator.next().done);  // 输出: false
console.log(iterator.next().value); // 输出: undefined
console.log(iterator.next().done);  // 输出: true

进阶用法: 和 for...of 循环殊途同归

实际上,下面的两种写法是等价的:

const fruits = ['apple', 'banana', 'orange'];

// 使用 values() 方法
for (const fruit of fruits.values()) {
  console.log(fruit);
}

// 使用 for...of 循环 (更简洁)
for (const fruit of fruits) {
  console.log(fruit);
}

// 输出:
// apple
// banana
// orange

values() 的特殊价值:兼容性

虽然 for...of 循环更简洁,但在一些旧版本的浏览器或 JavaScript 环境中可能不支持 for...of 循环。 在这种情况下,可以使用 values() 方法来获得一个迭代器,然后手动调用 next() 方法来遍历数组。

当然,现在这种情况已经很少见了,所以 values() 方法在实际开发中的使用频率相对较低。

表格总结:

方法 返回值 迭代内容 适用场景
values() 数组迭代器对象 值 (Value) 需要遍历数组的值时 (通常可以用 for…of 循环替代)

总结: 掌握迭代器的力量

总而言之,entries(), keys(), 和 values() 这三个方法都返回数组迭代器对象,它们分别用于遍历数组的键值对、索引和值。 虽然在很多情况下,可以使用更简洁的 for...of 循环来替代它们,但在某些特殊场景下,它们仍然非常有用。

记住,迭代器是一种强大的工具,它可以让你更灵活地访问和操作集合中的元素。 掌握迭代器的概念,不仅可以更好地理解 JavaScript 中的数组,还可以帮助你更好地理解其他类型的集合,比如 Map 和 Set。

希望今天的讲解对你有所帮助! 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注