js迭代器模式:高效遍历数据的核心技巧

一、概述

迭代器模式(Iterator Pattern) 是一种行为型设计模式,其核心思想是:提供一种统一的方式来顺序访问聚合对象中的各个元素,而不暴露其内部结构

核心概念:

  • 迭代器(Iterator):负责定义访问和遍历元素的接口。
  • 聚合对象(Aggregate):包含元素集合,并能创建一个对应的迭代器。

优点:

  • 解耦遍历逻辑与业务逻辑:将遍历算法从业务代码中抽离出来,提高可维护性。
  • 支持多种遍历方式:如正序、倒序、过滤等。
  • 统一接口:对不同数据结构(数组、树、图等)提供一致的访问方式。

二、自己实现简单迭代器

我们可以手动实现一个简单的迭代器,模拟 Array.prototype.forEach 的行为。

示例代码:

function createIterator(array) {
  let index = 0;

  return {
    next: function () {
      return index < array.length ? { value: array[index++], done: false } : { done: true };
    }
  };
}

const iterator = createIterator(['apple', 'banana', 'orange']);

console.log(iterator.next()); // { value: "apple", done: false }
console.log(iterator.next()); // { value: "banana", done: false }
console.log(iterator.next()); // { value: "orange", done: false }
console.log(iterator.next()); // { done: true }

三、内部迭代器(Internal Iterator)

内部迭代器是指由迭代器本身控制遍历过程,调用者只需传入回调函数。

特点:

  • 遍历逻辑封装在迭代器内部。
  • 调用者无法控制遍历流程(如中断、跳过等)。
  • 常见于原生方法,如 Array.prototype.forEachmapfilter 等。

示例代码:

[1, 2, 3].forEach(function (item) {
  console.log(item);
});

四、外部迭代器(External Iterator)

外部迭代器是由调用者主动控制每次迭代的过程,具有更高的灵活性。

特点:

  • 控制权在调用者手中,可以随时开始、暂停、终止迭代。
  • 更适合复杂的数据结构或需要条件判断的遍历。

示例代码:

const iterator = createIterator([10, 20, 30]);

let current = iterator.next();
while (!current.done) {
  console.log(current.value);
  current = iterator.next();
}

五、迭代器适用对象类型

迭代器适用于以下类型的对象或结构:

数据结构是否支持
数组(Array)
类数组对象(arguments、NodeList)
Map / Set
字符串
自定义数据结构(如树、图)✅(需自定义迭代器)

自定义迭代器示例(针对树结构):

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 };
        const node = stack.pop();
        stack.push(...node.children.reverse());
        return { value: node.value, done: false };
      }
    };
  }
}

const tree = new TreeNode(1, [
  new TreeNode(2, [new TreeNode(4)]),
  new TreeNode(3)
]);

for (const val of tree) {
  console.log(val); // 输出:1, 2, 4, 3
}

六、倒序迭代器(Reverse Iterator)

倒序迭代器用于从后往前遍历集合。

实现方式:

function createReverseIterator(array) {
  let index = array.length - 1;

  return {
    next: function () {
      return index >= 0 ? { value: array[index--], done: false } : { done: true };
    }
  };
}

const reverseIter = createReverseIterator([1, 2, 3]);
console.log(reverseIter.next()); // { value: 3, done: false }
console.log(reverseIter.next()); // { value: 2, done: false }
console.log(reverseIter.next()); // { value: 1, done: false }
console.log(reverseIter.next()); // { done: true }

七、终止迭代器(Early Termination)

有些场景下我们希望提前终止迭代过程,例如在找到目标元素后停止遍历。

示例代码:

function findItem(iterator, target) {
  let result = iterator.next();
  while (!result.done) {
    if (result.value === target) {
      console.log('找到目标:', result.value);
      return result.value;
    }
    result = iterator.next();
  }
  console.log('未找到目标');
  return null;
}

const iter = createIterator([10, 20, 30]);
findItem(iter, 20); // 找到目标: 20

八、迭代器模式应用        

在 Vue 项目的实际开发中,迭代器模式不仅可用于处理数据结构的遍历,还可以用于统一访问接口、异步迭代器、动态渲染组件、分页加载、表单校验流程等复杂业务场景。以下是几个典型的 Vue 项目中使用迭代器模式的实际案例。

8.1 统一访问接口(适配器模式结合使用)

当处理多种数据结构时,可以通过统一的迭代器接口进行访问:

function findItem(iterator, target) {
  let result = iterator.next();
  while (!result.done) {
    if (result.value === target) {
      console.log('找到目标:', result.value);
      return result.value;
    }
    result = iterator.next();
  }
  console.log('未找到目标');
  return null;
}

const iter = createIterator([10, 20, 30]);
findItem(iter, 20); // 找到目标: 20

8.2 异步迭代器(Async Iteration)

ES2018 引入了异步迭代器,适用于流式数据处理、异步加载等场景。

async function* asyncIterator() {
  yield await fetch('https://api.example.com/data1');
  yield await fetch('https://api.example.com/data2');
}

(async () => {
  for await (const res of asyncIterator()) {
    console.log(res.status);
  }
})();

 8.3 组件树递归渲染 + 异步懒加载节点

class TreeNode {
  constructor(label, children = [], isLeaf = false, hasChildren = true) {
    this.label = label;
    this.children = children;
    this.isLeaf = isLeaf;
    this.hasChildren = hasChildren;
  }

  [Symbol.iterator]() {
    const stack = [this];
    const result = [];

    return {
      next: () => {
        if (stack.length === 0) return { done: true };

        const node = stack.shift();
        result.push(node);

        if (!node.isLeaf && node.hasChildren && node.children.length === 0) {
          // 模拟异步加载子节点
          fetch(`/api/load-children?node=${node.label}`)
            .then(res => res.json())
            .then(children => {
              node.children = children.map(c => new TreeNode(c.label));
              stack.push(...node.children);
            });
        } else {
          stack.push(...node.children);
        }

        return { value: node, done: false };
      }
    };
  }
}

// Vue 组件中使用
export default {
  data() {
    return {
      treeRoot: new TreeNode('根节点')
    };
  },
  mounted() {
    const iterator = this.treeRoot[Symbol.iterator]();
    let current = iterator.next();
    while (!current.done) {
      console.log(current.value.label);
      current = iterator.next();
    }
  },
  template: `
    <ul>
      <li v-for="node in traverseTree" :key="node.label">
        {{ node.label }}
        <ul v-if="node.children && node.children.length > 0">
          <tree-node v-for="child in node.children" :key="child.label" :node="child"/>
        </ul>
      </li>
    </ul>
  `
};

 8.4 动态表单验证

// 验证规则定义
const validators = {
  required: (value) => !!value,
  email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
  minLength: (length) => (value) => value.length >= length
};

// 表单字段配置
const formFields = [
  { name: 'username', rules: ['required'] },
  { name: 'email', rules: ['required', 'email'] },
  { name: 'password', rules: ['required', 'minLength(6)'] }
];

// 创建验证迭代器
function createValidatorIterator(form, validators) {
  const entries = Object.entries(form);
  let index = 0;

  return {
    next() {
      if (index >= entries.length) return { done: true };

      const [key, value] = entries[index++];
      const fieldConfig = formFields.find(f => f.name === key);

      if (!fieldConfig) return this.next();

      for (const rule of fieldConfig.rules) {
        let isValid = false;
        if (rule.startsWith('minLength')) {
          const len = parseInt(rule.match(/\d+/)[0]);
          isValid = validators.minLength(len)(value);
        } else {
          isValid = validators[rule]?.(value) ?? true;
        }

        if (!isValid) {
          throw new Error(`验证失败:${key} 不符合规则 ${rule}`);
        }
      }

      return this.next();
    }
  };
}

// 在 Vue 组件中使用
export default {
  data() {
    return {
      formData: {
        username: '',
        email: '',
        password: ''
      }
    };
  },
  methods: {
    submitForm() {
      const iterator = createValidatorIterator(this.formData, validators);
      try {
        while (true) {
          iterator.next();
        }
        alert('验证通过,提交成功');
      } catch (e) {
        alert(e.message);
      }
    }
  }
};

 8.5 分页加载

<template>
  <div class="container">
    <ul class="item-list">
      <li v-for="item in items" :key="item.id">{{ item.name }}</li>
    </ul>
    <button class="load-more" @click="loadNext" :disabled="loading || !hasMore">
      {{ loading ? '加载中...' : (hasMore ? '加载更多' : '没有更多') }}
    </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [],
      paginator: null,
      loading: false,
      hasMore: true
    };
  },
  mounted() {
    this.paginator = createPaginator(this.fetchData, 10);
  },
  methods: {
    async loadNext() {
      this.loading = true;
      const { value, done } = await this.paginator.next();
      if (value) this.items.push(...value);
      this.hasMore = !done;
      this.loading = false;
    },
    // 模拟 API 请求
    async fetchData(page, pageSize) {
      // 模拟网络延迟
      await new Promise(resolve => setTimeout(resolve, 800));

      // 模拟后端返回数据
      const startId = (page - 1) * pageSize + 1;
      const items = Array.from({ length: pageSize }, (_, i) => ({
        id: startId + i,
        name: `项目 #${startId + i}`
      }));

      // 假设只加载到第3页为止
      const hasNext = page < 3;

      return { items, hasNext };
    }
  }
};

// 创建分页迭代器
function createPaginator(fetchFn, pageSize = 10) {
  let page = 1,
    hasMore = true;

  return {
    [Symbol.asyncIterator]() {
      return this;
    },
    async next() {
      if (!hasMore) return { done: true };
      const data = await fetchFn(page++, pageSize);
      hasMore = data.hasNext;
      return { value: data.items, done: false };
    }
  };
}
</script>

<style scoped>
.container {
  max-width: 400px;
  margin: 20px auto;
  text-align: center;
}

.item-list {
  list-style: none;
  padding: 0;
  margin-bottom: 16px;
}

.item-list li {
  padding: 8px;
  border-bottom: 1px solid #eee;
}

.load-more {
  padding: 8px 16px;
  font-size: 14px;
  cursor: pointer;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
}

.load-more:disabled {
  background-color: #aaa;
  cursor: not-allowed;
}
</style>

九、JavaScript 原生迭代器支持

9.1 可迭代协议(Iterable Protocol)

任何实现了 [Symbol.iterator]() 方法的对象都称为可迭代对象。包括:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • arguments 对象
  • DOM collections(如 NodeList

9.2 迭代器协议(Iterator Protocol)

一个对象如果具备 next() 方法并返回 {value, done} 形式的对象,则称为迭代器对象


十、总结与建议

场景推荐方式
简单遍历内部迭代器(如 forEach
复杂控制外部迭代器(自定义 next()
自定义结构实现 [Symbol.iterator]
异步数据流异步迭代器 async/await + yield
提前退出主动 break 或 return
倒序遍历倒序迭代器
统一接口迭代器 + 工厂函数

十一、参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值