在For Of循环中使用对象


83

为什么无法在for循环中使用对象?还是这是浏览器错误?这段代码在Chrome 42中不起作用,它说undefined不是一个函数:

test = { first: "one"}

for(var item of test) {
  console.log(item)
}

是测试数组还是对象?
Kick Buttowski

9
@KickButtowski,看不到吗?绝对是一个对象。
格林

4
(让Object.keys(test)的键){...}
Clocksmith

Answers:


69

for..of环路仅支持像数组可迭代的对象,而不是对象。

要遍历对象的值,请使用:

for (var key in test) {
    var item = test[key];
}

3
@DanielHerr具有.iterable成员函数,当您尝试在对象(不包含对象)上使用它时,错误就来自此。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
章概述

4
我的意思是,为什么对象没有那个?本地添加它会有什么问题?
Daniel Herr 2015年

3
@DanielHerr我没有答案,您必须问设计语言的人。
2015年

6
@DanielHerr如果对象“基类”是可迭代的,那么除了其他复杂性之外,任何Function / Date / etc“子类”也将是可迭代的。请参阅esdiscuss.org/topic/es6-iteration-over-object-values#content-5,以更全面/准确地讨论您的问题。
natevw

5
有了这个for..in解决方案,您是否在技术上仍然不需要进行检查if (test.hasOwnProperty(key)){ ... }?还是不需要?
Tennisgent

39

您可以使用以下语法:

let myObject = {first: "one"};

for(let [key, value] of Object.entries(myObject)) {
    console.log(key, value); // "first", "one"
}

然而, Object.entries 目前支持不佳 在IE中不起作用 或iOS Safari。你会大概 可能需要一个polyfill。


33

如果要将数据存储在键值存储中,请使用Map专门为此目的设计的数据。

如果您必须使用对象,则ES2017(ES8)允许您使用Object.values

const foo = { a: 'foo', z: 'bar', m: 'baz' };
for (let value of Object.values(foo)) {
    console.log(value);
}

如果还不支持,使用填充工具:用于替代版本Object.values()

最后,如果您要支持不支持此语法的较旧环境,则必须诉诸使用forEachand Object.keys

var obj = { a: 'foo', z: 'bar', m: 'baz' };
Object.keys(obj).forEach(function (prop) {
    var value = obj[prop];
    console.log(value);
});

对象原型不能扩展以支持此功能吗?
Sonic Soul

1
@SonicSoul:从技术上讲是的,但是通常不建议扩展Object原型,因为(几乎)所有东西都继承自该原型。
澳洲航空94

1
Object.entries可以通过填充而不接触原型。
mpen

5
为什么使用地图代替对象?
丹尼尔·赫尔

1
比起简单的例子,使用这些复杂的例子有什么好处for-in吗?
1252748年

18

ECMAScript 2015 / ES6中的Iterator,Iterable和for..of循环

let tempArray = [1,2,3,4,5];

for(element of tempArray) {
  console.log(element);
}

// 1
// 2
// 3
// 4
// 5

但是如果我们这样做

let tempObj = {a:1, b:2, c:3};

for(element of tempObj) {
   console.log(element);
}
// error

我们收到错误消息是因为for..of循环仅适用于Iterables,也就是说,该对象具有遵守Iterator协议@@ iterator,这意味着它必须具有使用下一个方法的对象。下一个方法不带参数,它应该返回具有这两个属性的对象。

done:当为true时,表示序列已结束;如果为false,则表示可能还有更多值 value:这是序列中的当前项目

因此,为了使对象可迭代是使其与工作for..of我们可以:

1。 通过Symbol.iterator属性为其分配一个神秘的@@ iterator属性,使该对象成为Iterable。方法如下:

let tempObj = {a:1, b:2, c:3};

tempObj[Symbol.iterator]= () => ({
next: function next () {
return {
    done: Object.keys(this).length === 0,
    value: Object.keys(this).shift()
     }
    }
  })

for(key in tempObj){
 console.log(key)
}
// a
// b
// c

2.使用Object.entries,它返回一个Iterable

let tempObj = {a:1, b:2, c:3};

for(let [key, value] of Object.entries(tempObj)) {
    console.log(key, value);
}
// a 1
// b 2
// c 3

3.使用Object.keys,方法如下:

let tempObj = {a:1, b:2, c:3};
for (let key of Object.keys(tempObj)) {
    console.log(key);
}

// a
// b
// c

希望这可以帮助!!!!!!


16

我使用以下代码使对象变得可迭代:

Object.prototype[Symbol.iterator] = function*() {
 for(let key of Object.keys(this)) {
  yield([ key, this[key] ])
} }

用法:

for(let [ key, value ] of {}) { }

或者:

for(let [ key, value ] of Object.entries({})) { }

47
不知道为什么这是公认的解决方案。除非对原型进行填充,否则对其进行修改始终是一个糟糕的主意。
user1703761

2
@ user1703761这是公认的解决方案,因为它可以工作。请解释一下,如果这是如此可怕,将会引起什么问题。
丹尼尔·赫尔

9
存在各种各样的问题,主要是前向兼容性问题。一个示例是Array.prototype.includes(以前使用的名称包含),但是Moo Tools扩展了原型,并且实现不兼容,请参见bugzilla.mozilla.org/show_bug.cgi?id=1075059, 另请查找原型库灾难;)
user1703761

4
我相信这不会产生前向兼容性问题,因为如果将迭代器添加到对象中,它将覆盖它;如果将迭代器添加到对象子类型,则将使用子类型迭代器。
丹尼尔·赫尔

4
嘿,修改原型是个坏主意!!!让我们为实际回答问题而感到遗憾!
NiCk Newman


3

答案是否定的。不可能将For..Of与Object文字一起使用。

我同意Overv的观点,即For..Of仅适用于可迭代对象。我有完全相同的问题,因为我使用对象通过for..in遍历键和值。但是我才意识到这就是ES6 MAPSSETS的目的。

let test = new Map();
test.set('first', "one");
test.set('second', "two");

for(var item of test) {
  console.log(item); // "one" "two"
}

因此,它实现了不必使用..In(使用hasOwnProperty验证)并且不必使用Object.keys()的目标。

此外,您的键不仅限于字符串。您可以使用数字,对象或其他文字。


2

对象文字没有内置的迭代器,而迭代器是处理for...of循环所必需的。但是,如果您不想麻烦地将自己[Symbol.iterator]的对象添加到对象中,则只需使用该Object.keys()方法即可。此方法返回一个Array对象,该对象已经具有内置的迭代器,因此可以将其与如下for...of循环一起使用:

const myObject = {
    country: "Canada",
    province: "Quebec",
    city: "Montreal"
}

for (let i of Object.keys(myObject)) {
    console.log("Key:", i, "| Value:", myObject[i]);
}

//Key: country | Value: Canada
//Key: province | Value: Quebec
//Key: city | Value: Montreal

每次使用密钥比一次添加迭代器更麻烦。同样,Object.keys()是ES5。
Daniel Herr

1

可以在任何给定对象上定义迭代器,这样您可以为每个对象放置不同的逻辑

var x = { a: 1, b: 2, c: 3 }
x[Symbol.iterator] = function* (){
    yield 1;
    yield 'foo';
    yield 'last'
}

然后直接迭代 x

for (let i in x){
    console.log(i);
}
//1
//foo
//last

可以在Object.prototype对象上执行相同的操作,并为所有对象提供通用迭代器

Object.prototype[Symbol.iterator] = function*() {
    for(let key of Object.keys(this)) {
         yield key 
    } 
 }

然后像这样迭代你的对象

var t = {a :'foo', b : 'bar'}
for(let i of t){
    console.log(t[i]);
}

或者这样

var it = t[Symbol.iterator](), p;
while(p = it.next().value){
    console.log(t[p])
}

1

我只是做了以下操作,以轻松控制我的工作。

for (let key in obj) {
  if(obj.hasOwnProperty(key){
    console.log(`${key}: ${obj[key]}`);
  }
}


0

那使用呢

function* entries(obj) {
    for (let key of Object.keys(obj)) {
        yield [key, obj[key]];
    }
}

for ([key, value] of entries({a: "1", b: "2"})) {
    console.log(key + " " + value);
}

0

在ES6中,您可以使用generator:

var obj = {1: 'a', 2: 'b'};

function* entries(obj) {
  for (let key of Object.keys(obj)) {
    yield [key, obj[key]];
  }
}

let generator = entries(obj);

let step1 = generator.next();
let step2 = generator.next();
let step3 = generator.next();

console.log(JSON.stringify(step1)); // {"value":["1","a"],"done":false}
console.log(JSON.stringify(step2)); // {"value":["2","b"],"done":false}
console.log(JSON.stringify(step3)); // {"done":true}

这是jsfiddle。 在输出中,您将获得带有"value""done"键的对象。"Value"包含您想要拥有的所有内容,并且"done"是bool中迭代的当前状态。


0

使用数组销毁,您可以使用以下方法进行迭代 forEach

const obj = { a: 5, b: 7, c: 9 };

Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});
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.