(for… in)和(for… of)语句有什么区别?

我知道什么是for... in循环(它迭代键),但我第一次听说for... of(它迭代值)。

我对for... of循环感到困惑。

var arr = [3, 5, 7];arr.foo = "hello";    
for (var i in arr) {console.log(i); // logs "0", "1", "2", "foo"}    
for (var i of arr) {console.log(i); // logs "3", "5", "7"// it doesn't log "3", "5", "7", "hello"}

我知道for... of迭代属性值。那为什么它不记录"3", "5", "7", "hello"而不是"3", "5", "7"

for... in循环不同,它迭代每个键("0", "1", "2", "foo")并迭代foo键,for... of不要迭代foo属性的值,即"hello"。为什么会这样?

这里我控制台for... of循环。它应该记录"3", "5", "7","hello",但它记录"3", "5", "7"。为什么?

示例链接

576546 次浏览

for in循环遍历对象的可枚举属性名称。

for of(ES6中的新功能)确实使用特定对象的迭代器并循环生成的值。

在您的示例中,数组迭代器确实产生数组中的所有值(忽略非索引属性)。

我在迭代器和生成器找到了一个完整的答案(虽然它适用于TypeScript,但JavaScript也是如此)

for..offor..in语句都遍历列表;值但是迭代是不同的,for..in返回一个键列表正在迭代的对象,而for..of返回值列表正在迭代的对象的数值属性。

这里有一个例子来证明这种区别:

let list = [4, 5, 6];
for (let i in list) {console.log(i); // "0", "1", "2",}
for (let i of list) {console.log(i); // "4", "5", "6"}

另一个区别是for..in操作任何对象;它服务于作为检查此对象属性的一种方式。另一方面for..of手,主要对可迭代对象的值感兴趣。内置像MapSet这样的对象实现Symbol.iterator属性访问存储的值。

let pets = new Set(["Cat", "Dog", "Hamster"]);pets["species"] = "mammals";
for (let pet in pets) {console.log(pet); // "species"}
for (let pet of pets) {console.log(pet); // "Cat", "Dog", "Hamster"}

for-in语句以任意顺序遍历对象的可枚举属性。

循环将遍历对象本身的所有可枚举属性以及对象从其构造函数原型继承的属性

您可以将其视为“for in”基本上迭代并列出所有键。

var str = 'abc';var arrForOf = [];var arrForIn = [];
for(value of str){arrForOf.push(value);}
for(value in str){arrForIn.push(value);}
console.log(arrForOf);// ["a", "b", "c"]console.log(arrForIn);// ["0", "1", "2", "formatUnicorn", "truncate", "splitOnLast", "contains"]

for… in循环

为…在循环通过消除计数逻辑和退出条件来改进for循环的弱点。

示例:

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {console.log(digits[index]);}

但是,您仍然必须处理使用索引访问数组值的问题,这很糟糕;它几乎使它比以前更混乱。

此外,当你需要向数组(或另一个对象)添加额外的方法时,for… in循环会给你带来大麻烦。因为for… in循环循环所有可枚举的属性,这意味着如果你向数组的原型添加任何额外的属性,那么这些属性也会出现在循环中。

Array.prototype.decimalfy = function() {for (let i = 0; i < this.length; i++) {this[i] = this[i].toFixed(2);}};
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {console.log(digits[index]);}

打印:

1

2

3

4

5

6

7

8

9

方法 for(设i=0; i

这就是为什么在数组上循环时不鼓励for… in循环。

for每个循环是JavaScript中另一种类型的for循环。但是,forEach()实际上是一个数组方法,因此它只能用于专门使用数组。也没有办法停止或破坏如果你在循环中需要这种类型的行为,你将必须使用基本的for循环。

for… of循环

为…的循环用于循环任何类型的可迭代数据。

示例:

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {console.log(digit);}

打印:

1

2

3

4

5

6

7

8

9

这使得for… of循环成为所有for循环中最简洁的版本。

但是等等,还有更多!for… of循环还有一些额外的好处,可以修复for和for… in循环的弱点。

您可以随时停止或中断for… of循环。

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {if (digit % 2 === 0) {continue;}console.log(digit);}

打印:

1

3

5

7

9

您不必担心向对象添加新属性。for… of循环只会循环对象中的值。

for...in语句以任意顺序遍历对象的可枚举属性。可枚举属性是那些内部[[可枚举]]标志设置为true的属性,因此如果原型链中有任何可枚举属性,for...in循环也会迭代这些属性。

for...of语句迭代可迭代对象定义要迭代的数据。

示例:

Object.prototype.objCustom = function() {};Array.prototype.arrCustom = function() {};
let iterable = [3, 5, 7];
for (let i in iterable) {console.log(i); // logs: 0, 1, 2, "arrCustom", "objCustom"}
for (let i in iterable) {if (iterable.hasOwnProperty(i)) {console.log(i); // logs: 0, 1, 2,}}
for (let i of iterable) {console.log(i); // logs: 3, 5, 7}

像前面一样,您可以跳过在for...of循环中添加hasOwnProperty

有一些已经定义的数据类型允许我们轻松地迭代它们,例如Array、Map、String Object

正常对于在迭代迭代器,并作为响应为我们提供按插入顺序排列的键,如下例所示。

  const numbers = [1,2,3,4,5];for(let number in number) {console.log(number);}
// result: 0, 1, 2, 3, 4

现在,如果我们尝试与对于相同,那么作为响应,它为我们提供了值而不是密钥。例如

  const numbers = [1,2,3,4,5];for(let numbers of numbers) {console.log(number);}
// result: 1, 2, 3, 4, 5

因此,查看这两个迭代器,我们可以轻松区分它们之间的差异。

注意:-for of仅适用于Symbol.iterator

所以如果我们尝试迭代普通对象,那么它会给我们一个错误,例如-

const Room = {area: 1000,height: 7,floor: 2}
for(let prop in Room) {console.log(prop);}
// Result area, height, floor
for(let prop of Room) {console.log(prop);}

房间不可迭代

现在为了迭代,我们需要定义一个ES6Symbol.iterator例如

  const Room= {area: 1000, height: 7, floor: 2,[Symbol.iterator]: function* (){yield this.area;yield this.height;yield this.floors;}}

for(let prop of Room) {console.log(prop);}
//Result 1000, 7, 2

这是因为在for of的区别。希望它能清除差异。

每个人都解释了为什么会出现这个问题,但是仍然很容易忘记它,然后挠头为什么你得到了错误的结果。尤其是当你在处理大量数据时,结果乍一看似乎很好。

使用Object.entries可以确保遍历所有属性:

var arr = [3, 5, 7];arr.foo = "hello";
for ( var [key, val] of Object.entries( arr ) ) {console.log( val );}
/* Result:
357hello
*/

区别for..infor..of

for..infor..of都是循环结构,用于迭代数据结构。它们之间唯一的区别是实体他们遍历:

  1. for..in遍历所有可枚举的对象的属性键
  2. for..of迭代可迭代对象的值。可迭代对象的示例有数组、字符串和节点列表。

示例:

let arr = ['el1', 'el2', 'el3'];
arr.addedProp = 'arrProp';
// elKey are the property keysfor (let elKey in arr) {console.log(elKey);}
// elValue are the property valuesfor (let elValue of arr) {console.log(elValue)}

在这个例子中,我们可以观察到for..in循环迭代对象的键,在这个例子中是一个数组对象。键是0、1、2(对应于数组元素)和addedProp。这就是arr数组对象在chrome devols中的样子:

在此处输入图片描述

您可以看到我们的for..in循环只是简单地迭代这些键。


我们示例中的for..of循环迭代数据结构的。此特定示例中的值为'el1', 'el2', 'el3'。可迭代数据结构将使用for..of返回的值取决于可迭代对象的类型。例如,数组将返回所有数组元素的值,而字符串返回字符串的每个单独字符。

A看到很多好的答案,但我决定把我的5美分只是为了有一个好的例子:

循环中

遍历所有可枚举道具

let nodes = document.documentElement.childNodes;
for (var key in nodes) {console.log( key );}

for of循环

遍历所有可迭代值

let nodes = document.documentElement.childNodes;
for (var node of nodes) {console.log( node.toString() );}

另一个区别在两个循环之间,以前没有人提到过:

不建议使用for...in解构。请改用for...of

来源

因此,如果我们想在循环中使用结构化,对于每个阵列元素的索引,我们应该使用for...of循环和数组方法entries()

for (const [idx, el] of arr.entries()) {console.log( idx + ': ' + el );}

for-in循环

for-in循环用于遍历集合的可枚举属性,以任意的顺序。集合是容器类型对象,其项可以使用索引或键。

var myObject = {a: 1, b: 2, c: 3};var myArray = [1, 2, 3];var myString = "123";
console.log( myObject[ 'a' ], myArray[ 1 ], myString[ 2 ] );

for-in循环提取集合所有的一次enumerable属性()并一次迭代一个。可枚举属性是可以出现在for-in循环中的集合的属性。

默认情况下,数组和对象的所有属性都出现在for-in循环中。但是,我们可以使用Object.define属性方法手动配置集合的属性。

var myObject = {a: 1, b: 2, c: 3};var myArray = [1, 2, 3];
Object.defineProperty( myObject, 'd', { value: 4, enumerable: false } );Object.defineProperty( myArray, 3, { value: 4, enumerable: false } );
for( var i in myObject ){ console.log( 'myObject:i =>', i ); }for( var i in myArray ){ console.log( 'myArray:i  =>', i ); }

在上面的示例中,myObject的属性dmyArray的索引3没有出现在for-in循环中,因为它们配置了enumerable: false

for-in循环很少有问题。在数组的情况下,for-in循环也会考虑使用myArray.someMethod = f语法在数组上添加methods,然而,myArray.length仍然是4

for-of循环

for-of循环迭代集合的值是一种误解。for-of循环迭代Iterable对象。可迭代对象是一个对象,它的一个原型上直接包含名称为Symbol.iterator的方法。

Symbol.iterator方法应该返回迭代器。迭代器是具有next方法的对象。调用此方法时返回valuedone属性。

当我们使用for-of循环迭代迭代对象时,Symbol.iterator方法将被调用一次,得到一个Symbol.iterator0对象。对于for-of循环的每次迭代,该迭代器对象的next方法将被调用,直到next()调用返回的done返回false。如果next()调用返回的value属性,则for-of循环每次迭代接收到的值。

var myObject = { a: 1, b: 2, c: 3, d: 4 };
// make `myObject` iterable by adding `Symbol.iterator` function directlty on itmyObject[ Symbol.iterator ] = function(){console.log( `LOG: called 'Symbol.iterator' method` );var _myObject = this; // `this` points to `myObject`  
// return an iterator objectreturn {keys: Object.keys( _myObject ),current: 0,next: function() {console.log( `LOG: called 'next' method: index ${ this.current }` );      
if( this.current === this.keys.length ){return { done: true, value: null }; // Here, `value` is ignored by `for-of` loop} else {return { done: false, value: _myObject[ this.keys[ this.current++ ] ] };}}};}
// use `for-of` loop on `myObject` iterablefor( let value of myObject ) {console.log( 'myObject: value => ', value );}

for-of循环在ES6中是新的,Array1和Array2也是如此。Array构造函数类型的原型上有Symbol.iterator方法。遗憾的是,Object构造函数没有它,但Object.keys()Object.values()Object.entries()方法返回一个可迭代的(Array3)。for-of循环的好处是任何对象都可以变得可迭代,甚至你的自定义DogArray0类。

使对象可迭代的简单方法是实现ES6发电机而不是自定义迭代器实现。

for-in不同,for-of循环可以在每次迭代中等待异步任务完成。这是在for语句留档之后使用await关键字实现的。

for-of循环的另一个好处是它支持Unicode。根据ES6规范,字符串以UTF-16编码存储。因此,每个字符可以采用16-bit32-bit。传统上,字符串以UCS-2编码存储,该编码支持只能存储在16 bits内的字符。

因此,String.length返回字符串中16-bit块的数量。像Emoji字符这样的现代字符需要32位。因此,此字符将返回2中的lengthfor-in循环迭代16-bit块并返回错误的index。然而,for-of循环基于UTF-16规范迭代单个字符。

var emoji = "😊🤣";
console.log( 'emoji.length', emoji.length );
for( var index in emoji ){ console.log( 'for-in: emoji.character', emoji[index] ); }for( var character of emoji ){ console.log( 'for-of: emoji.character', character ); }

当我第一次开始学习对于在的循环时,我也对我的输出感到困惑,但是通过一些研究和理解,你可以像下面这样思考单个循环:的

  1. 为…在循环返回单个属性的索引,对财产的价值没有效果的影响,它循环并返回财产而不是的信息。例如

让配置文件={名称:"Naphtali",年龄: 24,FavCar:"Mustang",标签:“Baileys”

上面的代码只是创建了一个名为个人资料对象,我们将它用于我们的例子,所以,当你在示例中看到配置文件对象时不要感到困惑,只要知道它是创建的。

现在让我们使用下面的for… in loop

for(let myIndex in profile){console.log(`The index of my object property is ${myIndex}`)}// Outputs :The index of my object property is 0The index of my object property is 1The index of my object property is 2The index of my object property is 3

现在输出的原因是我们的个人资料对象中有四(4)属性,我们都知道索引从0… n开始,所以,我们得到属性0,1,2,3的索引,因为我们正在使用for… in循环。

  1. for… of循环*可以返回的财产两者,让我们看看如何。在javaScript中,我们不能像在数组中那样正常地循环对象,因此,我们可以使用一些元素来访问我们从对象中选择的任何一个。

    • Object.keys对象名称去这里) >>> 返回一个对象的键或属性。

    • Object.values对象名称去这里) >>> 返回一个对象的值。

    • Object.entries对象-名称-去-这里)>>>返回两者对象的

以下是它们的用法示例,注意Object.entries()

Step One: Convert the object to get either its key, value, or both.Step Two: loop through.

// Getting the keys/property
Step One: let myKeys = ***Object.keys(profile)***Step Two: for(let keys of myKeys){console.log(`The key of my object property is ${keys}`)}
// Getting the values of the property
Step One: let myValues = ***Object.values(profile)***Step Two : for(let values of myValues){console.log(`The value of my object property is ${values}`)}

当使用Object.entries()时,您可以调用对象上的两个条目,键和值。您可以通过任一条目调用两者。下面的示例。

Step One: Convert the object to entries, using ***Object.entries(object-name)***Step Two: **Destructure** the ***entries object which carries the keys and values***like so **[keys, values]**, by so doing, you have access to either or both content.

// Getting the keys/property
Step One: let myKeysEntry = ***Object.entries(profile)***Step Two: for(let [keys, values] of myKeysEntry){console.log(`The key of my object property is ${keys}`)}
// Getting the values of the property
Step One: let myValuesEntry = ***Object.entries(profile)***Step Two : for(let [keys, values] of myValuesEntry){console.log(`The value of my object property is ${values}`)}
// Getting both keys and values
Step One: let myBothEntry = ***Object.entries(profile)***Step Two : for(let [keys, values] of myBothEntry){console.log(`The keys of my object is ${keys} and its valueis ${values}`)}

对不清楚的部分进行评论。

这是一个有用的记忆for...in循环和for...of循环之间的区别的助记符。

"索引在,对象"

for...in Loop=>遍历数组的索引在

for...of Loop=>遍历的对象对象。

为…的循环仅适用于可迭代对象。在JavaScript中,可迭代对象是可以循环的对象。

String、Array、TypeArray、Map和Set都是内置的可迭代对象,因为它们的每个原型对象都实现了一个@@迭代器方法。因此,for… of循环适用于上述对象类型。

JavaScript中的对象默认不可迭代。因此,for… of循环对对象不起作用。

简单地说,for… of使用字符串和数组,但不使用对象。

为…在适用于那些可枚举标志设置为true的属性。

通过简单赋值或属性初始化器创建的属性的枚举标志默认为true。通过Object.define属性创建的属性的枚举标志默认为false。

这里有一个更详细的例子:https://dev.to/swastikyadav/difference-between-forof-and-forin-loop-in-javascript-j2o

简短的回答:for...in循环,而for...of循环

for (let x in ['a', 'b', 'c', 'd'] {console.log(x);}
// Output0123

for (let x of ['a', 'b', 'c', 'd'] {console.log(x);}
// Outputabcd

//对于in,迭代对象中的键和数组中的索引

 let obj={a:1, b:2}    
for( const key in obj)console.log(obj[key]); //would print 1 and 2console.log(key);      //would print a and b
let arr = [10, 11, 12, 13];
for (const item in arr)console.log(item);   //would print 0 1 2 3

//for,迭代数组或任何可迭代对象中的值

let arr = [10, 11, 12, 13];
for (const item of arr )console.log(item);  //would print 10  11  12  13

for of用于迭代可迭代对象,for in用于迭代对象属性

这里有一个技巧要记住:

for of不适用于object(所以它适用于可迭代对象)

for in不适用于terables(所以它适用于对象)

另一个诡计:

for in返回对象dices(键),而for of返回值

简单地说,forIN迭代数组(index)/对象(key)中的KEYS,
而forOF迭代数组的VALUES(value)。