[英]JavaScript adding “return” method to an iterator doesn't properly close the iterator
我正在學習 JavaScript ES6 迭代器模式並遇到了這個問題:
const counter = [1, 2, 3, 4, 5];
const iter = counter[Symbol.iterator]();
iter.return = function() {
console.log("exiting early");
return { done: true };
};
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
// 1
// 2
// 3
// exiting early
// ---
// 4
// 5
因此,我向從數組中提取的迭代器添加了return
方法定義。 盡管調用了 return 方法,但它實際上並沒有關閉迭代器。 相比之下,如果我在定義中定義迭代器return
方法,它將按預期工作:
class Counter {
[Symbol.iterator]() {
let count = 1;
return {
next() {
if (count <= 5) {
return {
done: false,
value: count++
}
} else {
return {
done: true,
value: undefined
}
}
},
return() {
console.log('exiting early');
return { done: true, value: undefined };
}
}
}
}
const myCounter = new Counter();
iter = myCounter[Symbol.iterator]();
for (let i of myCounter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of myCounter) {
console.log(i);
}
// 1
// 2
// 3
// exiting early
// ---
// 1
// 2
// 3
// 4
// 5
我的問題是,為什么我會出現這種意外行為? 我假設如果沒有調用return
方法,那么迭代器將不會關閉,直到它通過調用next
到達最后。 但是添加return
屬性將正確地“調用” return
方法,因為我得到了控制台日志,但即使我在return
方法中返回{ done: true }
也不會真正終止迭代器。
您的兩個return
方法都沒有真正關閉迭代器。 為了實現這一點,他們需要記錄迭代器的新 state,從而導致next
方法在所有后續調用中也返回{done: true}
- 這就是“關閉”的實際含義。
我們可以通過生成器看到這種行為:
const iter = function*(){ yield 1; yield 2; yield 3; }(); console.log(iter.next()); console.log(iter.return()); console.log(iter.next());
您的第一個片段存在您已覆蓋iter.return
的問題,並且您的方法被調用(從日志中可以看出)但它實際上從未關閉iter
。 根本問題是數組迭代器無法關閉,它們通常根本沒有return
方法。 您還必須覆蓋iter.next
方法來模擬這一點。
您的第二個片段的問題是它實際上並沒有嘗試迭代iter
,但它迭代了myCounter
兩次,這為每個循環創建了一個新的迭代器 object 。 相反,我們需要使用[Symbol.iterator]
方法多次返回相同的 object,最簡單的方法是讓Counter
實現迭代器接口本身。 我們現在可以重現意外的行為:
class Counter { count = 1; [Symbol.iterator]() { return this; } next() { if (this.count <= 5) { return {done: false, value: this.count++ }; } else { return {done: true, value: undefined}; } } return() { console.log('exiting early'); return { done: true, value: undefined }; } } const iter = new Counter(); for (let i of iter) { console.log(i); if (i >= 3) { break; } } console.log('---'); for (let i of iter) { console.log(i); }
為了解決這個問題,我們將通過讓return
方法將計數設置為超過 5 來關閉迭代器:
class Counter { count = 1; [Symbol.iterator]() { return this; } next() { if (this.count <= 5) { return {done: false, value: this.count++ }; } else { return {done: true, value: undefined}; } } return() { this.count = 6; // ^^^^^^^^^^^^^^^ console.log('exiting early'); return { done: true, value: undefined }; } } const iter = new Counter(); for (let i of iter) { console.log(i); if (i >= 3) { break; } } console.log('---'); for (let i of iter) { console.log(i); // not executed! }
您的示例可以簡化為
let count = 1;
const iter = {
[Symbol.iterator]() { return this; },
next() {
if (count <= 5) {
return {
done: false,
value: count++
}
} else {
return {
done: true,
value: undefined
}
}
},
return() {
console.log('exiting early');
return { done: true, value: undefined };
}
};
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
所以iter
只是一個普通的 object。 您將它傳遞給for..of
循環兩次。
您對迭代器接口的工作方式做出了錯誤的假設。 核心問題是這段代碼中沒有任何內容存儲和跟蹤iter
返回done: true
的事實,因此應該繼續這樣做。 如果這是你想要的行為,你需要自己做,例如
let count = 1;
let done = false;
const iter = {
[Symbol.iterator]() { return this; },
next() {
if (!done && count <= 5) {
return {
value: count++
}
} else {
done = true;
return { done };
}
},
return() {
done = true;
console.log('exiting early');
return { done };
}
};
for..of
循環本質上調用.next()
直到返回結果done: true
,並且在某些情況下調用.return
。 由迭代器本身的實現來確保它正確地進入一個“封閉的”state。
所有這一切也可以通過使用生成器 function 來簡化,因為生成器對象具有內部“關閉”state 作為 ZC1C425268E68385D1AB5074C17A94 F14 的副作用自動包含在內
function* counter() {
let counter = 1;
while (counter <= 5) yield counter++;
}
const iter = counter();
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.