defineProperty()
學(xué)習(xí)書(shū)籍《ECMAScript 6 入門(mén) 》
Proxy
Proxy 用于修改某些操作的默認(rèn)行為,等同于在語(yǔ)言層面做出修改,所以屬于一種“元編程”(meta programming),即對(duì)編程語(yǔ)言進(jìn)行編程。
Proxy 可以理解成,在目標(biāo)對(duì)象之前架設(shè)一層“攔截”,外界對(duì)該對(duì)象的訪問(wèn),都必須先通過(guò)這層攔截,因此提供了一種機(jī)制,可以對(duì)外界的訪問(wèn)進(jìn)行過(guò)濾和改寫(xiě)。Proxy 這個(gè)詞的原意是代理,用在這里表示由它來(lái)“代理”某些操作,可以譯為“代理器”。
Proxy 構(gòu)造函數(shù)
var proxy = new Proxy(target,handler);
Proxy 對(duì)象的所有用法,都是上面這種形式,不同的只是handler參數(shù)的寫(xiě)法。其中,new Proxy()表示生成一個(gè)Proxy實(shí)例,target參數(shù)表示所要攔截的目標(biāo)對(duì)象,handler參數(shù)也是一個(gè)對(duì)象,用來(lái)定制攔截行為。
實(shí)例代碼:
var handler = {
? ? get: function(target, name) {
? ? ? ? if (name === 'prototype') {
? ? ? ? ? ? return Object.prototype;
? ? ? ? }
? ? ? ? return 'Hello, ' + name;
? ? },
? ? apply: function(target, thisBinding, args) {
? ? ? ? return args[0];
? ? },
? ?construct: function(target, args) {
? ? ? ? return {value: args[1]};
? ? }
};
var fproxy = new Proxy(function(x, y) {
? ? return x + y;
? }, handler);
fproxy(1, 2) ?// 1
new fproxy(1,2) // {value: 2}
fproxy.prototype === Object.prototype // true
fproxy.foo // "Hello, foo"
下面是 Proxy 支持的攔截操作一覽。
(1)get(target, propKey, receiver)
攔截對(duì)象屬性的讀取,比如proxy.foo和proxy['foo']。
最后一個(gè)參數(shù)receiver是一個(gè)對(duì)象,可選,參見(jiàn)下面Reflect.get的部分。
(2)set(target, propKey, value, receiver)
攔截對(duì)象屬性的設(shè)置,比如proxy.foo = v或proxy['foo'] = v,返回一個(gè)布爾值。
(3)has(target, propKey)
攔截propKey in proxy的操作,返回一個(gè)布爾值。
(4)deleteProperty(target, propKey)
攔截delete proxy[propKey]的操作,返回一個(gè)布爾值。
(5)ownKeys(target)
攔截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一個(gè)數(shù)組。該方法返回目標(biāo)對(duì)象所有自身的屬性的屬性名,而Object.keys()的返回結(jié)果僅包括目標(biāo)對(duì)象自身的可遍歷屬性。
(6)getOwnPropertyDescriptor(target, propKey)
攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述對(duì)象。
(7)defineProperty(target, propKey, propDesc)
攔截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一個(gè)布爾值。
(8)preventExtensions(target)
攔截Object.preventExtensions(proxy),返回一個(gè)布爾值。
(9)getPrototypeOf(target)
攔截Object.getPrototypeOf(proxy),返回一個(gè)對(duì)象。
(10)isExtensible(target)
攔截Object.isExtensible(proxy),返回一個(gè)布爾值。
(11)setPrototypeOf(target, proto)
攔截Object.setPrototypeOf(proxy, proto),返回一個(gè)布爾值。
如果目標(biāo)對(duì)象是函數(shù),那么還有兩種額外操作可以攔截。
(12)apply(target, object, args)
攔截 Proxy 實(shí)例作為函數(shù)調(diào)用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
(13)construct(target, args)
攔截 Proxy 實(shí)例作為構(gòu)造函數(shù)調(diào)用的操作,比如new proxy(...args)。
get()
下面的例子使用get攔截,實(shí)現(xiàn)數(shù)組讀取負(fù)數(shù)的索引。
function createArray(...elements) {
? ? let handler = {
? ? ? ? get(target, propKey, receiver) {
? ? ? ? ? ? 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');
arr[-1] // c
該例子,通過(guò)判定index來(lái)對(duì)數(shù)組的角標(biāo)返回值進(jìn)行攔截過(guò)濾
if(index<0){
? ? propKey=String(target.length+index);
}
即當(dāng)index小于0時(shí),propkey也就是 arr[-1]? 傳入的-1的index,對(duì)其進(jìn)行修改,讓 propkey的值變?yōu)?(target.length+index)= 3+(-1)=2;
然后執(zhí)行反射函數(shù) Reflect;
Reflect.get(target, propKey, receiver)
通過(guò)字面意思翻譯以及上面代碼的返回值 可猜測(cè)到? Refect為反射函數(shù);
而該句的意思為:用過(guò)Reflect 調(diào)用 攔截對(duì)象 本身的get方法執(zhí)行;
利用 Proxy,可以將讀取屬性的操作(get),轉(zhuǎn)變?yōu)閳?zhí)行某個(gè)函數(shù),從而實(shí)現(xiàn)屬性的鏈?zhǔn)讲僮鳌?/p>
var pipe = (function () {
? ? return function (value) {
? ? ? ? var funcStack = [];
? ? ? ?var oproxy = new Proxy({} , {
? ? ? ? ? ? get : function (pipeObject, fnName) {
? ? ? ? ? ? ? ? if (fnName === 'get') {
? ? ? ? ? ? ? ? ? ?return funcStack.reduce(function (val, fn) {
? ? ? ? ? ? ? ? ? ? ? ? return fn(val);
? ? ? ? ? ? ? ? ? ?},value);
? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? funcStack.push(window[fnName]);
? ? ? ? ? ? ? ? return oproxy;
? ? ? ? ? }
? ? ? ?});
? ? ? ? return oproxy;
? ? ? }
? ? }());
var double = n => n * 2;
var pow? ? = n => n * n;
var reverseInt = n => n.toString().split("").reverse().join("") | 0;
pipe(3).double.pow.reverseInt.get; // 63
該例子用到了一個(gè)沒(méi)見(jiàn)過(guò)的方法也就是reduce();通過(guò)百度知道了 這是Array 數(shù)組里面的一個(gè)方法;
reduce()對(duì)數(shù)組中的所有元素調(diào)用指定的回調(diào)函數(shù)。該回調(diào)函數(shù)的返回值為累積結(jié)果,并且此返回值在下一次調(diào)用該回調(diào)函數(shù)時(shí)作為參數(shù)提供。

也就是說(shuō)reduce方法的第一個(gè)參數(shù)是回調(diào)函數(shù),函數(shù)格式如下

而回調(diào)函數(shù)的第一個(gè)參數(shù)為上次回調(diào)的值,如果首次調(diào)用該函數(shù),則參數(shù)的值為initalValue.
所以
funcStack.reduce(function (val, fn) {
return fn(val);
},value);
該行代碼的執(zhí)行結(jié)果為:?
? 第一次調(diào)用? val = value = 3? ; ?fn =? n => n * 2 ?; ? return 值 : 3*2 = 6;
? 第二次調(diào)用? val = 6 ; value = 3 ?; ?fn =? n => n * n ;? return 值 : 6*6 = 36;
? 第三次調(diào)用? val = 36 ; value = 3? ;? fn =? n =>n.toString().split("").reverse().join("") | 0 ?;??
? ? ? ? ? ? ? ? ? ? ? ?return 值 :把36變成字符串 然后分割 反轉(zhuǎn) 變成 '63' ,在轉(zhuǎn)換成 數(shù)字 ?63;
因此,最后的執(zhí)行結(jié)果為 63 .
set()
set方法用來(lái)攔截某個(gè)屬性的賦值操作。
假定Person對(duì)象有一個(gè)age屬性,該屬性應(yīng)該是一個(gè)不大于200的整數(shù),那么可以使用Proxy保證age的屬性值符合要求。
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');
? ? ? ? ? ?}
? ? ? ? }
? ? ? ? // 對(duì)于age以外的屬性,直接保存
? ? ? ? obj[prop] = value;
? ? ? }
? };
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 報(bào)錯(cuò)
person.age = 300 // 報(bào)錯(cuò)
apply()
apply方法攔截函數(shù)的調(diào)用、call和apply操作。
apply方法可以接受三個(gè)參數(shù),分別是目標(biāo)對(duì)象、目標(biāo)對(duì)象的上下文對(duì)象(this)和目標(biāo)對(duì)象的參數(shù)數(shù)組。
var handler = {
? ? apply (target, ctx, args) {
? ? ? ? return Reflect.apply(...arguments);
? ? }
};
下面是一個(gè)例子。
var target = function () { ?return 'I am the target'; ?};
var handler = {
? ? apply: function () {
? ? ? ? return 'I am the proxy';
? ? }
};
var p = new Proxy(target, handler);
p()
// "I am the proxy"
上面代碼中,變量p是 Proxy 的實(shí)例,當(dāng)它作為函數(shù)調(diào)用時(shí)(p()),就會(huì)被apply方法攔截,返回一個(gè)字符串。
下面是另外一個(gè)例子。
var twice = {
? ? apply (target, ctx, args) {
? ? ? ? return Reflect.apply(...arguments) * 2;
? ? }
};
function sum (left, right) {
? ? return left + right;
};
var proxy = new Proxy(sum, twice);
proxy(1, 2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30
上面代碼中,每當(dāng)執(zhí)行proxy函數(shù)(直接調(diào)用或call和apply調(diào)用),就會(huì)被apply方法攔截。
另外,直接調(diào)用Reflect.apply方法,也會(huì)被攔截。
Reflect.apply(proxy,null,[9,10])// 38
has()
has方法用來(lái)攔截HasProperty操作,即判斷對(duì)象是否具有某個(gè)屬性時(shí),這個(gè)方法會(huì)生效。典型的操作就是in運(yùn)算符。
下面的例子使用has方法隱藏某些屬性,不被in運(yùn)算符發(fā)現(xiàn)。
var handler = {
? ? has (target, key) {
? ? ? ? if (key[0] === '_') {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ?return key in target;
? ? }
};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler);
'_prop' in proxy // false
上面代碼中,如果原對(duì)象的屬性名的第一個(gè)字符是下劃線,proxy.has就會(huì)返回false,從而不會(huì)被in運(yùn)算符發(fā)現(xiàn)。
如果原對(duì)象不可配置或者禁止擴(kuò)展,這時(shí)has攔截會(huì)報(bào)錯(cuò)。
var obj = { a: 10 };
Object.preventExtensions(obj);
var p = new Proxy(obj, {
? ? has: function(target, prop) {
? ? ? ? return false;
? ? }
});
'a' in p // TypeError is thrown
上面代碼中,obj對(duì)象禁止擴(kuò)展,結(jié)果使用has攔截就會(huì)報(bào)錯(cuò)。也就是說(shuō),如果某個(gè)屬性不可配置(或者目標(biāo)對(duì)象不可擴(kuò)展),則has方法就不得“隱藏”(即返回false)目標(biāo)對(duì)象的該屬性。
值得注意的是,has方法攔截的是HasProperty操作,而不是HasOwnProperty操作,即has方法不判斷一個(gè)屬性是對(duì)象自身的屬性,還是繼承的屬性。
另外,雖然for...in循環(huán)也用到了in運(yùn)算符,但是has攔截對(duì)for...in循環(huán)不生效。
let stu1 = {name: '張三', score: 59};
let stu2 = {name: '李四', score: 99};
let handler = {
? ? has(target, prop) {
? ? ? ? if (prop === 'score' && target[prop] < 60) {
? ? ? ? ? ? console.log(`${target.name} 不及格`);
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? return prop in target;
? ? }
};
let oproxy1 = new Proxy(stu1, handler);
let oproxy2 = new Proxy(stu2, handler);
'score' in oproxy1
// 張三 不及格
// false
'score' in oproxy2
// true
for (let a in oproxy1) {
console.log(oproxy1[a]);
}
// 張三
// 59
for (let b in oproxy2) {
console.log(oproxy2[b]);
}
// 李四
// 99
上面代碼中,has攔截只對(duì)in循環(huán)生效,對(duì)for...in循環(huán)不生效,導(dǎo)致不符合要求的屬性沒(méi)有被排除在for...in循環(huán)之外。
construct()
construct方法用于攔截new命令,下面是攔截對(duì)象的寫(xiě)法。
var handler = {
? ? construct (target, args, newTarget) {
? ? ? ? return new target(...args);
? ? }
};
construct方法返回的必須是一個(gè)對(duì)象,否則會(huì)報(bào)錯(cuò)。
construct方法可以接受兩個(gè)參數(shù)。
? ? --target: 目標(biāo)對(duì)象
? ? --args:構(gòu)建函數(shù)的參數(shù)對(duì)象
var p = new Proxy(function () {}, {
? ? construct: function(target, args) {
? ? ? ? console.log('called: ' + args.join(', '));
? ? ? ? return { value: args[0] * 10 };
? ? }
});
(new p(1)).value
// "called: 1"
// 10
deleteProperty()
deleteProperty方法用于攔截delete操作,如果這個(gè)方法拋出錯(cuò)誤或者返回false,當(dāng)前屬性就無(wú)法被delete命令刪除。
var handler = {
? ? deleteProperty (target, key) {
? ? ? ? invariant(key, 'delete');
? ? ? ? return true;
? ? }
};
function invariant (key, action) {
? ? if (key[0] === '_') {
? ? ? ? throw new Error(`Invalid attempt to ${action} private "${key}" property`);
? ? }
}
var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: Invalid attempt to delete private "_prop" property
上面代碼中,deleteProperty方法攔截了delete操作符,刪除第一個(gè)字符為下劃線的屬性會(huì)報(bào)錯(cuò)。
注意,目標(biāo)對(duì)象自身的不可配置(configurable)的屬性,不能被deleteProperty方法刪除,否則報(bào)錯(cuò)。
defineProperty()
defineProperty方法攔截了Object.defineProperty操作。
var handler = {
? ? defineProperty (target, key, descriptor) {
? ? ? ? return false;
? ? }
};
var target = {};
var proxy = new Proxy(target, handler);
proxy.foo = 'bar'
// TypeError: proxy defineProperty handler returned false for property '"foo"'
上面代碼中,defineProperty方法返回false,導(dǎo)致添加新屬性會(huì)拋出錯(cuò)誤。
注意,如果目標(biāo)對(duì)象不可擴(kuò)展(extensible),則defineProperty不能增加目標(biāo)對(duì)象上不存在的屬性,否則會(huì)報(bào)錯(cuò)。另外,如果目標(biāo)對(duì)象的某個(gè)屬性不可寫(xiě)(writable)或不可配置(configurable),則defineProperty方法不得改變這兩個(gè)設(shè)置。
getOwnPropertyDescriptor()
getOwnPropertyDescriptor方法攔截Object.getOwnPropertyDescriptor,返回一個(gè)屬性描述對(duì)象或者undefined。
var handler = {
? ? getOwnPropertyDescriptor (target, key) {
? ? ? ? if (key[0] === '_') {
? ? ? ? return;
? ? ? ? }
? ? return Object.getOwnPropertyDescriptor(target, key);
? ?}
};
var target = { _foo: 'bar', baz: 'tar' };
var proxy = new Proxy(target, handler);
Object.getOwnPropertyDescriptor(proxy, 'wat')
// undefined
Object.getOwnPropertyDescriptor(proxy, '_foo')
// undefined
Object.getOwnPropertyDescriptor(proxy, 'baz')
// { value: 'tar', writable: true, enumerable: true, configurable: true }
上面代碼中,handler.getOwnPropertyDescriptor方法對(duì)于第一個(gè)字符為下劃線的屬性名會(huì)返回undefined。
getPrototypeOf()
getPrototypeOf方法主要用來(lái)攔截Object.getPrototypeOf()運(yùn)算符,以及其他一些操作。
? ? --Object.prototype.__proto__
? ? --Object.prototype.isPrototypeOf()
? ? --Object.getPrototypeOf()
? ? --Reflect.getPrototypeOf()
? ? --instanceof運(yùn)算符
var proto = {};
var p = new Proxy({}, {
? ? getPrototypeOf(target) {
? ? ? ? return proto;
? ? }
});
Object.getPrototypeOf(p) === proto // true
上面代碼中,getPrototypeOf方法攔截Object.getPrototypeOf(),返回proto對(duì)象。
注意,getPrototypeOf方法的返回值必須是對(duì)象或者null,否則報(bào)錯(cuò)。另外,如果目標(biāo)對(duì)象不可擴(kuò)展(extensible),getPrototypeOf方法必須返回目標(biāo)對(duì)象的原型對(duì)象。
isExtensible()?
isExtensible方法攔截Object.isExtensible操作。
var p = new Proxy({}, {
? ? isExtensible: function(target) {
? ? ? ? console.log("called");
? ? ? ? return true;
? ? }
});
Object.isExtensible(p)
// "called"
// true
上面代碼設(shè)置了isExtensible方法,在調(diào)用Object.isExtensible時(shí)會(huì)輸出called。
注意,該方法只能返回布爾值,否則返回值會(huì)被自動(dòng)轉(zhuǎn)為布爾值。
這個(gè)方法有一個(gè)強(qiáng)限制,它的返回值必須與目標(biāo)對(duì)象的isExtensible屬性保持一致,否則就會(huì)拋出錯(cuò)誤。
ownKeys()
ownKeys方法用來(lái)攔截以下操作。
? ? --Object.getOwnPropertyNames()
? ? --Object.getOwnPropertySymbols()
? ? --Object.keys()
注意,使用Object.keys方法時(shí),有三類屬性會(huì)被ownKeys方法自動(dòng)過(guò)濾,不會(huì)返回。
? ? --目標(biāo)對(duì)象上不存在的屬性
? ? --屬性名為 Symbol 值
? ? --不可遍歷(enumerable)的屬性
let target = {
? ? a: 1,
? ? b: 2,
? ? c: 3,
? ? [Symbol.for('secret')]: '4',
};
Object.defineProperty(target, 'key', {
? ? enumerable: false,
? ? configurable: true,
? ? writable: true,
? ? value: 'static'
});
let handler = {
? ? ownKeys(target) {
? ? ? ? return ['a', 'd', Symbol.for('secret'), 'key'];
? ? }
};
let proxy = new Proxy(target, handler);
Object.keys(proxy)
// ['a']
上面代碼中,ownKeys方法之中,顯式返回不存在的屬性(d)、Symbol 值(Symbol.for('secret'))、不可遍歷的屬性(key),結(jié)果都被自動(dòng)過(guò)濾掉。
preventExtensions()
preventExtensions方法攔截Object.preventExtensions()。該方法必須返回一個(gè)布爾值,否則會(huì)被自動(dòng)轉(zhuǎn)為布爾值。
這個(gè)方法有一個(gè)限制,只有目標(biāo)對(duì)象不可擴(kuò)展時(shí)(即Object.isExtensible(proxy)為false),proxy.preventExtensions才能返回true,否則會(huì)報(bào)錯(cuò)。
var p = new Proxy({}, {
? ? preventExtensions: function(target) {
? ? ? ? return true;
? ? }
});
Object.preventExtensions(p) // 報(bào)錯(cuò)
上面代碼中,proxy.preventExtensions方法返回true,但這時(shí)Object.isExtensible(proxy)會(huì)返回true,因此報(bào)錯(cuò)。
為了防止出現(xiàn)這個(gè)問(wèn)題,通常要在proxy.preventExtensions方法里面,調(diào)用一次Object.preventExtensions。
var p = new Proxy({}, {
? ? preventExtensions: function(target) {
? ? ? ? ?console.log('called');
? ? ? ? Object.preventExtensions(target);
? ? ? ? return true;
? ? }
});
Object.preventExtensions(p)
// "called"
// true
setPrototypeOf()
setPrototypeOf方法主要用來(lái)攔截Object.setPrototypeOf方法。
var handler = {
? ? setPrototypeOf (target, proto) {
? ? ? ? throw new Error('Changing the prototype is forbidden');
? ?}
};
var proto = {};
var target = function () {};
var proxy = new Proxy(target, handler);
Object.setPrototypeOf(proxy, proto);
// Error: Changing the prototype is forbidden
上面代碼中,只要修改target的原型對(duì)象,就會(huì)報(bào)錯(cuò)。
注意,該方法只能返回布爾值,否則會(huì)被自動(dòng)轉(zhuǎn)為布爾值。另外,如果目標(biāo)對(duì)象不可擴(kuò)展(extensible),setPrototypeOf方法不得改變目標(biāo)對(duì)象的原型。
Proxy.revocable()
Proxy.revocable方法返回一個(gè)可取消的 Proxy 實(shí)例。
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
Proxy.revocable方法返回一個(gè)對(duì)象,該對(duì)象的proxy屬性是Proxy實(shí)例,revoke屬性是一個(gè)函數(shù),可以取消Proxy實(shí)例。上面代碼中,當(dāng)執(zhí)行revoke函數(shù)之后,再訪問(wèn)Proxy實(shí)例,就會(huì)拋出一個(gè)錯(cuò)誤。
Proxy.revocable的一個(gè)使用場(chǎng)景是,目標(biāo)對(duì)象不允許直接訪問(wèn),必須通過(guò)代理訪問(wèn),一旦訪問(wèn)結(jié)束,就收回代理權(quán),不允許再次訪問(wèn)。
this 問(wèn)題
雖然 Proxy 可以代理針對(duì)目標(biāo)對(duì)象的訪問(wèn),但它不是目標(biāo)對(duì)象的透明代理,即不做任何攔截的情況下,也無(wú)法保證與目標(biāo)對(duì)象的行為一致。主要原因就是在 Proxy 代理的情況下,目標(biāo)對(duì)象內(nèi)部的this關(guān)鍵字會(huì)指向 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,后者內(nèi)部的this就是指向proxy,而不是target。
實(shí)例:Web 服務(wù)的客戶端
Proxy 對(duì)象可以攔截目標(biāo)對(duì)象的任意屬性,這使得它很合適用來(lái)寫(xiě) Web 服務(wù)的客戶端。
const service = createWebService ('http://example.com/data');
service.employees().then(json => {
? ? const employees = JSON.parse(json);
? ? // ···
});
上面代碼新建了一個(gè) Web 服務(wù)的接口,這個(gè)接口返回各種數(shù)據(jù)。Proxy 可以攔截這個(gè)對(duì)象的任意屬性,所以不用為每一種數(shù)據(jù)寫(xiě)一個(gè)適配方法,只要寫(xiě)一個(gè) Proxy 攔截就可以了。
function createWebService(baseUrl) {
? ? return new Proxy({}, {
? ? ? ? get(target, propKey, receiver) {
? ? ? ? ?return () => httpGet(baseUrl+'/' + propKey);
? ? ? ? }
? ? });
}
同理,Proxy 也可以用來(lái)實(shí)現(xiàn)數(shù)據(jù)庫(kù)的 ORM 層。