1. 程式人生 > 程式設計 >JavaScript中的Proxy物件

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物件的資料請關注我們其它相關文章!