for...in
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
尝试一下
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
参数
描述
该循环将迭代对象本身的所有可枚举属性,以及对象从其原型链继承的属性(原型链中较近的原型的属性优先于较远的原型的属性)。
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)
)将值赋给对象属性。
一种遗留的语法允许在 for...in
循环中的 var
声明的循环变量具有初始值。在严格模式下,这会抛出语法错误,而在非严格模式下则会被忽略。
删除、添加或修改的属性
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]}`);
}
// 输出:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"
迭代自有属性
下面的函数展示了 Object.hasOwn()
的用法:继承的属性不会显示。
const triangle = { a: 1, b: 2, c: 3 };
function ColoredTriangle() {
this.color = "红色";
}
ColoredTriangle.prototype = triangle;
const obj = new ColoredTriangle();
for (const prop in obj) {
if (Object.hasOwn(obj, prop)) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
}
// 输出:
// "obj.color = 红色"
并发修改
警告:
你不应该自己编写类似的代码。这里仅仅展示了 for...in
在某些极端情况下的行为。
在迭代期间添加到当前对象的属性永远不会被访问:
const obj = { a: 1, b: 2 };
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
obj.c = 3;
}
// 输出:
// obj.a = 1
// obj.b = 2
被遮蔽(shadowed)的属性只会被访问一次:
const proto = { a: 1 };
const obj = { __proto__: proto, a: 2 };
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
// 输出:
// obj.a = 2
Object.defineProperty(obj, "a", { enumerable: false });
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
// 没有输出,因为第一个“a”属性已经被访问过,并且是不可枚举的。
此外,请考虑以下情况,即行为是未指定的,并且实现往往偏离规定的算法:
在迭代过程中更改原型链:
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 };
// 在属性被访问前删除它
for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
delete obj.c;
}
const obj2 = { a: 1, b: 2, c: 3 };
// 在属性被访问后删除它
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 });
}
规范
Specification |
---|
ECMAScript® 2025 Language Specification # sec-for-in-and-for-of-statements |
浏览器兼容性
Report problems with this compatibility data on GitHubdesktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
for...in |
Legend
Tip: you can click/tap on a cell for more information.
- Full support
- Full support