JS `Array.prototype.entries()`:获取键值对数组的迭代器

各位观众,各位朋友,欢迎来到今天的“数组探秘”特别节目!今天我们要聊的可是数组里的一个“小能手”——Array.prototype.entries()。 别看它名字平平无奇,用好了,能让你在处理数组的时候效率翻倍,代码也更优雅!

开场白:数组,你真的了解吗?

咱们都知道,数组是编程里最基础的数据结构之一。它就像一个有序的盒子,可以存放各种各样的东西,比如数字、字符串、对象,甚至是其他的数组!

但问题来了,当你需要同时访问数组的索引(也就是位置)和值的时候,你会怎么做?

  • 传统方法:for 循环

    最常见的方法就是用 for 循环,就像这样:

    const myArray = ['apple', 'banana', 'cherry'];
    
    for (let i = 0; i < myArray.length; i++) {
      console.log(`Index: ${i}, Value: ${myArray[i]}`);
    }

    这段代码没毛病,简单粗暴,但缺点也很明显:需要手动管理索引 i,容易出错,而且代码看起来有点冗长。

  • forEach 方法

    ES5 之后,我们有了 forEach 方法,可以简化代码:

    myArray.forEach((value, index) => {
      console.log(`Index: ${index}, Value: ${value}`);
    });

    forEach 看起来更简洁了,但它有一个致命的缺陷:无法提前终止循环(除非抛出异常,但这可不是好习惯)。

主角登场:entries() 的魅力

现在,让我们的主角 entries() 闪亮登场!entries() 方法会返回一个新的 Array Iterator 对象,这个迭代器对象包含了数组中每个索引的键值对。 简单来说,它会把数组变成一个“索引-值”对的序列。

  • 基本用法

    const myArray = ['apple', 'banana', 'cherry'];
    const iterator = myArray.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

    这段代码首先创建了一个迭代器 iterator,然后通过 iterator.next() 方法逐个获取键值对。每次调用 next() 方法,都会返回一个包含 valuedone 属性的对象。value 属性就是键值对数组,done 属性表示是否遍历完成。

    当所有元素都被遍历完后,next() 方法会返回一个 valueundefineddonetrue 的对象。

  • 配合 for...of 循环

    entries() 最常用的方式是和 for...of 循环配合使用:

    const myArray = ['apple', 'banana', 'cherry'];
    
    for (const [index, value] of myArray.entries()) {
      console.log(`Index: ${index}, Value: ${value}`);
    }

    这段代码看起来是不是很清爽?for...of 循环会自动处理迭代器的 next() 方法,并解构返回的键值对数组,直接得到索引和值。

entries() 的应用场景

entries() 在很多场景下都非常有用,下面我们来看几个例子:

  1. 查找特定元素的索引

    假设我们需要找到数组中第一个满足特定条件的元素的索引。使用 entries() 可以很方便地实现:

    const myArray = [10, 20, 30, 40, 50];
    
    function findIndexGreaterThan(arr, threshold) {
      for (const [index, value] of arr.entries()) {
        if (value > threshold) {
          return index;
        }
      }
      return -1; // 如果没有找到,返回 -1
    }
    
    const index = findIndexGreaterThan(myArray, 25);
    console.log(index); // 输出: 2

    这段代码定义了一个 findIndexGreaterThan 函数,用于查找数组中第一个大于给定阈值的元素的索引。for...of 循环可以让我们同时访问索引和值,并在找到目标元素后立即返回,避免了不必要的循环。

  2. 创建对象

    有时候,我们需要把数组转换成对象,其中数组的索引作为对象的键,值作为对象的值。entries() 也能派上用场:

    const myArray = ['apple', 'banana', 'cherry'];
    
    const myObject = {};
    for (const [index, value] of myArray.entries()) {
      myObject[index] = value;
    }
    
    console.log(myObject); // 输出: { '0': 'apple', '1': 'banana', '2': 'cherry' }

    这段代码创建了一个空对象 myObject,然后使用 for...of 循环遍历数组,把数组的索引作为对象的键,值作为对象的值。

    当然,更简洁的方法是使用 Object.fromEntries()

    const myArray = ['apple', 'banana', 'cherry'];
    const entries = myArray.entries();
    const myObject = Object.fromEntries(entries);
    
    console.log(myObject); // 输出: { '0': 'apple', '1': 'banana', '2': 'cherry' }

    Object.fromEntries() 方法会把一个键值对的列表转换成一个对象。它接受一个可迭代对象作为参数,比如 entries() 返回的迭代器。

  3. 修改数组元素

    有时候,我们需要根据元素的索引来修改数组中的元素。entries() 也可以简化代码:

    const myArray = [1, 2, 3, 4, 5];
    
    for (const [index, value] of myArray.entries()) {
      if (index % 2 === 0) { // 如果索引是偶数
        myArray[index] = value * 2; // 把元素乘以 2
      }
    }
    
    console.log(myArray); // 输出: [2, 2, 6, 4, 10]

    这段代码使用 for...of 循环遍历数组,如果元素的索引是偶数,就把元素乘以 2。

entries() 和其他方法的比较

  • entries() vs. for 循环

    特性 for 循环 entries()
    代码简洁性 较冗长 更简洁
    可读性 较低 较高
    易错性 较高,容易出错索引越界 较低,不容易出错
    灵活性 较高,可以自定义循环逻辑 较低,只能按顺序遍历数组
    适用场景 需要高度自定义循环逻辑时 需要同时访问索引和值时
  • entries() vs. forEach

    特性 forEach entries()
    代码简洁性 较高 较高
    可读性 较高 较高
    终止循环 无法提前终止 可以提前终止 (for...of 循环)
    适用场景 不需要提前终止循环,只需要遍历数组元素时 需要提前终止循环,或者需要同时访问索引和值时

注意事项

  • entries() 方法不会修改原始数组。它只是返回一个新的迭代器对象。
  • entries() 返回的迭代器只能使用一次。一旦遍历完成,就不能再次使用。
  • entries() 方法适用于所有类型的数组,包括稀疏数组。对于稀疏数组,entries() 会跳过空槽。

高级用法:自定义迭代器

entries() 返回的是一个迭代器对象,我们可以利用迭代器来实现一些高级的功能。

  • 自定义迭代逻辑

    我们可以创建一个自定义的迭代器,根据特定的规则来遍历数组。

    const myArray = [1, 2, 3, 4, 5];
    
    function* customIterator(arr) {
      for (let i = 0; i < arr.length; i++) {
        if (arr[i] % 2 === 0) { // 只迭代偶数
          yield [i, arr[i]];
        }
      }
    }
    
    for (const [index, value] of customIterator(myArray)) {
      console.log(`Index: ${index}, Value: ${value}`);
    }
    
    // 输出:
    // Index: 1, Value: 2
    // Index: 3, Value: 4

    这段代码定义了一个生成器函数 customIterator,它只迭代数组中的偶数。yield 关键字用于返回迭代器的值。

  • 无限迭代器

    我们可以创建一个无限迭代器,不断地生成新的值。

    function* infiniteIterator() {
      let i = 0;
      while (true) {
        yield i++;
      }
    }
    
    const iterator = infiniteIterator();
    
    console.log(iterator.next().value); // 输出: 0
    console.log(iterator.next().value); // 输出: 1
    console.log(iterator.next().value); // 输出: 2
    // ...

    这段代码定义了一个无限迭代器 infiniteIterator,它会不断地生成递增的数字。注意,在使用无限迭代器时要小心,避免造成死循环。

总结

Array.prototype.entries() 是一个非常实用的方法,它可以让我们更方便地访问数组的索引和值。配合 for...of 循环,可以写出更简洁、更易读的代码。

希望今天的讲座能帮助大家更好地理解和使用 entries() 方法。记住,熟练掌握数组的各种方法,是成为一名优秀的 JavaScript 程序员的必经之路!

课后作业

  1. 编写一个函数,接受一个数组和一个回调函数作为参数。该函数使用 entries() 方法遍历数组,并对每个元素执行回调函数。回调函数应该接受两个参数:元素的索引和值。
  2. 编写一个函数,接受一个数组作为参数。该函数使用 entries() 方法创建一个新的数组,其中每个元素都是一个包含原始数组元素的索引和值的对象。

下次再见! 祝大家编程愉快!

发表回复

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