fe-interview前端迭代器模式:集合遍历访问

fe-interview前端迭代器模式:集合遍历访问

【免费下载链接】fe-interview haizlin/fe-interview: 前端面试指南,包含大量的前端面试题及参考答案,适合用于准备前端面试。 【免费下载链接】fe-interview 项目地址: https://gitcode.com/GitHub_Trending/fe/fe-interview

引言:为什么需要迭代器模式?

在日常前端开发中,我们经常需要处理各种数据集合的遍历操作。从简单的数组遍历到复杂的树形结构处理,如何优雅、高效地访问集合元素成为了每个开发者必须面对的挑战。传统的for循环虽然功能强大,但在复杂场景下往往显得笨重且难以维护。

迭代器模式(Iterator Pattern)正是为了解决这一问题而生的设计模式。它提供了一种统一的方式来遍历各种集合对象,而无需暴露其内部表示。本文将深入探讨迭代器模式在前端开发中的应用,结合fe-interview项目中的实际面试题,为你揭示集合遍历的最佳实践。

什么是迭代器模式?

核心概念

迭代器模式是一种行为设计模式,它允许你顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。在JavaScript中,迭代器模式通过迭代器协议(Iterator Protocol) 来实现。

// 迭代器协议的基本结构
const iterator = {
  next() {
    return {
      value: any,    // 当前迭代的值
      done: boolean  // 是否迭代完成
    }
  }
}

迭代器 vs 可迭代对象

理解迭代器模式,首先要区分两个重要概念:

概念描述示例
迭代器(Iterator)具有next()方法的对象,用于遍历集合array[Symbol.iterator]()
可迭代对象(Iterable)实现了[Symbol.iterator]方法的对象Array, Map, Set, String
// 可迭代对象示例
const iterable = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step <= 3) {
          return { value: step, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (const value of iterable) {
  console.log(value); // 1, 2, 3
}

JavaScript内置迭代器实现

数组迭代器

数组是JavaScript中最常用的可迭代对象,其迭代器实现非常高效:

const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

Map和Set迭代器

ES6引入的Map和Set也实现了迭代器协议:

// Map迭代器
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
  console.log(key, value); // a 1, b 2
}

// Set迭代器
const set = new Set([1, 2, 3]);
for (const value of set) {
  console.log(value); // 1, 2, 3
}

字符串迭代器

字符串也是可迭代对象,可以正确处理Unicode字符:

const str = "前端";
for (const char of str) {
  console.log(char); // "前", "端"
}

自定义迭代器实现

实现一个范围迭代器

让我们实现一个简单的数字范围迭代器:

class Range {
  constructor(start, end, step = 1) {
    this.start = start;
    this.end = end;
    this.step = step;
  }

  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    const step = this.step;

    return {
      next() {
        if ((step > 0 && current <= end) || (step < 0 && current >= end)) {
          const value = current;
          current += step;
          return { value, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
}

// 使用示例
const range = new Range(1, 5);
for (const num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

树形结构迭代器

处理树形数据时,迭代器模式特别有用:

class TreeNode {
  constructor(value, children = []) {
    this.value = value;
    this.children = children;
  }

  // 深度优先遍历迭代器
  [Symbol.iterator]() {
    const stack = [this];
    
    return {
      next() {
        if (stack.length === 0) {
          return { done: true, value: undefined };
        }
        
        const node = stack.pop();
        // 将子节点逆序压入栈中,保证正序遍历
        for (let i = node.children.length - 1; i >= 0; i--) {
          stack.push(node.children[i]);
        }
        
        return { done: false, value: node.value };
      }
    };
  }
}

// 使用示例
const tree = new TreeNode('root', [
  new TreeNode('child1', [
    new TreeNode('grandchild1'),
    new TreeNode('grandchild2')
  ]),
  new TreeNode('child2')
]);

for (const value of tree) {
  console.log(value); // root, child1, grandchild1, grandchild2, child2
}

生成器函数与迭代器

ES6的生成器函数极大地简化了迭代器的创建:

基本生成器

function* numberGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = numberGenerator();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: undefined, done: true }

无限序列生成器

function* fibonacci() {
  let [prev, curr] = [0, 1];
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5

异步迭代器

ES2018引入了异步迭代器,用于处理异步数据流:

async function* asyncNumberGenerator() {
  for (let i = 0; i < 3; i++) {
    // 模拟异步操作
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

(async () => {
  for await (const num of asyncNumberGenerator()) {
    console.log(num); // 0, 1, 2 (每隔100ms输出)
  }
})();

迭代器模式在实际项目中的应用

数据处理管道

迭代器模式可以构建高效的数据处理管道:

function* filter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item;
    }
  }
}

function* map(iterable, mapper) {
  for (const item of iterable) {
    yield mapper(item);
  }
}

function* take(iterable, count) {
  let taken = 0;
  for (const item of iterable) {
    if (taken++ >= count) break;
    yield item;
  }
}

// 使用管道处理数据
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = take(
  map(
    filter(numbers, n => n % 2 === 0),
    n => n * 2
  ),
  3
);

console.log([...result]); // [4, 8, 12]

分页数据加载

迭代器模式非常适合处理分页数据:

async function* paginatedData(fetchPage, pageSize = 10) {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const data = await fetchPage(page, pageSize);
    if (data.length === 0) {
      hasMore = false;
      break;
    }
    
    for (const item of data) {
      yield item;
    }
    
    page++;
  }
}

// 模拟API调用
async function fetchPage(page, pageSize) {
  // 模拟网络延迟
  await new Promise(resolve => setTimeout(resolve, 100));
  const start = (page - 1) * pageSize + 1;
  const end = start + pageSize - 1;
  return Array.from({ length: pageSize }, (_, i) => start + i);
}

// 使用分页迭代器
(async () => {
  const dataIterator = paginatedData(fetchPage, 5);
  for await (const item of dataIterator) {
    console.log(item); // 1, 2, 3, 4, 5, 6, 7, ... 
    if (item >= 15) break; // 只取前15条
  }
})();

迭代器模式的性能优化

惰性求值(Lazy Evaluation)

迭代器模式天然支持惰性求值,只在需要时才计算值:

function* bigDataGenerator() {
  for (let i = 0; i < 1000000; i++) {
    yield expensiveCalculation(i);
  }
}

function expensiveCalculation(n) {
  console.log(`Calculating for ${n}`);
  return n * 2;
}

// 只计算前3个值
const iterator = bigDataGenerator();
console.log(iterator.next().value); // Calculating for 0 → 0
console.log(iterator.next().value); // Calculating for 1 → 2
console.log(iterator.next().value); // Calculating for 2 → 4

内存效率对比

与传统数组操作相比,迭代器模式在内存使用上更有优势:

// 传统方式 - 需要创建中间数组
const bigArray = Array.from({ length: 1000000 }, (_, i) => i);
const result = bigArray
  .filter(x => x % 2 === 0)
  .map(x => x * 2)
  .slice(0, 10);

console.log(result); // 创建了多个中间数组,内存占用高

// 迭代器方式 - 惰性计算,内存友好
function* processData(array) {
  for (const item of array) {
    if (item % 2 === 0) {
      yield item * 2;
    }
  }
}

const iterator = processData(bigArray);
const finalResult = [];
for (let i = 0; i < 10; i++) {
  const next = iterator.next();
  if (next.done) break;
  finalResult.push(next.value);
}

console.log(finalResult); // 相同结果,但内存占用更少

常见面试题解析

题目1:实现一个可迭代的类

问题:创建一个类,使其实例可以被for...of循环遍历。

class CustomCollection {
  constructor(data) {
    this.data = data;
  }

  [Symbol.iterator]() {
    let index = 0;
    const data = this.data;
    
    return {
      next() {
        if (index < data.length) {
          return { value: data[index++], done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
}

// 测试
const collection = new CustomCollection([1, 2, 3]);
for (const item of collection) {
  console.log(item); // 1, 2, 3
}

题目2:实现zip函数

问题:实现一个zip函数,将多个可迭代对象合并。

function* zip(...iterables) {
  const iterators = iterables.map(iterable => iterable[Symbol.iterator]());
  
  while (true) {
    const results = iterators.map(iterator => iterator.next());
    if (results.some(result => result.done)) break;
    yield results.map(result => result.value);
  }
}

// 测试
const result = zip([1, 2, 3], ['a', 'b', 'c'], [true, false, true]);
console.log([...result]); // [[1, 'a', true], [2, 'b', false], [3, 'c', true]]

题目3:扁平化嵌套数组

问题:使用迭代器实现数组的深度扁平化。

function* flattenDeep(array) {
  for (const item of array) {
    if (Array.isArray(item)) {
      yield* flattenDeep(item);
    } else {
      yield item;
    }
  }
}

// 测试
const nested = [1, [2, [3, 4], 5], 6];
console.log([...flattenDeep(nested)]); // [1, 2, 3, 4, 5, 6]

迭代器模式的最佳实践

1. 错误处理

class SafeIterator {
  constructor(iterable) {
    this.iterable = iterable;
  }

  [Symbol.iterator]() {
    const iterator = this.iterable[Symbol.iterator]();
    
    return {
      next() {
        try {
          return iterator.next();
        } catch (error) {
          console.error('Iterator error:', error);
          return { done: true, value: undefined };
        }
      }
    };
  }
}

2. 资源清理

function* resourceGenerator() {
  const resource = acquireResource();
  try {
    while (resource.hasMore()) {
      yield resource.getNext();
    }
  } finally {
    resource.release();
  }
}

3. 性能监控

function* monitoredIterator(iterable, name = 'unknown') {
  let count = 0;
  const startTime = Date.now();
  
  for (const item of iterable) {
    count++;
    yield item;
  }
  
  const endTime = Date.now();
  console.log(`Iterator ${name}: ${count} items in ${endTime - startTime}ms`);
}

总结与展望

迭代器模式是现代JavaScript编程中不可或缺的重要模式。通过本文的深入探讨,我们可以看到:

  1. 统一接口:迭代器模式为各种数据集合提供了统一的遍历接口
  2. 惰性计算:支持按需计算,提高内存使用效率
  3. 组合性:可以轻松组合多个迭代操作形成处理管道
  4. 异步支持:异步迭代器为流式数据处理提供了强大支持

在实际开发中,合理运用迭代器模式可以:

  • 提高代码的可读性和可维护性
  • 优化内存使用和性能表现
  • 增强代码的复用性和扩展性

随着JavaScript语言的不断发展,迭代器模式在前端开发中的应用将会越来越广泛。掌握这一模式,不仅有助于通过技术面试,更能提升你的实际开发能力。

记住:优秀的开发者不是知道所有答案的人,而是知道如何找到最佳解决方案的人。迭代器模式就是你工具箱中又一个强大的工具!

【免费下载链接】fe-interview haizlin/fe-interview: 前端面试指南,包含大量的前端面试题及参考答案,适合用于准备前端面试。 【免费下载链接】fe-interview 项目地址: https://gitcode.com/GitHub_Trending/fe/fe-interview

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值