for...in
试一试
const object = { a: 1, b: 2, c: 3 };
for (const property in object) {
console.log(`${property}: ${object[property]}`);
}
// Expected output:
// "a: 1"
// "b: 2"
// "c: 3"
语法
for (variable in object)
statement
参数
描述
循环将遍历对象自身的所有可枚举属性,以及对象从其原型链继承的属性(距离对象原型链更近的原型属性优先于距离对象更远的原型属性)。
像其他循环语句一样,你可以在 statement 中使用控制流语句
for...in 循环只遍历可枚举的、非符号属性。由 Array 和 Object 等内置构造函数创建的对象从 Array.prototype 和 Object.prototype 继承了不可枚举的属性,例如 Array 的 indexOf() 方法或 Object 的 toString() 方法,这些属性在 for...in 循环中不会被访问。
根据现代 ECMAScript 规范,遍历顺序是明确定义的,并且在不同实现中保持一致。在原型链的每个组成部分中,所有非负整数键(可以作为数组索引的键)将首先按值升序遍历,然后其他字符串键按属性创建的升序时间顺序遍历。
for...in 的 variable 部分接受任何可以放在 = 运算符前面的内容。你可以使用 const 声明变量,只要它在循环体内部不被重新赋值即可(它可以在迭代之间改变,因为那是两个独立的变量)。否则,你可以使用 let。你可以使用解构来赋值多个局部变量,或者使用属性访问器,例如 for (x.y in iterable) 将值赋值给对象属性。但是,不允许使用 using 和 await using,因为变量始终是字符串或符号,而这些声明需要一次性对象。
已删除、添加或修改的属性
for...in 以下列方式访问属性键
- 它首先获取当前对象的所有自有字符串键,其方式与
Object.getOwnPropertyNames()非常相似。 - 对于每个键,如果从未访问过具有相同值的字符串,则检索属性描述符,并且仅当属性可枚举时才访问该属性。但是,即使该属性字符串不可枚举,它也会被标记为已访问。
- 然后,当前对象被替换为其原型,并重复该过程。
这意味着
- 在迭代期间添加到当前对象的任何属性都不会被访问,因为当前对象的所有自有属性都已事先保存。
- 如果原型链中的多个对象具有相同名称的属性,则只会考虑第一个,并且只有当它可枚举时才会被访问。如果它不可枚举,则原型链中具有相同名称的其他属性都不会被访问,即使它们可枚举。
通常,最好不要在迭代期间添加、修改或删除对象的属性,除非是当前正在访问的属性。规范明确允许实现在以下情况之一中不遵循上述算法:
- 在迭代期间对象的原型链被修改。
- 在迭代期间从对象或其原型链中删除属性。
- 在迭代期间向对象的原型链添加属性。
- 在迭代期间属性的可枚举性发生改变。
在这些情况下,实现的行为可能与你预期的不同,甚至彼此之间也可能不同。
数组迭代和 for...in
数组索引只是具有整数名称的可枚举属性,并且与一般的对象属性相同。for...in 循环将先遍历所有整数键,然后遍历其他键,并且严格按照递增顺序,这使得 for...in 的行为接近正常的数组迭代。但是,for...in 循环将返回所有可枚举属性,包括那些具有非整数名称和那些继承的属性。与 for...of 不同,for...in 使用属性枚举而不是数组的迭代器。在稀疏数组中,for...of 将访问空槽,但 for...in 不会。
最好使用带有数字索引的 for 循环、Array.prototype.forEach() 或 for...of 循环,因为它们将以数字而不是字符串的形式返回索引,并且还可以避免非索引属性。
仅遍历自有属性
如果你只想考虑附加到对象本身的属性,而不是其原型的属性,你可以使用以下技术之一:
Object.keys 将返回可枚举的自有字符串属性列表,而 Object.getOwnPropertyNames 也会包含不可枚举的属性。
许多 JavaScript 样式指南和代码检查工具不建议使用 for...in,因为它会遍历整个原型链,这通常不是我们想要的,并且可能会与更广泛使用的 for...of 循环混淆。for...in 最实际的用途是用于调试目的,作为一种检查对象属性的简单方法(通过输出到控制台或其他方式)。在对象用作临时键值对的情况下,for...in 允许你检查这些键中是否包含特定值。
示例
使用 for...in
下面的 for...in 循环遍历对象的所有可枚举、非符号属性,并记录属性名称及其值的字符串。
const obj = { a: 1, b: 2, c: 3 };
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
// Logs:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"
遍历自有属性
以下函数演示了 Object.hasOwn() 的使用:不显示继承的属性。
const triangle = { a: 1, b: 2, c: 3 };
function ColoredTriangle() {
this.color = "red";
}
ColoredTriangle.prototype = triangle;
const obj = new ColoredTriangle();
for (const prop in obj) {
if (Object.hasOwn(obj, prop)) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
}
// Logs:
// "obj.color = red"
并发修改
警告:你不应该自己编写这样的代码。这里包含它只是为了说明 for...in 在某些极端情况下的行为。
在迭代期间添加到当前对象的属性永远不会被访问
const obj = { a: 1, b: 2 };
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
obj.c = 3;
}
// Logs:
// obj.a = 1
// obj.b = 2
被遮蔽的属性只被访问一次
const proto = { a: 1 };
const obj = { __proto__: proto, a: 2 };
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
// Logs:
// obj.a = 2
Object.defineProperty(obj, "a", { enumerable: false });
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
// Logs nothing, because the first "a" property visited is non-enumerable.
此外,请考虑以下情况,其中行为未指定,并且实现往往与指定算法不同:
在迭代期间改变原型
const obj = { a: 1, b: 2 };
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
Object.setPrototypeOf(obj, { c: 3 });
}
在迭代期间删除属性
const obj = { a: 1, b: 2, c: 3 };
// Deleting a property before it is visited
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
delete obj.c;
}
const obj2 = { a: 1, b: 2, c: 3 };
// Deleting a property after it is visited
for (const prop in obj2) {
console.log(`obj2.${prop} = ${obj2[prop]}`);
delete obj2.a;
}
在迭代期间添加到原型的可枚举属性
const proto = {};
const obj = { __proto__: proto, a: 1, b: 2 };
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
proto.c = 3;
}
在迭代期间改变属性的可枚举性
const obj = { a: 1, b: 2, c: 3 };
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
Object.defineProperty(obj, "c", { enumerable: false });
}
规范
| 规范 |
|---|
| ECMAScript® 2026 语言规范 # sec-for-in-and-for-of-statements |
浏览器兼容性
加载中…