主要知識點:代理和反射的定義、經常使用的陷阱函數、可被撤銷的代理、將代理對象做爲原型使用、將代理做爲類的原型
《深刻理解ES6》筆記 目錄segmentfault
經過調用 new Proxy() ,你能夠建立一個代理用來替代另外一個對象(被稱之爲目目標對象) ,這個代理對目標對象進行了虛擬,所以該代理與該目標對象表面上能夠被看成同一個對象來對待。
代理容許你攔截目標對象上的底層操做,而這原本是JS引擎的內部能力,攔截行爲適用了一個能響應特定操做的函數(被稱之爲陷阱);函數
被Reflect對象所表明的反射接口,是給底層操做提供默認行爲的方法的集合,這些操做是可以被代理重寫的。每一個代理陷阱都有一個對應的反射方法,每一個方法都與對應的陷阱函數同名,而且接收的參數也與之一致。spa
使用Proxy構建能夠建立一個簡單的代理對象,須要傳遞兩個參數:目標對象以及一個處理器,後者是定義一個或多個陷阱函數的對象。若是不定義陷阱函數,則依然使用目標對象的默認行爲:代理
let target = {}; let proxy = new Proxy(target, {}); proxy.name = "proxy"; console.log(proxy.name); // "proxy" console.log(target.name); // "proxy" target.name = "target"; console.log(proxy.name); // "target" console.log(target.name); // "target"
假設你想要建立一個對象,並要求其屬性值只能是數值,這就意味着該對象的每一個新增屬性都要被驗證,而且在屬性值不爲數值類型時應當拋出錯誤。爲此你須要定義 set 陷阱函數來重寫設置屬性值時的默認行爲,該陷阱函數能接受四個參數:code
Reflect.set() 是 set 陷阱函數對應的反射方法,同時也是 set 操做的默認行爲。
Reflect.set() 方法與 set 陷阱函數同樣,能接受這四個參數,讓該方法能在陷阱函數內部被方便使用:對象
let target = { name: "target" }; let proxy = new Proxy(target, { set(trapTarget, key, value, receiver) { // 忽略已有屬性,避免影響它們 if (!trapTarget.hasOwnProperty(key)) { 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 // 你能夠爲 name 賦一個非數值類型的值,由於該屬性已經存在 proxy.name = "proxy"; console.log(proxy.name); // "proxy" console.log(target.name); // "proxy" // 拋出錯誤 proxy.anotherName = "proxy";
JS 語言有趣但有時卻使人困惑的特性之一,就是讀取對象不存在的屬性時並不會拋出錯誤,而會把 undefined 看成該屬性的值,例如:blog
let target = {}; console.log(target.name); // undefined
在多數語言中,試圖讀取 target.name 屬性都會拋出錯誤,由於該屬性並不存在;但 JS 語言卻會使用 undefined 。
對象外形( Object Shape )指的是對象已有的屬性與方法的集合,因爲該屬性驗證只須在讀取屬性時被觸發,所以只要使用 get 陷阱函數。該陷阱函數會在讀取屬性時被調用,即便該屬性在對象中並不存在,它能接受三個參數:接口
相應的Reflect.get()方法一樣擁有這三個參數。進行對象外形驗證的示例代碼:圖片
//import {color,sum,magicNumber} from "./export.js" import * as example from "./export.js" console.log(example.color) console.log(example.magicNumber) console.log(example. sum(76, 2)) let proxy = new Proxy({}, { get(trapTarget, key, receiver) { if (!(key in receiver)) { throw new TypeError("Property " + key + " doesn't exist."); } return Reflect.get(trapTarget, key, receiver); } }); // 添加屬性的功能正常 proxy.name = "proxy"; console.log(proxy.name); // "proxy" // 讀取不存在屬性會拋出錯誤 console.log(proxy.nme); // 拋出錯誤
in運算符用於判斷指定對象中是否存在某個屬性,若是對象的屬性名與指定的字符串或符號值相匹配,那麼in運算符就會返回true。不管該屬性是對象自身的屬性仍是其原型的屬性。
has陷阱函數會在使用in運算符的狀況下被調用,控制in運算符返回不一樣的結果,has陷阱函數會傳入兩個參數:字符串
trapTarget:代理的目標對象;
key:屬性鍵;
Reflect.has()方法接收相同的參數,並向in運算符返回默認的響應結果,用於返回默認響應結果。
let target = { name: "target", value: 42 }; let proxy = new Proxy(target, { has(trapTarget, key) { if (key === "value") { return false; } else { return Reflect.has(trapTarget, key); } } }); console.log("value" in proxy); // false console.log("name" in proxy); // true console.log("toString" in proxy); // true