JavaScript中的for语句和for语句之间有什么区别?


409

我知道什么是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 is does not log "3", "5", "7", "hello"
}

我得到的是,for... of遍历属性值。那么,为什么它不记录(返回)"3", "5", "7", "hello"而不是"3", "5", "7"?但for... in循环会遍历每个键("0", "1", "2", "foo")。在这里,for... in循环还会遍历foo键。但是for... of不会迭代foo财产的价值,即"hello"。为什么会这样呢?

总而言之:

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

范例连结


1
在你错过的情况下,这里是开始链接developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
安东尼罗素

1
据我所知,for ... of该语言已被用于解决for ... in与Arrays 结合使用所产生的问题。Array.prototype可以以可用额外属性的方式进行修改,从而使其不安全,因为您可能会得到非预期的非数字键。
系统发育

2
对于将来的读者:这可能不是JavaScript of关键字(for…of循环)的重复,因为它询问功能的特定行为,而不是询问一般概述。
Apsillers

2
只是习惯于说“ for <key> in”和“ for <value> of”并意识到IE不支持for..of
BotNet

关于enumerablemedium.com/@shivamethical/…的精彩
Kalhan.Toress,

Answers:


304

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

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

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


9
for ... of在ES6中已标准化。
贾斯汀

2
真奇怪,我发誓我读到某个地方将其移回到ES7,但是显然那不是事实。我的错。
亚历山大·奥玛拉

40
助记符:'o'f->不是'o'bjects,'i'n->不是'i'terables
Placoplatr

4
另一个助记符::: for... of数组::数组始终有长度,因此您可以考虑for.. [nth元素] of.. [q元素]
Nathan Smith,

14
另一个助记符... for..in..keys===外键=== for...in用于键!因此,for...of用于值。
冈瑟

237

我在以下位置找到了完整的答案:https : //www.typescriptlang.org/docs/handbook/iterators-and-generators.html(尽管它是针对类型脚本的,但对于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另一方面,主要关注可迭代对象的值。内置对象(例如Map和Set implement 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"
}

1
此外,调用for(让{的i表示)之类的东西{console.log(i); }会引发TypeError:VM391:1 Uncaught TypeError:{}在<anonymous>:1:14不可迭代,至少在Chrome中如此
kboom,

TS获胜-示例不正确,后者应返回“哺乳动物”,而不是//“猫”,“狗”,“仓鼠”
martinp999

8
我记得那是:为“中”的index。然后,“ of”将是values每个索引/键/项目的。
SherylHohman

很好,这对我来说将是最重要的:使用for-ins进行项目的迭代,我通常必须创建一个let thisItem = items[all];变量,这for...of有助于简化操作!
瓦西里大厅

我的记忆方式是:for...inas Object.keys(),你猜怎么着?数组是对象,也可以返回它们的索引。:)
Sujeet Agrahari

38

对于...在循环中

为...在循环,消除计数逻辑和退出条件在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]);
}

印刷品:

0

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循环。

对于... 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循环只会循环遍历对象中的值。


2
for ... in循环通过消除计数逻辑和退出条件改善了for循环的弱点 ”-不,这不是它的工作。一点也不。
Bergi '18

1
@Bergi您能否澄清一下为什么您认为那不是它的作用,以及您实际上认为它会改善什么?
Elar

2
它没有任何改善,它有自己的存在理由。它所做的事情与for (var index=0; index<arr.length; index++)循环(index计数器是整数,不同于您的示例)完全不同。
Bergi '18

令人困惑的是,您为示例选择的数组值与数组索引值相对应……
Sergey

18

区别for..infor..of

for..infor..of被循环其在数据结构用于迭代构建体。唯一的区别在于它们的迭代方式:

  1. for..in遍历对象的所有可枚举的属性键
  2. for..of迭代一个可迭代对象的。可迭代对象的示例是数组,字符串和NodeLists。

例:

let arr = ['el1', 'el2', 'el3'];

arr.addedProp = 'arrProp';

// elKey are the property keys
for (let elKey in arr) {
  console.log(elKey);
}

// elValue are the property values
for (let elValue of arr) {
  console.log(elValue)
}

在此示例中,我们可以观察到for..in循环在对象的键上进行迭代,该对象在此示例中是数组对象。键是0、1、2,分别对应于我们添加的数组元素和addedProp。这是arrchrome devtools中数组对象的外观:

在此处输入图片说明

您会看到我们的for..in循环只不过是迭代这些值而已。


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


8

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
}

与早期的,你可以跳过添加hasOwnPropertyfor...of循环。


7

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
Milad

1
打印出“ formatUnicorn”,“ truncate”,“ splitOnLast”,“包含”,因为stackoverflow覆盖String.prototype
jasonxia23 '19

6

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

迭代中的普通for迭代器,作为响应,为我们提供了按插入顺序排列的键,如下面的示例所示。

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

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

现在,如果我们对for进行相同的尝试,那么作为响应,它将为我们提供值而不是键。例如

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

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

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

注: - 对于只能与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 inFor of之间的区别。希望它可以清除差异。


5

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

拆解for...in已被弃用。使用for...of代替。

资源

因此,如果我们想在循环中使用解构,以获取每个数组元素的索引,则应将循环与Array方法一起使用:for...ofentries()

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

1
是的@GalMargalit,我仔细阅读过。我同意for each...in不赞成使用(第一点),但是我没有写过……我写道:“ for...in不赞成使用解构。请for...of改用。” (第二点):developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…您是否同意我@GalMargalit?
simhumileco

1
哈哈,你是对的,我没有仔细阅读!没错,我基本上是在思考同一件事,并且以为您是在指另一件事。
Gal Margalit,

2

每个人都确实解释了为什么会发生此问题,但是仍然很容易忘记它,然后挠头问您为什么得到错误的结果。尤其是当您处理大量数据时,乍一看结果似乎还不错。

使用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

*/

2

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

对于循环

遍历所有可枚举的道具

let nodes = document.documentElement.childNodes;

for (var key in nodes) {
  console.log( key );
}

对于循环

迭代所有可迭代的值

let nodes = document.documentElement.childNodes;

for (var node of nodes) {
  console.log( node.toString() );
}


2

当我刚开始学习for inloop时,我也对我的输出感到困惑,但是通过一些研究和了解,您可以像下面这样思考单个循环:

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

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

现在,输出的原因是我们在配置文件对象中具有Four(4)属性,并且众所周知索引从0 ... n开始,因此,我们得到属性0、1、2、3的索引,因为使用for..in循环。

  1. for ... of循环*可以返回propertyvalue 两者都返回,让我们看看如何。在javaScript中,我们无法像在数组上那样正常地遍历对象,因此,可以使用一些元素来访问对象中的任一个选择。

    • Object.keysobject-name-goes-here)>>>返回对象的属性

    • Object.valuesobject-name-goes-here)>>>返回对象的

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

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

对不清晰的零件部分进行评论。


1

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

在上面的例子中,属性dmyObject和指数3myArray未出现在for-in,因为它们与配置循环enumerable: false

for-in循环几乎没有问题。在使用Arrays的情况下,for-in循环还将考虑methods使用myArray.someMethod = f语法将其添加到数组中,但是myArray.length仍然是4

for-of

for-of循环迭代集合的值是一种误解。for-of循环遍历Iterable对象。一个可迭代对象是一个对象,该对象的名称之一Symbol.iterator直接在其原型上。

Symbol.iterator方法应返回Iterator。迭代器是具有next方法的对象。当调用return valuedoneproperties 时,此方法。

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

var myObject = { a: 1, b: 2, c: 3, d: 4 };

// make `myObject` iterable by adding `Symbol.iterator` function directlty on it
myObject[ Symbol.iterator ] = function(){
  console.log( `LOG: called 'Symbol.iterator' method` );
  var _myObject = this; // `this` points to `myObject`
  
  // return an iterator object
  return {
    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` iterable
for( let value of myObject ) {
  console.log( 'myObject: value => ', value );
}

for-of回路处于ES6新等都是可迭代Iterables。该Array构造类型都有Symbol.iterator它的原型方法。该Object构造可悲的是没有它,但是Object.keys()Object.values()Object.entries()方法返回一个可迭代(你可以用console.dir(obj)检查原型方法)。for-of循环的好处是可以使任何对象都是可迭代的,即使您的自定义DogAnimal类也是如此。

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

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

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

因此,String.length返回16-bit字符串中的块数。像表情符号字符这样的现代字符需要32位。因此,此字符将返回length2. for-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 ); }


0

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

循环数组项的最古老方法之一是for循环索引:

let arr = ["Apple", "Orange", "Pear"];

for (let i = 0; i < arr.length; i++) { alert( arr[i] ); } But for arrays there is another form of loop, for..of:

let fruits = ["Apple", "Orange", "Plum"];

// iterates over array elements for (let fruit of fruits) { alert( fruit ); } The for..of doesn’t give access to the number of the current element, just its value, but in most cases that’s enough. And it’s shorter.

从技术上讲,因为数组是对象,所以也可以将..in用于:

let arr = ["Apple", "Orange", "Pear"];

for (let key in arr) { alert( arr[key] ); // Apple, Orange, Pear } But that’s actually a bad idea. There are potential problems with it:

..in的循环遍历所有属性,而不仅仅是数字属性。

在浏览器和其他环境中,有一些类似于数组的所谓“类似数组”的对象。也就是说,它们具有长度和索引属性,但它们也可能具有其他通常不需要的非数字属性和方法。for..in循环将列出它们。因此,如果我们需要处理类似数组的对象,那么这些“额外”属性可能会成为问题。

for..in循环针对通用对象而非数组进行了优化,因此速度要慢10-100倍。当然,它仍然非常快。加速可能仅在瓶颈方面起作用。但是我们仍然应该意识到其中的区别。

通常,我们不应该将for..in用于数组。


0

这是用于记住for...inLoop和for...ofLoop 之间区别的有用助记符。

“索引,对象”

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.