TypeScript實戰指南》—1.2 準備環境">《TypeScript實戰指南》—1.2 準備環境
688
2025-03-31
2.1.7 iterator和generator
1. iterator
當一個對象實現了Symbol.iterator時,我們認為它是可迭代的。如array、map、set、string、int32Array、uint32Array等一些內置的類型,目前都已經實現了各自的Symbol.iterator。對象上的Symbol.iterator函數負責返回供迭代的值。
for..of語句會遍歷可迭代的對象,調用對象上的Symbol.iterator方法。
比如下面是在數組上使用for..of的例子:
const array = [233, "hello", true];
for (let value of array) {
console.log(value); // 233, "hello", true
}
for..of和for..in都可以迭代一個數組,但它們之間的區別很大。最明顯的區別莫過于它們用于迭代器的返回值并不相同,for..in迭代的是對象的鍵,而for..of迭代的是對象的值。
我們可以從下面的例子中看出兩者之間的區別:
const array = [3, 4, 5];
for (let i in array) {
console.log(i); // 0, 1, 2
}
for (let i of array) {
console.log(i); // 4, 5, 6
}
另一個區別在于,for..in可以操作任何對象,提供了查看對象屬性的一種方法。但是for..of關注迭代對象的值,內置對象Map和Set已經實現了Symbol.iterator方法,讓我們可以訪問它們的值:
const fruits = new Set(["apple", "pear", "mango"]);
fruits["peach"] = "Princess Peach! Make a wish!";
for (let fruit in fruits) {
console.log(fruit); // "peach"
}
for (let fruit of fruits) {
console.log(fruit); // "apple", "pear", "peach"
}
但這樣的特性僅僅在 ES 6 及以上才上生效。
當我們將 TypeScript 的代碼生成目標設定為ES5或ES3,迭代器就只允許在array類型上使用。在非數組值上使用for..of語句會得到一個錯誤。即便這些非數組值已經實現了Symbol.iterator屬性,也是不可以的。
編譯器會生成一個簡單的for循環作為for..of循環,比如:
const numbers = [1, 2, 3];
for (let number of numbers) {
console.log(number);
}
生成的代碼為:
var numbers = [1, 2, 3];
for (var _i = 0; _i < numbers.length; _i++) {
var number = numbers[_i];
console.log(number);
}
2. generator
function * 是用來創建? generator 函數的語法。(在 MDN 的文檔中 generator 稱為生成器。)
調用 generator 函數時會返回一個generator 對象。generator 對象遵循迭代器接口,即通常所見到的 next、return 和 throw 函數。
generator函數用于創建懶迭代器,例如下面的這個函數可以返回一個無限整數的列表:
function* infiniteList() {
let i = 0;
while(true) {
yield i++;
}
}
var iterator = infiniteList();
while (true) {
console.log(iterator.next()); // { value: xxxx, done: false }
}
當然,也可以設定某個條件終止它,而不只是永遠循環下去。如下所示:
function* infiniteList(){
let i = 0;
while(i < 3)
yield i++;
}
let gen = infiniteList();
console.log(gen.next()); // { value: 0, done: false }
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: undefined, done: true }
可以說這個設定是generator中最令人興奮的部分。它在實質上允許一個函數可以暫停執行,比如當我們執行了第一次的 gen.next() 后,可以先去做別的事,再回來繼續執行 gen.next(),這樣剩余函數的控制權就交給了調用者。
當你直接調用 generator 函數時,它并不會執行,它只會創建一個 generator 對象。
在下面的例子中,我們可以看到一個更靈活的使用方式:
function* generator(){
console.log('Execution started');
yield 0;
console.log('Execution resumed');
yield 1;
console.log('Execution end');
}
執行它,則會看到如下輸出結果:
constiterator = generator();
console.log(iterator.next());
// "Execution started"
// { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
// "Execution resumed'"
// { value: 1, done: false }
console.log(iterator.next());
// "Execution end"
// { value: undefined, done: true }
console.log(iterator.next());
// { value: undefined, done: true }
從上面代碼可以得知:
generator 對象只會在調用next時開始執行。
函數在執行到yield語句時會暫停并返回yield的值。
函數在next被調用時繼續恢復執行。
所以實質上generator函數的執行與否是由外部的 generator 對象控制的。
不過除了 yield 傳值到外部,我們也可以通過 next 傳值到內部進行調用。下面的例子展示了iterator.next傳值的方式:
function* generator() {
const who = yield;
console.log('hello '+ who); // bar!
}
const iterator = generator();
Console.log(iterator.next());
// {value: undefined, done: false}
console.log(iterator.next('TypeScript'));
// hello TypeScript
// {value: undefined, done: true}
以上便是 next 和 return 函數的內容,接下來我們來看一下 throw 函數如何處理迭代器內部報錯。
下面是iterator.throw的例子:
function* generator() {
try {
yield 1;
}
catch(error) {
console.log(error.message);
}
}
const iterator = generator();
iterator.next()
// {value: 1, done: false}
iterator.throw(new Error('something incorrect'));
// something incorrect
// {value: undefined, done: true}
通過以上的案例我們可以得知,外部是可以對 generator 內部進行干涉的:
外部系統可以傳遞一個值到 generator 函數體中。
外部系統可以拋入一個異常到 generator 函數體中。
Generator TypeScript
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。