深入理解ES6--迭代器、生成器、代理、反射、Promise
迭代器(Iterator)和生成器(Generator)
for-of迴圈及展開運算子…都是針對迭代器的!!!
- 不能使用箭頭函式來建立生成器;ES6函式的簡寫方式可以(只需在函式名前加星號)
- 可迭代物件具有Symbol.iterator屬性,ES6中,所有的集合物件(陣列、Set集合和Map集合)和字串都是可迭代物件,這些物件都具有預設迭代器;
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item
}
}
}
collection.items.push(1)
collection.items.push(2)
for (let x of collection) console.log(x) // 1 2
內建迭代器
- entries() 返回一個迭代器,其值為多個鍵值對;
let ary = ['a', 'b']
let mySet = new Set(['a', 'b'])
let myMap = new Map().set('name', 'ligang').set('age', 28)
for (let entry of ary.entries()) console.log(entry) // [0, "a"][1, "b"]
for (let entry of mySet.entries()) console.log(entry) // ["a", "a"]["b", "b"]
for (let entry of myMap.entries()) console.log(entry) // ["name", "ligang"]["age", 28]
- values() 返回一個迭代器,其值為集合的值;
- keys() 返回一個迭代器,其值為集合中的所有鍵名
注意:陣列和Set集合的預設迭代器是values();Map集合的預設迭代器是entries()
// Map集合可以使用解構
let myMap = new Map().set('name' , 'ligang')
for (let [key, value] of myMap) console.log(key, value) // name ligang
展開運算子
展開運算子可以作用於可迭代物件,通過迭代器從物件中讀取相應的值並插入到一個數組中。
let ary = ['a', 'b']
let mySet = new Set(['a', 'b'])
let myMap = new Map().set('name', 'ligang').set('age', 28)
console.log([...ary, 'c']) // ["a", "b", "c"]
console.log([...mySet]) // ["a", "b"]
console.log([...myMap]) // [["name", "ligang"], ["age", 28]]
特別說明:
ES6中展開運算子只針對iterable才起作用,預設有陣列、set、map和字串。**並不包含物件!**ES6規範中也並未將展開運算子支援物件,但是目前的主流瀏覽器Chrome和firefox均已實現該特性。這意味著如果想在低版本瀏覽器中使用需要特別的Babel外掛進行轉換!object-rest-spread
生成器返回值
展開運算子與for-of迴圈語句會直接忽略通過return語句指定的任何返回值,只要done變為true就立即停止讀取其他的值!
function *createIterator() {
yield 1;
yield 2;
return 3;
}
let myIterator = createIterator();
console.log(myIterator.next()); // {value: 1, done: false}
console.log(myIterator.next()); // {value: 2, done: false}
console.log(myIterator.next()); // {value: 3, done: true}
let myIterator2 = createIterator();
for(let x of myIterator1) console.log(x) // 1 2
let myIterator3 = createIterator();
console.log([..myIterator3]) // [1, 2]
代理(Proxy)和反射(Reflections)
可以通過代理陷阱複製所有內建javascript物件的行為,當操作發生時,這些陷阱會被自動呼叫;反射API使開發者能夠實現每個代理的預設行為。
區分引數trapTarget和receiver
let target = {
name: 'lg'
}
let proxy = new Proxy(target, {
get(trapTarget, key, receiver) {
console.log(Object.is(target, trapTarget)) // true
console.log(Object.is(target, receiver)) // false
if(!(key in receiver)) {
throw new TypeError(`屬性${key}不存在`)
}
return Reflect.get(trapTarget, key, receiver)
}
})
console.log(proxy.name) // 'lg'
- trapTarget:用於接收屬性(代理的目標)的物件;
- receiver:操作發生的物件(通常是代理)
注意:key in receiver
要使用receiver,不要使用trapTarget,因為key in trapTarget
會出發has陷阱!
Promise與非同步程式設計
部落格中已好多地方提及Promise相關的知識
Promise的使用場景
並行執行兩個非同步操作,當兩個操作都結束時通知你;或者同時進行兩個非同步操作,只取優先完成的操作結果。在這些情況下,Promise是更好的選擇!
Promise執行
Promise的執行器會立即執行,然後才執行後續流程中的程式碼。then中相關的程式碼並不會立即執行,因為完成或拒絕處理程式總是在執行器完成後被新增到任務佇列的末尾。
let promise = new Promise((resolve, reject) => {
console.log(1)
resolve(2)
console.log(3)
})
promise.then(val => console.log(val))
console.log(4)
// 輸出:1 => 3 => 4 => 2
Promise鏈的返回值
let p1 = new Promise((resolve, reject) => {
resolve(42)
})
p1.then((val) => {
console.log(val)
return val + 1
}).then(val => {
console.log(val)
})
// 輸出42、43
Promise鏈中返回Promise
觸發完成處理程式
let p1 = new Promise((resolve, reject) => {
resolve(42)
})
p1.then((val) => {
console.log(val)
let p2 = new Promise((resolve, reject) => {
resolve(43)
})
return p2
}).then(val => console.log(val))
// 輸出42、43
觸發拒絕處理程式
let p1 = new Promise((resolve, reject) => {
resolve(42)
})
p1.then((val) => {
console.log(val)
let p2 = new Promise((resolve, reject) => {
reject('error')
})
return p2
}).then(val => console.log(val))
.catch(err => console.error(err))
// 輸出42、error
捕獲錯誤
在Promise的末尾留有一個拒絕處理程式可以確保能夠正確處理素有可能發生的錯誤。
Promise.reject('Error')
.then(() => console.log(1))
.then(() => console.log(2))
.catch(err => console.log(err))
// 輸出:Error,1、2並不會輸出
Promise.resolve('1')
.then(val => console.log(val))
.then(() => {throw new Error('error')})
.then(() => console.log(2))
.catch(err => console.log(err))
// 輸出:1、Error,2並不會輸出
全域性的拒絕處理
Node環境
unhandledRejection
:當promise失敗(rejected),但又沒有處理時觸發;rejectionHandled
:當promise失敗(rejected),被處理時觸發。
process.on('unhandledRejection', function(reason, promise){})
process.on('rejectionHandled', function(reason, promise){})
瀏覽器環境
unhandledrejection
:當promise失敗(rejected),但又沒有處理時觸發;rejectionhandled
:當promise失敗(rejected),被處理時觸發。
window.onunhandledrejection = function(event) {console.log(event, '...')}
window.onrejectionhandled = function(event) {console.log(event, '...')}
示例
window.addEventListener('unhandledrejection', event => {
// Prevent error output on the console:
event.preventDefault();
console.log('Reason: ' + event.reason);
});
window.addEventListener('rejectionhandled', event => {
console.log('REJECTIONHANDLED');
});
function foo() {
return Promise.reject('abc');
}
var r = foo();
setTimeout(() => {
r.catch(e => {});
}, 0);
// Reason: abc
// REJECTIONHANDLED