本篇目錄
Proxy數組
常見的Proxy攔截操做app
Reflect函數
豬八戒去高老莊找高翠蘭,結果高小姐是孫悟空變的,在這個場景中,對於豬八戒來講,孫悟空能夠算是高小姐的一個代理,在長相上來講,他們是一致的。豬八戒只能訪問到被孫悟空假扮的高小姐,卻見不到真正的高小姐。
在上面的場景中,孫悟空就相似於咱們今天要講的ES6中的Proxy,它是一種「代理」,或者能夠稱之爲「攔截」。外界在對一個對象進行訪問的時候,都先必須經過這層攔截,才能進行訪問。而這個攔截的過程當中能夠對外界的訪問進行過濾和改寫。this
咱們先來看一個例子:設計
let obj = new Proxy({}, { get: function (target, key, receiver) { console.log(`getting ${key}!`); return Reflect.get(target, key, receiver); }, set: function (target, key, value, receiver) { console.log(`setting ${key}!`); return Reflect.set(target, key, value, receiver); } });
在上面這個例子中,咱們經過Proxy對一個空對象進行了攔截,從新定義了對象屬性的讀取(get
)和設置(set
)行爲。代理
ES6 原生提供 Proxy 構造函數,用來生成 Proxy 實例。code
let proxy = new Proxy(target, handler);
其中,new Proxy()表示生成一個Proxy實例,target參數表示所要攔截的目標對象,handler參數也是一個對象,用來定製攔截行爲。server
注意,要使得Proxy起做用,必須針對Proxy實例(上例是proxy對象)進行操做,而不是針對目標對象(上例是空對象)進行操做。
若是handler沒有設置任何攔截,那就等同於直接通向原對象。對象
let target = {}; let handler = {}; let proxy = new Proxy(target, handler); proxy.a = 'b'; target.a // "b"
Proxy支持的攔截操做有:事件
下面咱們就幾個常見的攔截進行舉例說明。
默認一個對象:
let obj={ time:'2017-03-11', name:'net', _r:123 };
// 攔截對象屬性的讀取 get(target,key){ return target[key].replace('2017','2018') }
// 攔截對象設置屬性 set(target,key,value){ if(key==='name'){ return target[key]=value; }else{ return target[key]; } }
// 攔截key in object操做 has(target,key){ if(key==='name'){ return target[key] }else{ return false; } }
// 攔截delete deleteProperty(target,key){ if(key.indexOf('_')>-1){ delete target[key]; return true; }else{ return target[key] } }
// 攔截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames ownKeys(target){ return Object.keys(target).filter(item=>item!='time') }
Proxy.revocable方法返回一個能夠取消的Proxy實例
let target = {}; let handler = {}; let {proxy, revoke} = Proxy.revocable(target, handler); proxy.foo = 123; proxy.foo // 123 revoke(); proxy.foo // TypeError: Revoked
Proxy.revocable方法返回一個對象,該對象的proxy屬性是Proxy實例,revoke屬性是一個函數,能夠取消Proxy實例。上面代碼中,當執行revoke函數以後,再訪問Proxy實例,就會拋出一個錯誤。
Proxy.revocable的一個使用場景是,目標對象不容許直接訪問,必須經過代理訪問,一旦訪問結束,就收回代理權,不容許再次訪問。
雖然 Proxy 能夠代理針對目標對象的訪問,但它不是目標對象的透明代理,即不作任何攔截的狀況下,也沒法保證與目標對象的行爲一致。主要緣由就是在 Proxy 代理的狀況下,目標對象內部的this關鍵字會指向 Proxy 代理。
const target = { m: function () { console.log(this === proxy); } }; const handler = {}; const proxy = new Proxy(target, handler); target.m() // false proxy.m() // true
因此在一些狀況下,有些對象的屬性只能經過正確的this才能拿到時,因爲this指向的變化,致使Proxy沒法代理目標對象。
const target = new Date(); const handler = {}; const proxy = new Proxy(target, handler); proxy.getDate(); // TypeError: this is not a Date object.
爲何咱們要一塊兒來講Reflect呢?Reflect對象與Proxy對象同樣,也是 ES6 爲了操做對象而提供的新 API。
(1) 將Object對象的一些明顯屬於語言內部的方法(好比Object.defineProperty),放到Reflect對象上。現階段,某些方法同時在Object和Reflect對象上部署,將來的新方法將只部署在Reflect對象上。也就是說,從Reflect對象上能夠拿到語言內部的方法。
(2) 修改某些Object方法的返回結果,讓其變得更合理。好比,Object.defineProperty(obj, name, desc)在沒法定義屬性時,會拋出一個錯誤,而Reflect.defineProperty(obj, name, desc)則會返回false。
// 老寫法 try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure } // 新寫法 if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure }
(3) 讓Object操做都變成函數行爲。某些Object操做是命令式,好比name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)讓它們變成了函數行爲。
// 老寫法 'assign' in Object // true // 新寫法 Reflect.has(Object, 'assign') // true
(4)Reflect對象的方法與Proxy對象的方法一一對應,只要是Proxy對象的方法,就能在Reflect對象上找到對應的方法。這就讓Proxy對象能夠方便地調用對應的Reflect方法,完成默認行爲,做爲修改行爲的基礎。也就是說,無論Proxy怎麼修改默認行爲,你總能夠在Reflect上獲取默認行爲。
Proxy(target, { set: function(target, name, value, receiver) { var success = Reflect.set(target,name, value, receiver); if (success) { log('property ' + name + ' on ' + target + ' set to ' + value); } return success; } });
Reflect與ES5的Object有點相似,包含了對象語言內部的方法,Reflect也有13種方法,與proxy中的方法一一對應。
Proxy至關於去修改設置對象的屬性行爲,而Reflect則是獲取對象的這些行爲。
相關使用你們參照Object和Proxy的使用便可,再也不一一贅述。
觀察者模式(Observer mode)指的是函數自動觀察數據對象,一旦對象有變化,函數就會自動執行。
在咱們以前的開發過程當中,咱們若是想要實現觀察者模式的話,咱們可能須要進行事件綁定和觸發來實現。
Event.listen('changeName', name => console.log(name)) Event.trigger('changeName', name )
可是在ES6中,咱們能夠經過使用Proxy和Reflect來實現這個目的
//添加觀察者 const queuedObservers = new Set(); const observe = fn => queuedObservers.add(fn); //proxy 的set 方法 function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); queuedObservers.forEach(observer => observer()); return result; } //建立proxy代理 const observable = obj => new Proxy(obj, {set}); //被觀察的 對象 const person = observable({ name: '張三', age: 20 }); function print() { console.log(`${person.name}, ${person.age}`) } function print2() { console.log(`我是二號觀察者:${person.name}, ${person.age}`) } //添加觀察者 observe(print); observe(print2); person.name = '李四'; // 輸出 // 李四, 20 // 我是二號觀察者:李四, 20
Proxy和Reflect都是ES6中針對對象新增的方法,Proxy修改設置對象的屬性行爲,而Reflect則是獲取對象的這些行爲。