ChatGPT解决这个技术问题 Extra ChatGPT

( 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" 呢?

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

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

Example Link

如果您错过了,这里是初始链接 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
据我了解,将 for ... of 引入语言是为了解决将 for ... in 与数组一起使用的问题。 Array.prototype 可以修改为额外的属性可用,从而使迭代它们变得不安全,因为您可能会得到您不期望的非数字键。
对于未来的读者:这可能不是 JavaScript of Keyword (for…of loops) 的重复,因为它询问的是功能的特定行为,而不是询问一般概述。
习惯说“for <key> in”和“for <value> of”并意识到 IE 不支持 for..of
@BotNet“意识到 IE 不支持 for..of”虽然这在技术上是正确的,但大多数现代项目仍然使用 Babel。

G
Graham P Heath

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

for of(ES6 中的新功能)确实使用了 object-specific iterator 并循环由它生成的值。

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


助记符:'o'f -> not 'o'bjects, 'i'n -> not 'i'terables
另一个助记词:for... of :: 数组 :: 数组总是有长度的,所以你可以认为 for.. [nth element] of.. [q elements]
另一个助记符...for..in..keys === 外键 === 使用 for...in 作为键!因此,使用 for...of 作为值。
这些名字很容易混淆......名字的错误选择
in 为您提供索引。这足以记住差异。如果你在其他方面应用逻辑。
S
Sabito 錆兎 stands with Ukraine

我在 Iterators and Generators 找到了完整的答案(虽然它适用于 TypeScript,但 JavaScript 也是如此)

for..of 和 for..in 语句都遍历列表;但是,迭代的值是不同的,for..in 返回被迭代对象的键列表,而 for..of 返回被迭代对象的数字属性的值列表。下面是一个演示这种区别的示例: let list = [4, 5, 6]; for (让我在列表中) { console.log(i); // "0", "1", "2", } for (let i of list) { console.log(i); // "4", "5", "6" } 另一个区别是 for..in 对任何对象进行操作;它用作检查此对象的属性的一种方式。另一方面,for..of 主要对可迭代对象的值感兴趣。 Map 和 Set 等内置对象实现了 Symbol.iterator 属性,允许访问存储的值。 let pets = new Set(["Cat", "Dog", "Hamster"]);宠物[“物种”] =“哺乳动物”; for (let pet in pets) { console.log(pet); // “物种” } for (let pet of pets) { console.log(pet); // “猫”、“狗”、“仓鼠” }


此外,调用类似 for(let i of {}) { console.log(i); } 会抛出一个 TypeError: VM391:1 Uncaught TypeError: {} is not iterable at :1:14,至少在 Chrome 中
获胜的 TS - 示例不正确,后者应返回“哺乳动物”,而不是 //“猫”、“狗”、“仓鼠”
我记得它是:for "in" for index。然后 for "of" 将是每个索引/键/项目的 values
很好,这对我来说将是王道:使用 for-ins 来迭代项目我通常必须创建一个 let thisItem = items[all]; 变量,for...of 有助于简化它!
这里值得一提的注意事项:for...in 返回的索引是字符串! ...如果您需要在这里使用索引作为数字,您需要将其转换,例如 Number(i)
W
Willem van der Veen

for..in 和 for..of 的区别:

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

for..in 迭代对象的所有可枚举属性键 for..of 迭代可迭代对象的值。可迭代对象的示例是数组、字符串和 NodeList。

例子:

让 arr = ['el1', 'el2', 'el3']; arr. addedProp = 'arrProp'; // elKey 是 (let elKey in arr) { console.log(elKey); 的属性键} // elValue 是 (let elValue of arr) { console.log(elValue) } 的属性值

在本例中,我们可以观察到 for..in 循环遍历对象的键,在本例中是一个数组对象。键是 0、1、2(对应于数组元素)和 addedProp。这是 arr 数组对象在 chrome devtools 中的外观:

https://i.stack.imgur.com/In5A2.png

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

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


为什么不输出“arrProp”?
@AlifRamdani 在这种情况下,特别是因为有问题的对象是一个数组。这就是@Willem 的意思,“可迭代数据结构使用 for..of 返回的值取决于可迭代对象的类型。”在数组的情况下,这只是数字索引。
E
Elar

对于...在循环中

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 function() { for (let i = 0; i < this.length; i++) { this[i] = this[i].toFixed(2); } }

这就是为什么在遍历数组时不鼓励使用 for...in 循环。

注意:forEach 循环是 JavaScript 中另一种类型的 for 循环。但是,forEach() 实际上是一个数组方法,所以它只能与数组一起使用。也没有办法停止或中断 forEach 循环。如果您在循环中需要这种类型的行为,则必须使用基本的 for 循环。

For...of 循环

for...of 循环用于遍历任何类型的可迭代数据。

例子:

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const digit of digits) {
  console.log(digit);
}

印刷:

0 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 循环通过消除计数逻辑和退出条件来改进 for 循环的弱点” - 不,这不是它的作用。一点也不。
@Bergi您能否澄清一下为什么您认为这不是它的作用,以及您实际上认为它改进了什么?
它没有任何改进,它有自己的存在理由。它所做的事情与 for (var index=0; index<arr.length; index++) 循环完全不同(其中 index 计数器是一个整数,与您的示例不同)。
您为示例选择的数组值与数组索引值相对应有点让人困惑......
您的示例使用了一个包含从 0 到 9 的数字的数组,这会使人们感到困惑,就好像它们是索引一样,请将您的源数据更改为更符合上下文的内容。
b
bnieland

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

“索引,对象”

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

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


s
simhumileco

for...in 语句以任意顺序迭代对象的可枚举属性。可枚举属性是那些其内部 [[Enumerable]] 标志设置为 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


s
simhumileco

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

不推荐使用 for...in 的解构。使用 for...of 代替。

Source

因此,如果我们想在循环中使用 destructuring 来获取每个 array 元素的 indexvalue,我们应该使用for...of 使用 Array 方法循环 entries()

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

是的@GalMargalit,我仔细阅读了它。我同意 for each...in 已被弃用(第一点),但我没有写它...我写道“解构 for...in 已弃用。改用 for...of。” (第二点):developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 你同意我的观点吗@GalMargalit?
哈哈你是对的,我没有仔细阅读!没错,我基本上在想同样的事情,并认为你指的是另一个。
D
Devdutta Natu

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 只会显示我们添加的键,它不会显示 formatUnicorn
“formatUnicorn”、“truncate”、“splitOnLast”、“contains”打印出来,因为 stackoverflow 覆盖 String.prototype
A
Amit Mundra

有一些已经定义的数据类型允许我们轻松地对其进行迭代,例如数组、映射、字符串对象

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

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

   // result: 0, 1, 2, 3, 4

现在,如果我们尝试使用 for of,作为响应,它会为我们提供值而不是键。例如

  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);
 } 

房间不可迭代

现在为了迭代,我们需要定义一个 ES6 Symbol.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 in 和 For of 的区别。希望它可以消除差异。


在定义 Symbol.iterator 时,为什么使用 function* 而不是 function,有什么区别,在这里使用 function* 有什么意义,请解释一下。
A
Aaqib Javaid

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

 let obj={a:1, b:2}
    
    for( const key in obj)
      console.log(obj[key]); //would print 1 and 2
      console.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 of,迭代数组或任何可迭代对象中的值

let arr = [10, 11, 12, 13];

for (const item of arr )
  console.log(item);  //would print 10  11  12  13

U
Uday Hiwarale

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 循环一次性提取集合的可枚举属性()并一次迭代一个。可枚举属性是可以出现在 for-in 循环中的集合的属性。

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

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 方法应返回 Iterator。迭代器是具有 next 方法的对象。此方法在调用时返回 valuedone 属性。

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

var myObject = { a: 1, b: 2, c: 3, d: 4 }; // 通过在其上直接添加 `Symbol.iterator` 函数使 `myObject` 可迭代 myObject[ Symbol.iterator ] = function(){ console.log( `LOG: called 'Symbol.iterator' method` ); var _myObject = 这个; // `this` 指向 `myObject` // 返回一个迭代器对象 return { keys: Object.keys( _myObject ), current: 0, next: function() { console.log( `LOG: 调用 'next' 方法:索引 ${ this.current }` ); if( this.current === this.keys.length ){ return { done: true, value: null }; // 这里,`value`被`for-of`循环忽略 } else { return { done: false, value: _myObject[ this.keys[ this.current++ ] ] }; } } }; } // 在 `myObject` 上使用 `for-of` 循环 可迭代 for( let value of myObject ) { console.log( 'myObject: value => ', value ); }

for-of 循环在 ES6 中是新的,IterableIterables 也是如此。 Array 构造函数类型在其原型上有 Symbol.iterator 方法。遗憾的是,Object 构造函数没有它,但 Object.keys()Object.values()Object.entries() 方法返回一个可迭代对象(您可以使用 console.dir(obj) 检查原型方法)。 for-of 循环的好处是任何对象都可以迭代,甚至是您的自定义 DogAnimal 类。

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

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

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

因此,String.length 返回字符串中 16-bit 块的数量。现代字符(如 Emoji 字符)占用 32 位。因此,此字符将返回 length of 2。for-in 循环遍历 16-bit 块并返回错误的 index。但是,for-of 循环会根据 UTF-16 规范对单个字符进行迭代。

var emoji = "😊🤣"; console.log('emoji.length', emoji.length); for( 表情符号中的变量索引 ){ console.log( 'for-in: emoji.character', emoji[index] ); } for( 表情符号的 var 字符 ){ console.log( 'for-of: emoji.character', character ); }


S
Swastik Yadav

for...of 循环仅适用于可迭代对象。在 JavaScript 中,可迭代对象是可以循环的对象。

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

JavaScript 中的对象默认是不可迭代的。因此,for...of 循环不适用于对象。

简而言之,for...of 适用于字符串和数组,但不适用于对象。

for...in 适用于那些可枚举标志设置为 true 的属性。

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

这是一个更详细的帖子,其中包含示例:https://dev.to/swastikyadav/difference-between-forof-and-forin-loop-in-javascript-j2o


p
perryv

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

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

// Output
0
1
2
3


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

// Output
a
b
c
d

W
WebBrother

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

对于循环

遍历所有可枚举的道具

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

对于循环

遍历所有可迭代值

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


N
Naphtali Duniya

当我第一次开始学习 for in 和 of 循环时,我也对我的输出感到困惑,但是通过一些研究和理解,您可以将单个循环想象如下:

for...in 循环返回单个属性的索引并且对属性的值没有影响,它循环并返回有关属性而不是值的信息。例如

let profile = { name : "Naphtali", age : 24, favCar : "Mustang", favDrink : "Baileys" }

上面的代码只是创建了一个名为 profile 的对象,我们将在两个示例中使用它,因此,当您在示例中看到 profile 对象时不要感到困惑,只要知道它是创建的即可。

所以现在让我们使用下面的 for...in 循环

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

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

for...of loop* 可以返回属性,值或两者,让我们看看如何。在 javaScript 中,我们不能像在数组上那样正常循环对象,因此,我们可以使用一些元素来访问对象中的任何一个选择。 Object.keys(object-name-goes-here) >>> 返回对象的键或属性。 Object.values(object-name-goes-here) >>> 返回一个对象的值。 Object.entries(object-name-goes-here) >>> 返回对象的键和值。

以下是它们的用法示例,请注意 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 value 
is ${values}`)
                   }

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


你提到的第一行是错误的。您提到 >> “for...in 循环返回单个属性的索引”。实际上就 Object 而言,(for . . in) 返回对象键。另外,我们不能为 . . .的对象。我们可以在 Array 上执行 (for . . in) 和 (for . . of)。
s
simhumileco

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

使用 Object.entries 可以确保通过所有属性:

var arr = [3, 5, 7];
arr.foo = "hello";

for ( var [key, val] of Object.entries( arr ) ) {
   console.log( val );
}

/* Result:

3
5
7
hello

*/

B
Bergi

我发现 https://javascript.info/array 中的以下解释非常有帮助:

循环数组项的最古老的方法之一是 for 循环索引: let arr = ["Apple", "Orange", "Pear"]; for (let i = 0; i < arr.length; i++) { alert( arr[i] );但是对于数组,还有另一种形式的循环,for..of: let fruits = ["Apple", "Orange", "Plum"]; // 遍历数组元素 for (let fruit of fruits) { alert( fruit ); for..of 不能访问当前元素的编号,而只能访问其值,但在大多数情况下就足够了。而且更短。从技术上讲,因为数组是对象,所以也可以使用 for..in: let arr = ["Apple", "Orange", "Pear"]; for (let key in arr) { alert( arr[key] ); // Apple, Orange, Pear } 但这实际上是个坏主意。它存在潜在的问题:for..in 循环遍历所有属性,而不仅仅是数字属性。但这实际上是个坏主意。它存在潜在的问题:for..in 循环遍历所有属性,而不仅仅是数字属性。在浏览器和其他环境中存在所谓的“类数组”对象,它们看起来像数组。也就是说,它们具有长度和索引属性,但它们也可能具有其他我们通常不需要的非数字属性和方法。虽然 for..in 循环会列出它们。因此,如果我们需要使用类似数组的对象,那么这些“额外”属性可能会成为问题。 for..in 循环针对通用对象而不是数组进行了优化,因此速度要慢 10-100 倍。当然,它仍然非常快。加速可能只对瓶颈很重要。但是我们仍然应该意识到差异。一般来说,我们不应该将 for..in 用于数组。