Closure 閉包
- function 與 lexical environment (作用域環境) 的組合
- free variable
函式捕捉的是變數,並非變數值。
var fs = []; for (var i = 0; i < 10; ++i){ fs[i] = function(){ return i; // 捕捉的是變數 i } } console.log(fs[0]()); // 10 console.log(fs[1]()); // 10
捕捉變數值解法1
// ex1. ES6以前 function foo(i){ return function(){ return i; } } var fs = []; for(var i = 0; i < 10; ++i){ fs[i] = foo(i); // var有function scope } console.log(fs[0]()); // 0 console.log(fs[1]()); // 1
捕捉變數值解法2
// ex2. ES6 let let fs = []; for(let i = 0; i < 10; ++i){ // let 每次迭代都是重新宣告一個同名變數 i fs[i] = function(){ return i; } } console.log(fs[0]()); // 0 console.log(fs[1]()); // 1
Generatror 產生器函式
*
產生器關鍵字function* generator(){ // ... }
yield
關鍵字- 只能在產生器函式中使用
return
是直接停止函式運行yield
是暫時把控制權讓回給函式呼叫者,函式不會因此停止。yield
實際上會傳回產生器物件(generator object)以下範例
for...of
每次迭代會呼叫next()
方法,直到迭代出來的物件done
為true
function* range(start, end){ for(let i = start; i < end; ++i){ yield i; // 控制權先回到 main program } } // main program let g = range(3, 10); for (let num of g){ console.log(num); }
Generator object 產生器物件
function* range(start, end){ for(let i = start; i < end; ++i){ yield i; } } let g = range(1, 3); g.next(); // {value: 1, done: false} g.next(); // {value: 2, done: false} g.next(); // {value: undefined, done: true}
.next()
方法- type {value: any, done: boolean}
- e.g.,
{value: 3, done: false}
- 每次呼叫
.next()
才會運算並傳回下個產生值 - 可實現 惰性求值
for..of
每次迭代都會呼叫.next()
,所以產生器具有迭代器的行為 可用來當作 Iterator 以實作Symbol.iterator
。
-
function* producer(n){ for(let data = 0; data < n; ++data){ console.log('生產了:', data); yield data; } } function* consumer(n){ for(let i = 0; i < n; ++i){ let data = yield; console.log('消費了:', data); } } function clerk(n, producer, consumer){ console.log('執行', n, '次生產與消費'); let p = producer(n); let c = consumer(n); c.next(); // 先執行至 let data = yield; for(let data of p){ c.next(data); } } clerk(5, producer, consumer);
return()
直接要求產生器停止迭代throw()
Generator函式內也可以用
return
以下範例若用
for.. of
迭代不會迭代出return
傳回值function* range(start, end){ for(let i = start; i < end; ++i){ yield i; } return 'return'; } let g = range(1, 3); g.next(); // {value: 1, done: false} g.next(); // {value: 2, done: false} g.next(); // {value: 'return', done: true}
建立可直接取得其他產生器資料的產生器
// ex1. function* range(start, end){ for(let i = start; i < end; ++i){ yield i; } } function* np_range(n){ for(let i of range(0 - n, 0)){ yield i; } for(let i of range(1, n + 1)){ yield i; } } for(let i of np_range(3)){ console.log(i); }
// ex2. 使用 `yield*` 關鍵字改寫 ex1 function* range(start, end){ for(let i = start; i < end; ++i){ yield i; } } function* np_range(n){ yield* range(0 - n, 0); yield* range(1, n + 1); } for(let i of np_range(3)){ console.log(i); }
(ES6) Template string & tagged template
- Template string 模板字串
// Template string 模板字串 `Hello ${name} !`
Tagged template 標記模板
// Tagged template 標記模板 f(`${a} + ${b} = ${ a + b }`) // f('10 + 20 = 30') f`${a} + ${b} = ${ a + b }` // f(['', ' + ', ' = ', ''], 10, 20, 30)
標記模板應用
String.raw
搭配tagged template可以忽略逃脫文字console.log(String.raw`ABC\nEFG`); // ABC\nEFG
取得字串原始樣貌
function f(strings){ console.log(strings); console.log(strings.raw); } f`ABC\nEFG` // ['`ABC\nEFG`'], ['`ABC\\nEFG`']