虽然普通对象默认不支持 for...of,但有多种方法可以实现:
1. 使用 Object 的辅助方法
遍历键名 (keys)
const obj = { a: 1, b: 2, c: 3 };
// 方法1: Object.keys()
for (const key of Object.keys(obj)) {
console.log(key); // 'a', 'b', 'c'
console.log(obj[key]); // 1, 2, 3
}
// 方法2: 使用数组的解构和循环
for (const key of Object.keys(obj)) {
const value = obj[key];
console.log(key, value);
}
遍历值 (values)
const obj = { a: 1, b: 2, c: 3 };
for (const value of Object.values(obj)) {
console.log(value); // 1, 2, 3
}
遍历键值对 (entries)
const obj = { a: 1, b: 2, c: 3 };
// 方法1: 使用数组解构
for (const [key, value] of Object.entries(obj)) {
console.log(key, value); // 'a' 1, 'b' 2, 'c' 3
}
// 方法2: 不使用解构
for (const entry of Object.entries(obj)) {
const key = entry[0];
const value = entry[1];
console.log(key, value);
}
2. 使对象本身可迭代 (实现 Symbol.iterator)
基本实现
const obj = {
name: 'John',
age: 30,
city: 'New York',
// 添加 Symbol.iterator 方法
[Symbol.iterator]: function* () {
// 遍历自身属性
for (const key of Object.keys(this)) {
yield [key, this[key]];
}
}
};
// 现在可以用 for...of 直接遍历
for (const [key, value] of obj) {
console.log(`${key}: ${value}`);
}
// 输出:
// name: John
// age: 30
// city: New York
只返回值的迭代器
const obj = {
a: 1,
b: 2,
c: 3,
[Symbol.iterator]: function* () {
for (const key of Object.keys(this)) {
yield this[key];
}
}
};
for (const value of obj) {
console.log(value); // 1, 2, 3
}
3. 自定义迭代行为
按特定顺序迭代
const user = {
firstName: 'John',
lastName: 'Doe',
age: 30,
email: 'john@example.com',
[Symbol.iterator]: function* () {
// 自定义迭代顺序
yield ['姓名', `${this.firstName} ${this.lastName}`];
yield ['年龄', this.age];
yield ['邮箱', this.email];
}
};
for (const [label, value] of user) {
console.log(`${label}: ${value}`);
}
// 输出:
// 姓名: John Doe
// 年龄: 30
// 邮箱: john@example.com
只迭代特定属性
const product = {
id: 1,
name: 'Laptop',
price: 999.99,
category: 'Electronics',
inStock: true,
[Symbol.iterator]: function* () {
// 只迭代非布尔值的属性
const keys = Object.keys(this);
for (const key of keys) {
if (typeof this[key] !== 'boolean') {
yield [key, this[key]];
}
}
}
};
for (const [key, value] of product) {
console.log(`${key}: ${value}`);
}
// 输出:
// id: 1
// name: Laptop
// price: 999.99
// category: Electronics
4. 类实例的可迭代性
class UserCollection {
constructor() {
this.users = [];
}
add(user) {
this.users.push(user);
}
// 使实例可迭代
*[Symbol.iterator]() {
for (const user of this.users) {
yield user;
}
}
// 或者返回键值对
*entries() {
for (let i = 0; i < this.users.length; i++) {
yield [i, this.users[i]];
}
}
}
const collection = new UserCollection();
collection.add({ name: 'Alice', age: 25 });
collection.add({ name: 'Bob', age: 30 });
// 遍历用户
for (const user of collection) {
console.log(user.name, user.age);
}
// 遍历带索引的用户
for (const [index, user] of collection.entries()) {
console.log(index, user.name);
}
5. 使用生成器函数
function* objectEntries(obj) {
for (const key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
const obj = { x: 10, y: 20, z: 30 };
for (const [key, value] of objectEntries(obj)) {
console.log(key, value);
}
6. 实际应用场景
场景1:遍历配置对象
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
debug: false
};
// 配置迭代器
config[Symbol.iterator] = function* () {
const keys = Object.keys(this).sort(); // 按字母排序
for (const key of keys) {
yield { key, value: this[key] };
}
};
for (const { key, value } of config) {
if (typeof value !== 'boolean') { // 过滤布尔值
console.log(`配置 ${key} = ${value}`);
}
}
场景2:处理嵌套对象
const company = {
name: 'TechCorp',
departments: {
engineering: { employees: 50, budget: 1000000 },
sales: { employees: 20, budget: 500000 },
marketing: { employees: 15, budget: 300000 }
}
};
// 扁平化迭代器
company[Symbol.iterator] = function* () {
yield ['公司名称', this.name];
for (const [dept, info] of Object.entries(this.departments)) {
yield [`${dept}部门人数`, info.employees];
yield [`${dept}部门预算`, info.budget];
}
};
for (const [label, value] of company) {
console.log(`${label}: ${value}`);
}
7. 性能考虑
const obj = {};
// 创建一个大对象
for (let i = 0; i < 1000000; i++) {
obj[`key${i}`] = i;
}
// 测试不同方法的性能
console.time('Object.keys + for...of');
for (const key of Object.keys(obj)) {
const value = obj[key];
}
console.timeEnd('Object.keys + for...of');
console.time('Object.entries + for...of');
for (const [key, value] of Object.entries(obj)) {
// 直接有值
}
console.timeEnd('Object.entries + for...of');
console.time('for...in');
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
}
}
console.timeEnd('for...in');
8. 实用工具函数
// 创建可迭代对象的工具函数
function makeIterable(obj, options = {}) {
const {
exclude = [], // 排除的属性
includeOnly = null, // 只包含的属性
sortKeys = false, // 是否按键排序
valueOnly = false // 只返回值
} = options;
const iterable = { ...obj };
iterable[Symbol.iterator] = function* () {
let keys = Object.keys(this);
// 过滤
if (exclude.length) {
keys = keys.filter(key => !exclude.includes(key));
}
if (includeOnly) {
keys = keys.filter(key => includeOnly.includes(key));
}
// 排序
if (sortKeys) {
keys.sort();
}
// 迭代
for (const key of keys) {
if (valueOnly) {
yield this[key];
} else {
yield [key, this[key]];
}
}
};
return iterable;
}
// 使用示例
const data = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const iterableData = makeIterable(data, {
exclude: ['e'],
sortKeys: true
});
for (const [key, value] of iterableData) {
console.log(key, value); // a 1, b 2, c 3, d 4
}
总结
- 最简单的方法:使用
Object.keys()、Object.values()或Object.entries() - 需要直接迭代对象:实现
Symbol.iterator方法 - 需要自定义迭代逻辑:使用生成器函数
- 考虑性能:
Object.entries()+for...of通常是性能和可读性的最佳平衡
// 推荐的最佳实践
const obj = { a: 1, b: 2, c: 3 };
// 1. 只需要键或值
for (const key of Object.keys(obj)) { }
for (const value of Object.values(obj)) { }
// 2. 需要键值对
for (const [key, value] of Object.entries(obj)) { }
// 3. 需要直接迭代对象(添加迭代器)
obj[Symbol.iterator] = function* () {
for (const [key, value] of Object.entries(this)) {
yield { key, value };
}
};