跳到主要內容
黯羽輕揚每天積累一點點

JavaScript 生成器

免費2015-11-01#JS#JavaScript的yield#js的迭代器#JavaScript的迭代器#js的生成器#JavaScript generator

ES6 提供了(迭代器的)生成器,本文詳細介紹其語法與基本用法

一。語法

創建語法:function* + yield

使用語法:next(returnValue).value/done

P.S.迭代器生成器function* 定義的東西叫迭代器的生成器(簡稱生成器),因為調用它返回一個迭代器

二。迭代器的作用

###1. 函數節流

把時耗多的複雜任務用 yield 分成小塊慢慢做,也叫函數柯里化 currying,更多信息請查看 JS 學習筆記 11_高級技巧

###2. 生成無限序列

比如斐波那契數列

###3. 方便遍歷

不用手動維護內部狀態

三。JavaScript 生成器實例

###1. 基本用法

function* fun(a) {
    yield a + 1;
    yield a * 2;
    yield a * 2 + 1;
}

var iter = fun(3);
iter.next();
// => Object {value: 4, done: false}
iter.next();
// => Object {value: 6, done: false}
iter.next();
// => Object {value: 7, done: false}


function* fun(a) {
    yield a=a + 1;
    yield a=a * 2;
    yield a=a * 2 + 1;
}

var iter = fun(3);
// => iter.next();
Object {value: 4, done: false}
iter.next();
// => Object {value: 8, done: false}
iter.next();
// => Object {value: 17, done: false}
iter.next();
// => Object {value: undefined, done: true}

函數執行遇到 yield,先 return yield 後面的值,再保存函數執行的 context(效果類似於保存斷點),下一次調用 next() 時,恢復 context,從 yield 的下一句開始執行,遇到 yield 或者 return 退出

###2. 高級用法

function* fib() {
    var a = 1;
    var b = 1;

    while (true) {
        var current = b;
        b = a;
        a = a + current;

        var reset = yield current;
        if (reset) {
            a = 1;
            b = 1;
        }
    }
}

var fibSeq = fib();
fibSeq.next();
// => Object {value: 1, done: false}
fibSeq.next();
// => Object {value: 1, done: false}
fibSeq.next();
// => Object {value: 2, done: false}
fibSeq.next();
// => Object {value: 3, done: false}
fibSeq.next();
// => Object {value: 5, done: false}
fibSeq.next(false);
// => Object {value: 8, done: false}
fibSeq.next(true);
// => Object {value: 1, done: false}
fibSeq.next(false);
// => Object {value: 1, done: false}
fibSeq.next(false);
// => Object {value: 2, done: false}

next() 可以接受參數,此參數會被當做 yield 的返回值傳回函數,這樣就可以控制函數內部的狀態,例如上面的是否 reset

###3. 比較繞的例子

function* fun(a) {
    a = yield a + 1;
    a = yield a * 2;
    yield a * 2 + 1;
}

var iter = fun(3);
iter.next(0);
// => Object {value: 4, done: false}
iter.next(0);
// => Object {value: 0, done: false}
iter.next(1);
// => Object {value: 3, done: false}

類似的:

function* fun(a) {
    a = yield a = a + 1;
    a = yield a = a * 2;
    yield a = a * 2 + 1;
}
var iter = fun(3);
iter.next(0);
// => Object {value: 4, done: false}
iter.next(0);
// => Object {value: 0, done: false}
iter.next(1);
// => Object {value: 3, done: false}

內部執行機制一樣,只是寫法比較繞

###4. 其它

此外,迭代器還有 throw()return() 方法,FF 都實現了,Chrome 只實現了前者,更多信息請查看 MDN Iterators and generators

還有利用 1 個迭代器生成另一個迭代器的語法,例如:

function* fun() {
    yield 1;
    yield 2;
}
var iter = fun();
var newIter = (for (i of iter) i * 2);
newIter.next();
// => Object { value: 2, done: false }
newIter.next();
// => Object { value: 4, done: false }

注意,MDN 的例子有誤,必須是 for...of,且必須是圓括號(例子中的 for...in 和方括號都是錯的)

FF 支持這種語法,Chrome 不支持

三。相關語法

###1.for...of、for...in、forEach

for...of 用來遍歷屬性值,for...in 用來遍歷屬性名,forEach 是 Array.prototype 上的方法,能同時遍歷屬性名和屬性值

此外,for...of 也能遍歷 DOM NodeList 和自定義的迭代器(function* + yield),是 ES6 的新東西

實例如下:

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"
}


let arr = [3, 5, 7];
arr.foo = "hello";

arr.forEach(function (element, index) {
    console.log(element); // logs "3", "5", "7"
    console.log(index);   // logs "0", "1", "2"
});

###2.yield*

yield 功能類似,也是用來遍歷的,但 yield* 後面跟迭代器對象,作用是進入後面的迭代器,遍歷完再回來,例如:

function* g1() {
  yield 2;
  yield 3;
  yield 4;
}

function* g2() {
  yield 1;
  yield* g1();
  yield 5;
}

var iterator = g2();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

有了 yield* 後,迭代器可以嵌套了,可以「線性」遍歷層級結構(當然,前提是拿到所有下層結構的迭代器對象)

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論