最簡單的代理是空代理,就是說除了做爲一個抽象的目標對象,什麼也不作,在代理對象是執行的全部操做都會無障礙的傳播到目標對象,所以,任何使用目標對象的地方,均可以經過一樣的方式使用與之關聯的代理對象markdown
例子1:函數
const target = { id: 'target' };
const handler = {}; const proxy = new Proxy(target, handler); // id屬性會訪問同一個值 console.log(target.id); // target console.log(proxy.id); // target // 給目標屬性賦值會反映在兩個對象上, 由於兩個對象訪問的是同一個值 target.id = 'foo'; console.log(target.id); // foo console.log(proxy.id); // foo // 給代理屬性賦值會反映在兩個對象上,由於這個賦值會轉移到目標對象 proxy.id = 'bar'; console.log(target.id); // bar console.log(proxy.id); // bar // hasOwnProperty()方法在兩個地方 都會應用到目標對象 console.log(target.hasOwnProperty('id')); // true console.log(proxy.hasOwnProperty('id')); // true 複製代碼
小結:這是最簡單的一個空代理的案例,初看可能會不明因此,我當初看也很懵,可是不要着急,你如今只須要看懂,知道代理作了什麼事情,在這個例子裏,target是代理的目標,proxy是代理,代理的目標和代理屬性共享,操做共享,包括方法也是共享的,可是代理並不等於代理目標,更傾向於操做代理時,代理把操做轉發給了代理的目標,而後進行操做,目標擁有的,代理會擁有,代理擁有的,也會相應的傳遞給目標;oop
使用代理的「目的」 :是能夠定義「捕獲器」(trap),捕獲器就是能夠直接或間接在代理對象上調用,每次在代理對象上調用這些基本操做時, 代理能夠在這些操做傳播到目標對象以前先調用捕獲器函數,從而攔 截並修改相應的行爲。ui
「handler」 :代理的處理對象,例子1中是一個空對象,多數狀況下並非空對象,而是定義了一個或者多個捕獲器(trap)去處理代理,若是沒有定義,則和上例中的空對象同樣,使用默認行爲。this
「set trap」 :經常使用的trap,觸發條件是在設置屬性值的時候觸發,spa
「Reflect.set」: 將值分配給屬性的函數。返回一個Boolean
,若是更新成功,則返回true
代理
首先set trap接受4個參數,code
trapTarget - 接收的屬性的對象,就是**代理的目標 **orm
key - 要寫入的屬性的「鍵」對象
value- 寫入屬性的「值」
receiver- 操做的對象,一般是「代理」
例子2:用set trap驗證一個屬性的值是否爲number
let target = {
name: "target" }; let proxy = new Proxy(target, { //target,name,target,proxy; set(trapTarget, key, value, receiver) { console.log(`trapTarget is ${trapTarget}, key is ${key}, value is ${value}, receiver is ${receiver}`) // 忽視存在的屬性,以避免產生影響,不存在的屬性纔會進入判斷 if (!trapTarget.hasOwnProperty(key)) { //判斷值是否爲number if (isNaN(value)) { throw new TypeError("Property must be a number."); } } // 添加到屬性 return Reflect.set(trapTarget, key, value, receiver); } }); // 添加一個新的屬性 proxy.count = 1; console.log(proxy.count); // 1 console.log(target.count); // 1 // 賦值給存在target上的屬性 proxy.name = "proxy"; console.log(proxy.name); // "proxy" console.log(target.name); // "proxy" // 新的屬性值不是數字會拋出異常 proxy.anotherName = "proxy"; 複製代碼
小結:這個例子打印出了4個參數的時刻變化,能夠清晰的看到參數對應的值,能夠發現,每次設置屬性值的時候都會進行攔截判斷,因此在獲取時候,能夠用get進行攔截判斷,
複製代碼
「get trap」:
get 是讀取對象屬性的時候用到的trap,他接受3個參數
trapTarget: 從哪一個對象讀取的屬性,就是target
key
- 讀取的key
receiver
- 操做的對象,一般是代理(proxy)
例子1:這是一個普通的get代理的按理
const handler = {
get: function(obj, prop) { return prop in obj ? obj[prop] : 37; } }; const p = new Proxy({}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37 複製代碼
小結:p.a的時候,獲取代理裏面a的值通過get,返回了 a的值,p.b同樣,而後c in p,由於p沒有c這個屬性,因此會返回false,p.c像p獲取c屬性,進入get,賦值爲37,
複製代碼
再來看一個set 和get的綜合例子
例子3;
var p = {
name:"chen", work:function() { console.log("wording..."); }, _age:18, //監聽獲取name屬性的事件 get name(){ return this._age; }, //監聽設置age屬性的事件 set age(val) { if (val<0 || val> 100) {//若是年齡大於100就拋出錯誤 throw new Error("invalid value") }else{ this._age = val; } } }; console.log(p.name);//輸出18,由於這裏觸發了監聽name屬性的攔截,因此返回的age, console.log(p._age);//這裏的age沒有獲取攔截,因此輸出原值 console.log(p._age =20);//輸出20,由於這裏進入設置攔截,知足條件,完成賦值 console.log(p._age =200);//報錯,設置攔截進入,不知足條件,報錯 console.log(p.name = 'yu')//輸出 yu,由於name沒有設置攔截,因此能夠成功 複製代碼
小結:看完這個,代理的基本知識及其原理相信你們都明白了,其實捕獲器還有不少,列如has等等,可是基本原理都是這樣,只要把握住參數分別表明什麼,捕獲器的觸發條件,問題均可以迎刃而解了,但願能夠給你們帶來幫助
本文使用 mdnice 排版