JavaScript中的Proxy物件
Js中Proxy物件
Proxy物件用於定義基本操作的自定義行為,例如屬性查詢、賦值、列舉、函式呼叫等。
語法
const proxy = new Proxy(target,handler);
- target: 要使用Proxy包裝的目標物件,可以是任何型別的物件,包括原生陣列,函式,甚至另一個代理。
- handler: 一個通常以函式作為屬性的物件,各屬性中的函式分別定義了在執行各種操作時代理proxy的行為。
描述
Proxy用於修改某些操作的預設行為,也可以理解為在目標物件之前架設一層攔截,外部所有的訪問都必須先通過這層攔截,因此提供了一種機制,可以對外部的訪問進行過濾和修改。這個詞的原理為代理,在這裡可以表示由它來代理某些操作,譯為代理器。
var target = {a: 1}; var proxy = new Proxy(target,{ set: function(target,key,value,receiver){ console.log("watch"); return Reflect.set(target,receiver); },get: function(target,receiver){ return target[key]; } }); proxy.a = 11; // watch console.log(target); // {a: 11}
Object.defineProperty是用於監聽屬性,而Proxy是監聽整個物件,通過呼叫new Proxy(),可以建立一個代理用來替代另一個物件被稱為目標,這個代理對目標物件進行了虛擬,因此該代理與該目標物件表面上可以被當作同一個物件來對待。代理允許攔截在目標物件上的底層操作,而這原本是Js引擎的內部能力,攔截行為使用了一個能夠響應特定操作的函式,即通過Proxy去對一個物件進行代理之後,我們將得到一個和被代理物件幾乎完全一樣的物件,並且可以從底層實現對這個物件進行完全的監控。
// 常見的一道面試題 實現 a===1&&a===2&&a===3 為true // Object.defineProperty 定義的是屬性 // 可以實現對於題目的要求 var _a = 0; Object.defineProperty(window,"a",{ get:function(){ return ++_a; } }) console.log(a===1 && a===2 && a===3); // true // proxy 代理的是物件 // 因此在呼叫時實際與題目要求並不太相符 // 但同樣也是一種實現方式 var _a = 0; var proxy = new Proxy(window,receiver){ return Reflect.set(target,receiver){ if(key === "a") return ++_a; else return window[key]; } }); console.log(proxy.a===1 && proxy.a===2 && proxy.a===3); //true
方法
Proxy.revocable()
Proxy.revocable(target,handler)
Proxy.revocable()方法可以用來建立一個可撤銷的代理物件,其返回一個包含了代理物件本身和它的撤銷方法的可撤銷Proxy物件。
- target: 將用Proxy封裝的目標物件,可以是任何型別的物件,包括原生陣列,函式,甚至可以是另外一個代理物件。
- handler: 一個物件,其屬性是一批可選的函式,這些函式定義了對應的操作被執行時代理的行為。
該方法的返回值是一個物件,其結構為{"proxy": proxy,"revoke": revoke},一旦某個代理物件被撤銷,它將變得幾乎完全不可呼叫,在它身上執行任何的可代理操作都會丟擲TypeError異常,注意可代理操作一共有14種,執行這14種操作以外的操作不會丟擲異常。一旦被撤銷,這個代理物件便不可能被直接恢復到原來的狀態,同時和它關聯的目標物件以及處理器物件都有可能被垃圾回收掉。再次呼叫撤銷方法revoke()則不會有任何效果,但也不會報錯。
var revocable = Proxy.revocable({},{ get: function(target,key) { return `[[ ${key} ]]`; } }); var proxy = revocable.proxy; console.log(proxy.example); // [[ example ]] revocable.revoke(); // console.log(proxy.example); // 丟擲 TypeError // proxy.example = 1; // 丟擲 TypeError // delete proxy.example; // 丟擲 TypeError // typeof proxy // "object",因為 typeof 不屬於可代理操作
handler物件方法
handler物件是一個容納一批特定屬性的佔位符物件,它包含有Proxy的各個捕獲器trap。所有的捕捉器是可選的,如果沒有定義某個捕捉器,那麼就會保留源物件的預設行為。
- handler.getPrototypeOf(): Object.getPrototypeOf方法的捕捉器。
- handler.setPrototypeOf(): Object.setPrototypeOf方法的捕捉器。
- handler.isExtensible(): Object.isExtensible方法的捕捉器。
- handler.preventExtensions(): Object.preventExtensions方法的捕捉器。
- handler.getOwnPropertyDescriptor(): Object.getOwnPropertyDescriptor方法的捕捉器。
- handler.defineProperty(): Object.defineProperty方法的捕捉器。
- handler.has(): in操作符的捕捉器。
- handler.get(): 屬性讀取操作的捕捉器。
- handler.set(): 屬性設定操作的捕捉器。
- handler.deleteProperty(): delete操作符的捕捉器。
- handler.ownKeys(): Reflect.ownKeys、Object.getOwnPropertyNames、Object.keys、Object.getOwnPropertySymbols方法的捕捉器。
- handler.apply(): 函式呼叫操作的捕捉器。
- handler.construct(): new操作符的捕捉器。
var target = { a: 1,f: function(...args){ console.log(...args); } }; var proxy = new Proxy(target,{ getPrototypeOf: function(target) { console.log("getPrototypeOf"); return Object.getPrototypeOf(target); },setPrototypeOf: function(target,prototype) { console.log("setPrototypeOf"); return Object.setPrototypeOf(target,prototype); },isExtensible: function(target) { console.log("isExtensible"); return Object.isExtensible(target); },preventExtensions: function(target) { console.log("preventExtensions"); return Object.preventExtensions(target); },getOwnPropertyDescriptor: function(target,prop) { console.log("getOwnPropertyDescriptor"); return Object.getOwnPropertyDescriptor(target,prop); },defineProperty: function(target,prop,descriptor) { console.log("defineProperty"); return Object.defineProperty(target,descriptor); },has: function(target,prop) { console.log("has"); return prop in target; },receiver) { console.log("get"); return target[prop]; },set: function(target,receiver) { console.log("set"); target[prop] = value; return true; },deleteProperty: function(target,property) { console.log("deleteProperty"); delete target[property]; return true; },ownKeys: function(target) { console.log("ownKeys"); return Reflect.ownKeys(target); } }) var proxyF = new Proxy(target.f,{ construct: function(target,argumentsList,newTarget) { console.log("construct"); return new target(...argumentsList); },apply: function(target,thisArg,argumentsList) { console.log("apply"); return target.apply(thisArg,argumentsList); },}) const _prototype = {test: 1}; Object.setPrototypeOf(proxy,_prototype); // setPrototypeOf console.log(Object.getPrototypeOf(proxy)); // getPrototypeOf // { test: 1 } Object.preventExtensions(proxy); // preventExtensions console.log(Object.isExtensible(proxy)); // isExtensible // false Object.defineProperty(proxy,{configurable: true}); // defineProperty console.log(Object.getOwnPropertyDescriptor(proxy,"a")); // getOwnPropertyDescriptor // { value: 1,writable: true,enumerable: true,configurable: true } proxy.a = 11; // set console.log(proxy.a); // get // 11 console.log(Object.keys(proxy)); // ownKeys getOwnPropertyDescriptor getOwnPropertyDescriptor // [ 'a','f' ] delete proxy.a; // deleteProperty console.log("a" in proxy); // has // false proxyF(1,2,3); // apply 1 2 3 new proxyF(1,3); // construct 1 2 3
每日一題
https://github.com/WindrunnerMax/EveryDay
以上就是JavaScript中的Proxy物件的詳細內容,更多關於JavaScript Proxy物件的資料請關注我們其它相關文章!