Sử dụng các đối tượng trong vòng lặp For Of


83

Tại sao không thể sử dụng các đối tượng trong vòng lặp for of? Hay đây là lỗi của trình duyệt? Mã này không hoạt động trong Chrome 42, nói rằng không xác định không phải là một hàm:

test = { first: "one"}

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

Kiểm tra là một mảng hay một đối tượng?
Kick Buttowski

9
@KickButtowski, bạn không thấy sao? Nó chắc chắn là một đối tượng.
Màu xanh lá cây

4
for (let key of Object.keys (test)) {...}
thợ làm đồng hồ 14/02/17

Câu trả lời:


69

Các vòng lặp for..of chỉ hỗ trợ đối tượng iterable như mảng, không đối tượng.

Để lặp lại các giá trị của một đối tượng, hãy sử dụng:

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

3
@DanielHerr Có một .iterablehàm thành viên, đây là nơi xuất phát lỗi khi bạn cố gắng sử dụng nó trên một đối tượng (không có nó). developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Overv

4
Ý tôi là, tại sao các đối tượng không có điều đó? Sẽ có vấn đề gì với việc thêm nó vào?
Daniel Herr

3
@DanielHerr Tôi không có câu trả lời cho điều đó, bạn sẽ phải hỏi những người thiết kế ngôn ngữ.
Overv

6
@DanielHerr Nếu đối tượng "lớp cơ sở" có thể lặp lại, thì bất kỳ "lớp con" Hàm / Ngày / etc nào trong số các biến chứng khác cũng vậy. Mặc dù vậy, hãy xem esdiscuss.org/topic/es6-iteration-over-object-values#content-5 để thảo luận kỹ lưỡng / chính xác hơn về câu hỏi của bạn.
natevw 07/07/16

5
Với giải pháp for..in này, về mặt kỹ thuật bạn vẫn không phải kiểm tra if (test.hasOwnProperty(key)){ ... }sao? Hay là không cần thiết?
tennisgent

39

Bạn có thể sử dụng cú pháp này:

let myObject = {first: "one"};

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

Tuy nhiên, Object.entries có hỗ trợ kém ngay bây giờ không hoạt động trong IE hoặc iOS Safari. Bạn sẽcó lẽ có thể cần một polyfill.


33

Nếu bạn đang lưu trữ dữ liệu trong kho khóa-giá trị, hãy sử dụngMap dữ liệu được thiết kế rõ ràng cho mục đích này.

Nếu bạn phải sử dụng một đối tượng, ES2017 (ES8) cho phép bạn sử dụng Object.values:

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

Nếu điều đó chưa được hỗ trợ, hãy sử dụng polyfill: Phiên bản thay thế choObject.values()

Và cuối cùng nếu bạn đang hỗ trợ một môi trường cũ hơn không hỗ trợ cú pháp này, bạn sẽ phải sử dụng forEachObject.keys:

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

Không thể mở rộng nguyên mẫu đối tượng để hỗ trợ điều này?
Sonic Soul

1
@SonicSoul: về mặt kỹ thuật là có, nhưng nói chung không nên mở rộng nguyên mẫu Đối tượng vì (khá nhiều) mọi thứ kế thừa từ nó.
Qantas 94 Heavy

1
Object.entriescó thể bằng cách polyfilled mà không cần chạm vào nguyên mẫu.
mpen

5
Tại sao sử dụng bản đồ thay vì các đối tượng?
Daniel Herr

1
Có lợi thế nào khi sử dụng những ví dụ phức tạp hơn là đơn giản for-inkhông?
1252748

18

Vòng lặp lặp lại, lặp lại có thể lặp lại và for..of trong ECMAScript 2015 / ES6

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

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

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

Nhưng nếu chúng ta làm

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

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

Chúng tôi gặp lỗi vì vòng lặp for..of chỉ hoạt động trên Iterables , tức là đối tượng có trình lặp @@ tuân theo giao thức Iterator , nghĩa là nó phải có một đối tượng có phương thức tiếp theo . Phương thức tiếp theo không có đối số và nó sẽ trả về một đối tượng có hai thuộc tính này.

thực hiện : tín hiệu rằng chuỗi đã kết thúc khi sự thật, và các phương tiện sai có thể có nhiều giá trị giá trị : đây là mục hiện hành trong chuỗi

Vì vậy, để làm cho một đối tượng Iterable đó là để làm cho nó làm việc với for..of chúng ta có thể:

1. Biến một đối tượng trở thành Lặp lại bằng cách gán cho thuộc tính vòng lặp @@ huyền bí của nó thông qua thuộc tính Symbol.iterator. Dưới đây là cách thực hiện:

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.Sử dụng Object.entries , trả về một lặp lại :

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. Sử dụng Object.keys , đây là cách thực hiện:

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

// a
// b
// c

Hi vọng điêu nay co ich!!!!!!


16

Tôi đã tạo các đối tượng có thể lặp lại với mã này:

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

Sử dụng:

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

Ngoài ra:

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

47
Không biết tại sao đây là giải pháp được chấp nhận. Sửa đổi nguyên mẫu trừ khi một polyfill của nó luôn là một ý tưởng khủng khiếp.
user1703761

2
@ user1703761 Đây là giải pháp được chấp nhận vì nó hoạt động. Vui lòng giải thích những vấn đề này sẽ gây ra nếu nó quá khủng khiếp.
Daniel Herr

9
Có đủ loại vấn đề, chủ yếu là vấn đề tương thích chuyển tiếp. Một ví dụ là Array.prototype.inc bao gồm các tên trước đây chứa nhưng Công cụ Moo đã mở rộng nguyên mẫu và việc triển khai không tương thích, hãy xem bugzilla.mozilla.org/show_bug.cgi?id=1075059 Đồng thời tra cứu thư viện Nguyên mẫu desaster;)
user1703761

4
Tôi tin rằng điều này sẽ không có vấn đề về khả năng tương thích chuyển tiếp vì nếu một trình vòng lặp được thêm vào các đối tượng, điều này sẽ ghi đè lên nó và nếu một trình vòng lặp được thêm vào một kiểu phụ đối tượng, nó sẽ sử dụng trình vòng lặp kiểu con.
Daniel Herr

4
Này các bạn, sửa đổi nguyên mẫu là một ý kiến ​​tồi !!! Hãy xấu hổ OP vì đã thực sự đưa ra câu trả lời cho câu hỏi!
NiCk Newman,

12

Bởi vì đối tượng nghĩa đen không có thuộc tính Symbol.iterator . Cụ thể, bạn chỉ có thể lặp qua Chuỗi , Mảng , Bản đồ , Tập hợp , các đối số , NodeList (không được hỗ trợ rộng rãi) và Trình tạo với vòng lặp for ... of .

Để đối phó với việc lặp lại Object Literal, bạn có hai tùy chọn.

tại

for(let key in obj){
    console.log(obj[key]); 
}

Object.keys + forEach

Object.keys(obj).forEach(function(key){
    console.log(obj[key]);
});

3

Câu trả lời là Không. Không thể sử dụng For..Of với các ký tự Object.

Tôi đồng ý với Overv rằng For..Of chỉ dành cho các mục lặp. Tôi đã có chính xác câu hỏi tương tự vì tôi sử dụng Đối tượng để lặp lại các khóa và giá trị với for..in. Nhưng tôi chỉ nhận ra rằng đó là những gì ES6 MAPSSETS dành cho.

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

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

Do đó, nó đạt được mục tiêu là không phải sử dụng for..In (xác thực bằng hasOwnProperty ) và không phải sử dụng Object.keys ().

Ngoài ra, khóa của bạn không giới hạn ở chuỗi. Bạn có thể sử dụng số, đối tượng hoặc các chữ khác.


2

Các ký tự đối tượng không có trình vòng lặp tích hợp, được yêu cầu để hoạt động với for...ofcác vòng lặp. Tuy nhiên, nếu bạn không muốn gặp rắc rối khi thêm cái riêng [Symbol.iterator]vào đối tượng của mình, bạn có thể chỉ cần sử dụng Object.keys()phương pháp này. Phương thức này trả về một Arrayđối tượng, đối tượng đã có sẵn một trình vòng lặp, vì vậy bạn có thể sử dụng nó với một for...ofvòng lặp như sau:

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

Sử dụng các khóa mỗi lần rắc rối hơn việc thêm một trình lặp một lần. Ngoài ra, Object.keys () là ES5.
Daniel Herr

1

Có thể xác định một trình lặp trên bất kỳ đối tượng nào, bằng cách này, bạn có thể đặt logic khác nhau cho từng đối tượng

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

Sau đó, chỉ cần lặp lại trực tiếp x

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

Có thể làm điều tương tự trên Object.prototypeđối tượng Và có một trình lặp chung cho tất cả các đối tượng

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

sau đó lặp lại đối tượng của bạn như thế này

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

Hoặc theo cách này

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

1

Tôi chỉ làm như sau để dễ dàng giải quyết vấn đề của mình.

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


0

Còn việc sử dụng

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

trong ES6, bạn có thể sử dụng trình tạo:

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}

Đây là jsfiddle. Trong đầu ra, bạn sẽ nhận được một đối tượng với các phím "value""done". "Value"chứa mọi thứ bạn muốn nó có và "done"là trạng thái hiện tại của lần lặp trong bool.


0

Sử dụng Array Destruction, bạn có thể lặp lại nó như sau bằng cách sử dụng 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"
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.