概述
Proxy 用於修改某些操作的默認行為,等同於在語言層面做出修改,所以屬於一種“元編程”(meta programming),即對編程語言進行編程。
Proxy 可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這里表示由它來“代理”某些操作,可以譯為“代理器”。
基本使用
let obj = { a: 1, b: 2 } let proxyObj = new Proxy(obj, { get: function() { // 攔截了既有的操作,返回自己的邏輯 return "獲取值的內容" }, set: function() { console.log("設置值的內容"); } }) console.log(proxyObj.a); proxyObj.b = 20 console.log(proxyObj);
proxyObj是設置可以攔截的對象,第一個obj參數是對應要攔截的對象內容,第二個對象參數是攔截行為,所有的行為在一個對象中
行為的內部可以設置參數
let obj = { a: 1, b: 2 } let proxyObj = new Proxy(obj, { get: function(target, key) { // 攔截了既有的操作,返回自己的邏輯 return `屬性${key},屬性值:${target[key]}` }, set: function(target, key, value) { console.log(`屬性${key},屬性值:${target[key]},要設置的參數:${value}`); } }) console.log(proxyObj.a); proxyObj.b = 20 console.log(proxyObj);
set方法主要的參數有兩個,第一個target指的目標對象,key表示獲取的該對象的key,
第二個set方法主要的參數有三個,第一個target指的目標對象,key表示獲取的該對象的key,value表示要設置的value
Proxy可以當做原型對象
var proxy = new Proxy({}, { get: function(target, property) { return 1; } }); let obj = Object.create(proxy); console.log(obj.a)
get方法
get方法用於攔截某個屬性的讀取操作。
var person = { name: "小明" }; var proxy = new Proxy(person, { get: function(target, property) { // 判斷當前的key是否在對應的對象上,也就是查詢對象上有沒有要查的這個key if (property in target) { // 如果有就返回這個值 return target[property]; } else { // 沒有則拋出錯誤 throw new ReferenceError("Property \"" + property + "\" does not exist."); } } }); console.log(proxy.name ) console.log(proxy.age )
get方法內部有三個參數
let obj = { a: 1, b: 2 } let proxObj = new Proxy(obj, { get: function(target, key, receiver) { // 攔截了既有的操作,返回自己的邏輯 return `屬性${key},屬性值:${target[key]}` } }) console.log(proxObj.a);
第一個target表示接受對象,第二個是獲取的key,第三個表示操作對象
set方法
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 = 'young' // 報錯 person.age = 300 // 報錯
set方法一共有4個參數,第一個參數是接受的對象,第二個參數是設置的key,第三個是設置值,第四個參數是操作對象
let obj = { a: 1, b: 2 } let proxObj = new Proxy(obj, { set: function(target, key, value, receiver) { console.log(receiver); // 攔截了既有的操作,返回自己的邏輯 return target[key] = "3" } }) proxObj.b = 20 console.log(proxObj);
apply方法
apply方法攔截函數的調用、call和apply操作。
var twice = { apply(target, ctx, args) { // Reflect.apply(...arguments)返回是對參數的累加結果 return Reflect.apply(...arguments) * 2; } }; function sum(left, right) { // 累加 return left + right; }; var proxy = new Proxy(sum, twice); console.log(proxy(1, 2) ) console.log(proxy.call(null, 5, 6) ) console.log(proxy.apply(null, [7, 8]))
construct方法
construct方法用於攔截new命令,下面是攔截對象的寫法。
var p = new Proxy(function() {}, { construct: function(target, args) { console.log('called: ' + args.join(', ')); return { value: args[0] * 10 }; } }); new p(1).value
construct方法返回的必須是一個對象,否則會報錯。
var p = new Proxy(function() {}, { construct: function(target, argumentsList) { return 1; } }); new p()
Proxy 支持的攔截操作
對於可以設置、但沒有設置攔截的操作,則直接落在目標對象上,按照原先的方式產生結果。
(1)get(target, propKey, receiver)
攔截對象屬性的讀取,比如proxy.foo
和proxy['foo']
。
最后一個參數receiver
是一個對象,可選。
(2)set(target, propKey, value, receiver)
攔截對象屬性的設置,比如proxy.foo = v
或proxy['foo'] = v
,返回一個布爾值。
(3)has(target, propKey)
攔截propKey in proxy
的操作,返回一個布爾值。
(4)deleteProperty(target, propKey)
攔截delete proxy[propKey]
的操作,返回一個布爾值。
(5)ownKeys(target)
攔截Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
,返回一個數組。該方法返回目標對象所有自身的屬性的屬性名,而Object.keys()
的返回結 果僅包括目標對象自身的可遍歷屬性。
(6)getOwnPropertyDescriptor(target, propKey)
攔截Object.getOwnPropertyDescriptor(proxy, propKey)
,返回屬性的描述對象。
(7)defineProperty(target, propKey, propDesc)
攔截Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一個布爾值。
(8)preventExtensions(target)
攔截Object.preventExtensions(proxy)
,返回一個布爾值。
(9)getPrototypeOf(target)
攔截Object.getPrototypeOf(proxy)
,返回一個對象。
(10)isExtensible(target)
攔截Object.isExtensible(proxy)
,返回一個布爾值。
(11)setPrototypeOf(target, proto)
攔截Object.setPrototypeOf(proxy, proto)
,返回一個布爾值。
如果目標對象是函數,那么還有兩種額外操作可以攔截。
(12)apply(target, object, args)
攔截 Proxy 實例作為函數調用的操作,比如proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。
(13)construct(target, args)
攔截 Proxy 實例作為構造函數調用的操作,比如new proxy(...args)
。