Proxy 用於修改某些操做的默認行爲;能夠理解成,在目標對象以前架設一層"攔截",外界對對象的訪問,都必須先經過這層攔截,所以提供了一層機制,能夠對外界的訪問進行過濾和改寫。javascript
const proxy = new Proxy(target,handler); target表示所要攔截的目標對象,handler參數也是一個對象,用來定製攔截行爲。 const 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) } }) obj.count = 1 console.log(++obj.count)//2
get(target,prop,receiver ):攔截某個屬性的讀取,三個參數依次爲目標對象、屬性名和 proxy 實例自己 receiver, 其中 receiver 爲可選;java
function createArray(...elements) { let handler = { get(target, propKey, receiver) { console.log(propKey) let index = Number(propKey); if(index < 0) { propKey = String(target.length + index); } return Reflect.get(target, propKey, receiver) } }; let target = []; target.push(...elements); return new Proxy(target, handler); } let arr = createArray('a','b','c'); console.log(arr[-1]);// c console.log(arr[0]);// b console.log(arr[1])// a
這個例子用 get 攔截,實現根據數組索引讀取數組;當索引爲負時,表示數組遍歷爲從後到前;因此,當數組的位置是 -1 的時候,就會輸出數組的倒數第一個成員 c數組
set(target,prop, value, receiver):用來攔截某個屬性的賦值操做; prop 爲屬性名,value 爲屬性值,receiver 依舊可選;app
用set()做爲數據驗證:函數
let validator= { set: function(obj,prop,value) { if(prop ==='age') { if(!Number.isInteger(value)) { throw new TypeError('The age is not an integer') } if(value > 200) { throw new RangeError('The age seems invalid') } } // 對於age之外的屬性,直接保存 obj[prop] = value; } } let person = new Proxy({}, validator) person.age = 100; console.log(person.age)// 100 person.age = 300; console.log(person.age);// 報錯
任何不符合要求的age屬性賦值,都會報錯。this
apply(target,ctx, args):攔截函數的調用,call 和 apply 操做;參數 ctx 指的是目標對象的上下文對象 (this) 和目標對象的參數數組。代理
const twice = { apply(target, ctx, args) { console.log(args);// 1,2 return Reflect.apply(...arguments)*2; } }; function sum(left, right) { return left + right; }; const proxy = new Proxy(sum,twice); console.log(proxy(1,2));//6
當執行proxy()時,會被apply()攔截;code
has(target,key):用來攔截 hasProperty 操做,即判斷對象是否具備某個屬性;典型的操做就是 in 運算符。對象
const handler = { has(target,key) { if(key[0] === '_') { return false; } return key in target; } }; const target = {_prop: 'foo', prop: 'foo'}; const proxy = new Proxy(target,handler); console.log('_prop' in proxy);// false;
這裏的代碼中,若是原對象的屬性名的第一個字符時下劃線,proxy.has() 就會返回 false,從而不被 in 運算符發現。注意:has() 攔截的是hasProperty 操做,而不是 hasOwnProperty 操做,即它不判斷屬性來源。索引
雖然 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
上面代碼中,一旦 proxy 代理 target.m,後者內部的 this 就指向了 proxy 而不是 target 了。