您现在的位置是:网站首页 > 对象枚举与迭代文章详情

对象枚举与迭代

在JavaScript中,对象枚举与迭代是处理数据结构的基础操作。对象属性的遍历方式多样,不同方法适用于不同场景,而现代ES6+语法进一步简化了迭代流程。

对象属性的枚举方法

for...in循环

for...in 是遍历对象可枚举属性的经典方法,但会遍历原型链上的属性:

const person = { 
  name: 'Alice', 
  age: 28 
};
Object.prototype.gender = 'female';

for (const key in person) {
  console.log(key); // 输出 name, age, gender
}

可通过hasOwnProperty过滤原型属性:

for (const key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key); // 只输出 name, age
  }
}

Object.keys()

获取对象自身可枚举属性的数组:

const keys = Object.keys(person); // ['name', 'age']
keys.forEach(key => {
  console.log(`${key}: ${person[key]}`);
});

Object.getOwnPropertyNames()

包含不可枚举属性(如数组length):

const arr = ['a', 'b'];
console.log(Object.getOwnPropertyNames(arr)); 
// ['0', '1', 'length']

现代迭代协议

Symbol.iterator

实现可迭代对象:

const myIterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
};

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

生成器函数

动态生成迭代序列:

function* idGenerator() {
  let id = 0;
  while(true) {
    yield id++;
  }
}

const gen = idGenerator();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1

Map与Set的迭代

Map的三种迭代器

const map = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);

// 键值对迭代
for (const [key, value] of map.entries()) {
  console.log(`${key} => ${value}`);
}

// 单独获取键或值
console.log([...map.keys()]); // ['key1', 'key2']
console.log([...map.values()]); // ['value1', 'value2']

Set的迭代特性

const set = new Set([1, 2, 3]);

// Set可直接迭代
for (const value of set) {
  console.log(value); // 1, 2, 3
}

// 转换为数组
console.log([...set]); // [1, 2, 3]

性能比较与实践建议

遍历速度测试

const bigObj = {};
for (let i = 0; i < 1000000; i++) {
  bigObj[`key${i}`] = i;
}

// 测试for...in
console.time('for...in');
for (const key in bigObj) {}
console.timeEnd('for...in');

// 测试Object.keys
console.time('Object.keys');
Object.keys(bigObj).forEach(() => {});
console.timeEnd('Object.keys');

典型结果:

  • for...in: ~120ms
  • Object.keys+forEach: ~90ms

不可枚举属性的处理

const obj = {};
Object.defineProperty(obj, 'hidden', {
  value: 'secret',
  enumerable: false
});

// 使用Reflect.ownKeys获取所有键
console.log(Reflect.ownKeys(obj)); // ['hidden']

特殊场景处理

类数组对象迭代

function printArgs() {
  // arguments是类数组对象
  Array.from(arguments).forEach(arg => {
    console.log(arg);
  });
}

printArgs(1, 'a', true);

深度遍历对象

递归实现深度迭代:

function* deepTraverse(obj) {
  for (const [key, value] of Object.entries(obj)) {
    yield [key, value];
    if (typeof value === 'object' && value !== null) {
      yield* deepTraverse(value);
    }
  }
}

const nestedObj = { a: 1, b: { c: 2, d: { e: 3 } } };
for (const [key, value] of deepTraverse(nestedObj)) {
  console.log(`${key}: ${value}`);
}

迭代器的高级应用

组合迭代器

function* chainIterables(...iterables) {
  for (const iterable of iterables) {
    yield* iterable;
  }
}

const combined = chainIterables(
  [1, 2],
  new Set([3, 4]),
  'hello'
);

console.log([...combined]); // [1, 2, 3, 4, 'h', 'e', 'l', 'l', 'o']

异步迭代

处理流式数据:

async function* asyncNumbers() {
  let i = 0;
  while (i < 3) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i++;
  }
}

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

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步