各位观众,早上好!今天咱们来聊聊 JavaScript 数组里一个挺低调但有时又挺有用的家伙:Array.prototype.keys()
。别看它名字平平无奇,关键时刻能帮你解决不少问题。
啥是 Array.prototype.keys()
?
简单来说,Array.prototype.keys()
是一个方法,它会返回一个新的 迭代器 (iterator) 对象。这个迭代器会按照数组中元素的顺序,依次产生数组的 索引 (index)。
换句话说,它不是给你数组里的值,而是给你值的 位置。就像寻宝游戏,它给你的是藏宝图上的坐标,而不是直接把宝藏送到你面前。
语法
这玩意儿的语法简单得不能再简单了:
array.keys()
不需要任何参数,直接调用就行。
返回值
返回一个迭代器对象。这个迭代器会按顺序产生数组中每个元素的索引(从0开始)。
例子:最简单的用法
const arr = ['apple', 'banana', 'cherry'];
const iterator = arr.keys();
console.log(iterator.next().value); // 0
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // undefined (迭代结束)
看到了吧? iterator.next().value
会依次返回数组的索引 0、1 和 2。当迭代结束时,返回 undefined
。
为什么要用 Array.prototype.keys()
?
你可能会问:“直接用 for
循环不香吗?for (let i = 0; i < arr.length; i++)
多简单粗暴!”
没错,for
循环确实很直接。但是在某些情况下,Array.prototype.keys()
配合 for...of
循环会更优雅,更方便。尤其是在需要处理 稀疏数组 的时候。
稀疏数组:一个坑
稀疏数组是指数组中并非每个索引都有值。它们可能会有一些“空洞”。这些空洞的值实际上是 undefined
,但它们 不占用数组的长度。
const sparseArray = [1, , 3, , 5]; // 注意中间的两个逗号,表示空洞
console.log(sparseArray.length); // 5
console.log(sparseArray[1]); // undefined
console.log(sparseArray[3]); // undefined
如果用普通的 for
循环遍历稀疏数组,你会遍历到这些空洞,得到 undefined
。这可能不是你想要的。
const sparseArray = [1, , 3, , 5];
for (let i = 0; i < sparseArray.length; i++) {
console.log(`Index ${i}: ${sparseArray[i]}`);
}
// 输出:
// Index 0: 1
// Index 1: undefined
// Index 2: 3
// Index 3: undefined
// Index 4: 5
但是,如果用 Array.prototype.keys()
配合 for...of
循环,就可以跳过这些空洞,只处理实际有值的索引。
const sparseArray = [1, , 3, , 5];
for (const index of sparseArray.keys()) {
console.log(`Index ${index}: ${sparseArray[index]}`);
}
// 输出:
// Index 0: 1
// Index 2: 3
// Index 4: 5
看,空洞被完美地忽略了!
for...of
循环的威力
for...of
循环是 ES6 引入的一种新的循环方式,它可以遍历 可迭代对象 (iterable)。 数组、字符串、Map、Set 等都是可迭代对象。 Array.prototype.keys()
返回的迭代器对象正好可以被 for...of
循环遍历。
例子:配合 for...of
循环处理更复杂的情况
假设你有一个数组,里面存储的是一些对象。每个对象都有一个 id
属性。你想找到所有 id
大于 10 的对象的索引。
const objects = [
{ id: 5, name: 'apple' },
{ id: 12, name: 'banana' },
{ id: 8, name: 'cherry' },
{ id: 15, name: 'date' }
];
const indices = [];
for (const index of objects.keys()) {
if (objects[index].id > 10) {
indices.push(index);
}
}
console.log(indices); // [1, 3]
在这个例子中,我们用 Array.prototype.keys()
获取了数组的索引,然后用 for...of
循环遍历这些索引。在循环中,我们检查每个对象的 id
属性,如果 id
大于 10,就把对应的索引添加到 indices
数组中。
Array.prototype.entries()
和 Array.prototype.values()
:两个好兄弟
既然提到了 Array.prototype.keys()
,就不得不提一下它的两个好兄弟:Array.prototype.entries()
和 Array.prototype.values()
。
-
Array.prototype.entries()
: 返回一个迭代器对象,该迭代器会产生数组中每个元素的 键值对 (key-value pair)。 键就是索引,值就是对应索引上的元素。const arr = ['apple', 'banana', 'cherry']; const iterator = arr.entries(); console.log(iterator.next().value); // [0, 'apple'] console.log(iterator.next().value); // [1, 'banana'] console.log(iterator.next().value); // [2, 'cherry'] console.log(iterator.next().value); // undefined
配合
for...of
循环:const arr = ['apple', 'banana', 'cherry']; for (const [index, value] of arr.entries()) { console.log(`Index ${index}: ${value}`); } // 输出: // Index 0: apple // Index 1: banana // Index 2: cherry
-
Array.prototype.values()
: 返回一个迭代器对象,该迭代器会产生数组中每个元素的值。const arr = ['apple', 'banana', 'cherry']; const iterator = arr.values(); console.log(iterator.next().value); // 'apple' console.log(iterator.next().value); // 'banana' console.log(iterator.next().value); // 'cherry' console.log(iterator.next().value); // undefined
配合
for...of
循环:const arr = ['apple', 'banana', 'cherry']; for (const value of arr.values()) { console.log(value); } // 输出: // apple // banana // cherry
用表格总结一下这三个方法:
方法 | 返回值 |
---|---|
Array.prototype.keys() |
返回一个迭代器,产生数组的索引。 |
Array.prototype.values() |
返回一个迭代器,产生数组的值。 |
Array.prototype.entries() |
返回一个迭代器,产生数组的键值对([索引, 值])。 |
浏览器兼容性
这三个方法都是 ES6 引入的,所以需要注意浏览器的兼容性。一般来说,现代浏览器都支持这些方法。但是,如果你的代码需要在旧版本的浏览器上运行,就需要使用 polyfill。
Polyfill:给老浏览器打补丁
Polyfill 是一段代码,它可以为旧版本的浏览器提供新特性的支持。 如果浏览器不支持 Array.prototype.keys()
,你可以使用下面的 polyfill:
if (!Array.prototype.keys) {
Array.prototype.keys = function() {
let index = 0;
const array = this;
return {
next: function() {
if (index < array.length) {
return { value: index++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
};
}
这段代码会检查浏览器是否支持 Array.prototype.keys()
。如果不支持,就自己实现一个。 这段polyfill代码创建了一个实现了迭代器协议的对象,拥有next
方法,每次调用next
返回下一个索引直到数组结束。
同样的,你也可以为 Array.prototype.values()
和 Array.prototype.entries()
提供 polyfill。
一些高级用法和注意事项
-
修改原数组的影响: 如果在迭代过程中修改了原数组,迭代器的行为可能会变得难以预测。 最好不要在迭代过程中修改数组的结构(添加或删除元素)。 修改元素的值通常是安全的。
-
与扩展运算符一起使用: 你可以使用扩展运算符 (
...
) 将迭代器转换为数组。const arr = ['apple', 'banana', 'cherry']; const keysArray = [...arr.keys()]; console.log(keysArray); // [0, 1, 2]
-
自定义迭代器:
Array.prototype.keys()
返回的是一个 默认的 迭代器。 你可以自己实现一个更复杂的迭代器,来满足特定的需求。
总结
Array.prototype.keys()
是一个很有用的小工具,它可以帮助你更方便地遍历数组的索引。 尤其是在处理稀疏数组和需要同时访问索引和值的情况下,它可以让你的代码更简洁,更易读。虽然它不像 map()
、filter()
那样常用,但在某些场景下,它能发挥奇效。记住,工具不在于多,而在于用得巧。 掌握了 Array.prototype.keys()
,你就多了一件解决问题的利器。
希望今天的讲解对大家有所帮助! 咱们下次再见!