本系列博客爲ES6基礎語法的使用及總結,若有錯誤,歡迎指正。
重學ES6之出來混早晚要還的(六)主要包括 Generator、Set/Map、Proxy等。面試
其餘筆記:
重學ES6之出來混早晚要還的(一)
重學ES6之出來混早晚要還的(二)
重學ES6之出來混早晚要還的(三)
重學ES6之出來混早晚要還的(四)
重學ES6之出來混早晚要還的(五)
數據類型的轉換/判斷/比較ajax
Javascript語言的執行環境是"單線程"(single thread)。編程
所謂"單線程",就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務,以此類推。
Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。segmentfault
"同步模式"就是上一段的模式,後一個任務等待前一個任務結束,而後再執行,程序的執行順序與任務的排列順序是一致的、同步的;
"異步模式"則徹底不一樣,每個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,因此程序的執行順序與任務的排列順序是不一致的、異步的。
"異步模式"很是重要。在瀏覽器端,耗時很長的操做都應該異步執行,避免瀏覽器失去響應,最好的例子就是Ajax操做。在服務器端,"異步模式"甚至是惟一的模式,由於執行環境是單線程的,若是容許同步執行全部http請求,服務器性能會急劇降低,很快就會失去響應。數組
優勢是簡單,容易理解和部署;缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程混亂,並且每一個任務只能指定一個回調函數。瀏覽器
任務的執行不取決於代碼的順序,而取決於某個事件是否發生。 服務器
經過事件監聽,能夠綁定多個事件,每一個事件能夠指定多個回調函數,並且能夠"去耦合"(Decoupling),有利於實現模塊化。缺點是整個程序都要變成事件驅動型,運行流程會變得很不清晰。數據結構
咱們假定,存在一個"信號中心",某個任務執行完成,就向信號中心"發佈"(publish)一個信號,其餘任務能夠向信號中心"訂閱"(subscribe)這個信號,從而知道何時本身能夠開始執行。這就叫作"發佈/訂閱模式"(publish-subscribe pattern),又稱"觀察者模式"(observer pattern)。異步
這種方法的性質與"事件監聽"相似,可是明顯優於後者。由於咱們能夠經過查看"消息中心",瞭解存在多少信號、每一個信號有多少訂閱者,從而監控程序的運行。模塊化
Generator 函數是 ES6 提供的一種異步編程解決方案
顧名思義,它是一個生成器,它也是一個狀態機,內部擁有值及相關的狀態。生成器返回一個迭代器Iterator對象,咱們能夠經過這個迭代器,手動地遍歷相關的值、狀態,保證正確的執行順序。
Generator 函數不一樣於普通函數,是能夠暫停執行的,因此函數名以前要加星號,以示區別。
整個 Generator 函數就是一個封裝的異步任務,或者說是異步任務的容器。異步操做須要暫停的地方,都用 yield
關鍵字註明。
yield
關鍵字可讓 Generator內部的邏輯可以切割成多個部分。function* listNum() { let i = 1; yield i; i++; yield i; i++; yield i; } const num = listNum(); console.log(listNum());
next
方法執行一個部分代碼,執行哪一個部分就會返回哪一個部分定義的狀態const num = listNum(); console.log(num.next()); console.log(num.next()); console.log(num.next());
如上代碼,定義了一個listNum的生成器函數,調用以後返回了一個迭代器對象(即num)
調用next
方法後,函數內執行第一條yield
語句,輸出當前的狀態done(迭代器是否遍歷完成)以及相應值(通常爲yield關鍵字後面的運算結果)
每調用一次next
,則執行一次yield
語句,並在該處暫停。
2.1 能夠發現,若是不調用next
方法,那麼函數中封裝的代碼不會當即被執行(下例中的console.log(arr);
沒有運行)
let arr = [ {name: 'zs',age: 38,gender: 'male'}, {name: 'yw',age: 48,gender: 'male'}, {name: 'lc',age: 28,gender: 'male'}, ]; function* loop(arr) { console.log(arr); for(let item of arr){ yield item; } } let repoGen = loop(arr); console.log(repoGen);
2.2 只有開始調用next
方法,生成器函數裏面的代碼纔開始執行
let repoGen = loop(arr); console.log(repoGen.next()); console.log(repoGen.next()); console.log(repoGen.next());
2.3 當遍歷完以後,done標誌變爲true時,再打印生成器函數,能夠發現它的狀態已經變爲了closed
let repoGen = loop(arr); console.log(repoGen); console.log(repoGen.next()); console.log(repoGen.next()); console.log(repoGen.next()); console.log(repoGen.next()); console.log(repoGen);
3.1 調用Generator函數後,不管函數有沒有返回值,都會返回一個迭代器對象
3.2 調用Generator函數後,函數中封裝的代碼不會當即被執行
next()
調用中的傳參在調用next
方法的時候能夠傳遞一個參數, 這個參數會傳遞給上一個yield
注意:第一次調用next()
時是不能傳參的,只能從第二次開始
4.1 第一次調用next以後返回值one爲1,但在第二次調用next的時候one實際上是undefined的,由於generator不會自動保存相應變量值,咱們須要手動的指定,這時two值爲NaN,在第三次調用next的時候執行到yield 3 * two,經過傳參將上次yield返回值two設爲2,獲得結果
function* showNumbers() { var one = yield 1; var two = yield 2 * one; yield 3 * two; } var show = showNumbers(); console.log(show.next().value);// 1 console.log(show.next().value);// NaN console.log(show.next(2).value); // 6
4.2 解析我寫不出來(只能意會不能言傳...)
function* gen() { console.log("123"); let res = yield "aaa"; console.log(res); console.log("567"); yield 1 + 1; console.log("789"); yield true; } let it = gen(); console.log(it.next()); //先輸出123,再輸出{value: "aaa", done: false} console.log(it.next("666")); //傳遞參數給res,輸出666;再輸出567;再輸出{value: 2, done: false} console.log(it.next()); //{value: true, done: false} console.log(it.next()); //{value: undefined, done: true}
5.1 讓函數返回多個值
function* calculate(a, b) { yield a + b; yield a - b; } let it = calculate(10, 5); console.log(it.next().value); console.log(it.next().value);
5.2 用同步的流程來表示異步的操做(用來處理ajax請求的工做流)
5.3 因爲Generator函數就是遍歷器生成函數,所以能夠把Generator賦值給對象的Symbol.iterator屬性,從而使得該對象具備Iterator接口。
for...of循環能夠自動遍歷Generator函數時生成的Iterator對象,且此時再也不須要調用next方法。
function *foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); } // 1 2 3 4 5
上面代碼使用for...of
循環,依次顯示5個yield
語句的值。這裏須要注意,一旦next
方法的返回對象的done
屬性爲true
,for...of
循環就會停止,且不包含該返回對象,因此上面代碼的return
語句返回的6,不包括在for...of
循環之中。
ES6提供了新的數據結構Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。
與數組的不一樣之處:Set數據結構不能經過索引來獲取元素。
Set自己是一個構造函數,用來生成Set數據結構。
格式:new Set([iterable]);
let list = new Set(); console.log(list); let color = new Set(['red', 'yellow', 'green']); console.log(color);
2.1 add(value)
在Set對象尾部添加一個元素。返回該Set對象。
注意:添加相同的成員會被忽略
let list = new Set(); [1,2,3,4,5].map(item => list.add(item)); console.log(list);
2.2 .size
返回Set實例的成員總數。
let list = new Set(); [1,2,3,4,5].map(item => list.add(item)); console.log(list.size); //5
2.3 delete(value)
刪除某個值,返回一個布爾值,表示刪除是否成功。
let list = new Set(); [1,2,3,4,5].map(item => list.add(item)); console.log(list.delete(5)); //true
2.4 .clear
移除Set對象內的全部元素,沒有返回值。
let list = new Set(); [1,2,3,4,5].map(item => list.add(item)); list.clear(); console.log(list); //Set(0) {}
2.5 has(value)
返回一個布爾值,表示該值在Set中存在與否。
let list = new Set(); [1,2,3,4,5].map(item => list.add(item)); console.log(list.has(1)); //true
3.1 .values()
返回一個新的迭代器對象,該對象包含Set對象中的按插入順序排列的全部元素的值。
let list = new Set(); [1,2,3,4,5].map(item => list.add(item)); console.log(list.values());
3.2 調用遍歷器的next()
方法實現遍歷
let list = new Set(); [1,2,3,4,5].map(item => list.add(item)); let it = list.values(); console.log(it.next()); console.log(it.next()); console.log(it.next()); console.log(it.next()); console.log(it.next()); console.log(it.next());
3.3 Set方法部署了Iterator接口,因此也可使用for of
來遍歷
for(let key of list){ console.log(key); }
利用forEach遍歷
list.forEach((item,key,ownSet) => { console.log(item, key, ownSet); })
面試的時候常常有問:用ES5方法和ES6分別實現數組去重
先把數組轉爲Set結構實現去重,由於Set是能夠遍歷的,因此再使用擴展運算符將Set轉爲數組
let arr = [1,2,4,6,4,3,6,8]; let numberSet = new Set(arr); //數組轉Set console.log(numberSet); let uniqueArr = [...numberSet]; //Set轉數組 console.log(uniqueArr);
優雅寫法:
Array.prototype.unique = function () { return [...new Set(this)]; };
WeakSet結構與Set相似,也是不重複的值的集合。
WeakSet結構有如下三個方法。
.add(value)
:向WeakSet實例添加一個新成員。 .delete(value)
:清除WeakSet實例的指定成員。 .has(value)
:返回一個布爾值,表示某個值是否在WeakSet實例之中。
let lucy = {region:'America',age: 18}; let lily = {region:'Canada',age: 20}; let person = new WeakSet([lucy,lily]); person.add('lucas'); console.log(person); //Invalid value used in weak set
for(let key of person){ console.log(key); //person is not iterable }
size
屬性,沒有辦法遍歷它的成員。clear()
方法,可是具備本身清除的做用,避免內存泄漏。let lucy = {region:'America',age: 18}; let lily = {region:'Canada',age: 20}; let person = new WeakSet([lucy,lily]); console.log(person);
將lily對象置爲null以後,先後兩次打印的都是隻有lucy對象
let lucy = {region:'America',age: 18}; let lily = {region:'Canada',age: 20}; let person = new WeakSet([lucy,lily]); console.log(person); lily = null; console.log(person);
Map 對象保存鍵值對。任何值(對象或者原始值) 均可以做爲一個鍵或一個值。
ES6提供的Map數據結構。它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。也就是說,Object結構提供了「字符串—值」的對應,Map結構提供了「值—值」的對應,是一種更完善的Hash結構實現。若是你須要「鍵值對」的數據結構,Map比Object更合適。
new Map([iterable])
const people = new Map(); people.set('lucy',18); people.set('lily',20); console.log(people);
2.1 .size
size屬性返回Map結構的成員總數。
2.2 set(key, value)
set方法設置key所對應的鍵值,而後返回整個Map結構。若是key已經有值,則鍵值會被更新,不然就新生成該鍵。
2.3 get(key)
get方法讀取key對應的鍵值,若是找不到key,返回undefined。
2.4 has(key)
has方法返回一個布爾值,表示某個鍵是否在Map數據結構中。
2.5 delete(key)
delete方法刪除某個鍵,返回true。若是刪除失敗,返回false。
2.6 .clear()
clear方法清除全部成員,沒有返回值。
const people = new Map(); people.set('lucy',18); people.set('lily',20); people.set({},3); for(let key of people){ console.log(key); }
people.forEach((value,key,map) => { console.log(value, key, map); })
Map的鍵(即key)能夠是任意類型值,能夠是一個對象,能夠是一個函數等
const people = new Map(); people.set('lucy',18); people.set('lily',20); people.set({},3); console.log(people);
5.1 Map轉爲數組
Map轉爲數組最方便的方法,就是使用擴展運算符(...)。
const people = new Map(); people.set('lucy',18); people.set('lily',20); people.set({},3); let arr = [...people]; console.log(arr);
5.2 數組轉爲Map
將數組轉入Map構造函數,就能夠轉爲Map。
new Map([[true, 7], [{foo: 3}, ['abc']]])
5.3 Map轉爲對象
若是全部Map的鍵都是字符串,它能夠轉爲對象。
function strMapToObj(strMap) { let obj = Object.create(null); for (let [k,v] of strMap) { obj[k] = v; } return obj; }
5.4 對象轉爲Map
function objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; }
WeakMap與Map的區別在於:
WeakMap與Map在API上的區別主要是兩個:
key()
、values()
和entries()
方法),也沒有size
屬性;clear
方法。這與WeakMap的鍵不被計入引用、被垃圾回收機制忽略有關。所以,WeakMap只有四個方法可用:get()
、set()
、has()
、delete()
。Proxy 對象用於定義基本操做的自定義行爲(如屬性查找,賦值,枚舉,函數調用等)。
幫助咱們重寫對象上的一些默認的方法,定義本身的業務邏輯。
1.用法
let p = new Proxy(target, handler);
①target
用Proxy包裝的目標對象(能夠是任何類型的對象,包括原生數組,函數,甚至另外一個代理)。
②handler
一個對象,其屬性是當執行一個操做時定義代理的行爲的函數。
Proxy 對象的全部用法,都是上面這種形式,不一樣的只是handler參數的寫法。其中,new Proxy()
表示生成一個Proxy實例,target參數表示所要攔截的目標對象,handler參數也是一個對象,用來定製攔截行爲。
2.Proxy實例的方法
get()
用於攔截某個屬性的讀取操做
const person = {name: 'ghk',age: 18}; const personProxy = new Proxy(person,{ get(target, key){ console.log(target, key); //{name: "zs", age: 18} "name" return target[key].toUpperCase(); }, }); personProxy.name = 'zs'; console.log(personProxy); //Proxy {name: "zs", age: 18} console.log(personProxy.name); //ZS
set()
set方法用來攔截某個屬性的賦值操做。
const person = {name: 'ghk',age: 18}; const personProxy = new Proxy(person,{ set(target, key, value){ if(typeof value === 'string'){ target[key] = value.trim(); } } }); personProxy.string = ' this is a test '; console.log(personProxy.string); //THIS IS A TEST