JavaScript
Proxy
Proxy 是 ES6 出的语法,字面理解是“拦截”,提供了一种机制,对目标对象进行拦截,提供了 13 种拦截方式,对外界的访问进行过滤与修改 尝鲜 案例 1 对设置拦截行为的对象 obj,去读写它的属性 案例 2 Proxy 如何使用 案例 3 目标对象是一个空对象,拦截函数是返回 35,所以对任何属性的访问都是 35 案例 4 如果 handler 没有设置任何拦截,等同于目标对象 案例 5 …
Proxy 是 ES6 出的语法,字面理解是“拦截”,提供了一种机制,对目标对象进行拦截,提供了 13 种拦截方式,对外界的访问进行过滤与修改 尝鲜 案例 1 对设置拦截行为的对象 obj,去读写它的属性 案例 2 Proxy 如何使用 案例 3 目标对象是一个空对象,拦截函数是返回 35,所以对任何属性的访问都是 35 案例 4 如果 handler 没有设置任何拦截,等同于目标对象 案例 5 …
Proxy 是 ES6 出的语法,字面理解是“拦截”,提供了一种机制,对目标对象进行拦截,提供了 13 种拦截方式,对外界的访问进行过滤与修改
对设置拦截行为的对象 obj,去读写它的属性
var obj = new Proxy(
{},
{
get: function(target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
}
);
obj.count = 1;
--obj.count;
// 输出
// setting count!
// getting count!
// setting count!
Proxy 如何使用
// target: (必需, Object) 目标对象
// handler: (必需, String) 定制拦截行为
var proxy = new Proxy(target, handler);
目标对象是一个空对象,拦截函数是返回 35,所以对任何属性的访问都是 35
var proxy1 = new Proxy(
{},
{
get: function(target, property) {
return 35;
}
}
);
console.log(proxy1.koa);
console.log(proxy1.express);
// 输出
// 35
// 35
如果 handler 没有设置任何拦截,等同于目标对象
var target = {};
var handler = {};
var proxy2 = new Proxy(target, handler);
proxy2.a = 'b';
console.log(proxy2.a);
// 输出
// b
将 Proxy 设置到对象属性上,就可以在对象属性上调用
var object = { proxy: new Proxy(target, handler) };
Proxy 实例可以作为其他对象的原型对象
var proxy3 = new Proxy(
{},
{
get: function(target, property) {
return 35;
}
}
);
let obj2 = Object.create(proxy3);
console.log(proxy3.name);
// 输出
// 35
同一个拦截器函数,可以设置多个拦截操作
var handler2 = {
get: function(target, name) {
if (name === 'prototype') {
return Object.prototype;
}
return 'Hello, ' + name;
},
set: function(target, name, value) {
return value;
},
apply: function(target, thisBinding, args) {
console.log(thisBinding);
return args[0];
},
construct: function(target, args) {
return {
value: args[1]
};
}
};
var fproxy = new Proxy(function(x, y) {
return x + y;
}, handler2);
console.log(fproxy(1, 2));
console.log(new fproxy(1, 2));
console.log(fproxy.prototype === Object.prototype);
console.log(fproxy.foo === 'Hello, foo');
console.log((fproxy.koa = 77));
// 输出
// 1
// { value: 2 }
// true
// true
// 77
// 拦截对象属性的读取,比如proxy.foo和proxy['foo'] target是目标对象,propKey是对象属性,receiver是Proxy实例
get(target, propKey, receiver)
// 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值,target是目标对象,propKey是对象属性,value是设置的值,receiver是Proxy实例
set(target, propKey, value, receiver)
// 拦截propKey in proxy的操作,返回一个布尔值。target是目标对象,propKey是对象属性
has(target, propKey)
// 拦截delete proxy[propKey]的操作,返回一个布尔值。target是目标对象,propKey是对象属性
deleteProperty(target, propKey)
// 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性,target是目标对象
ownKeys(target)
// 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。target是目标对象,propKey是对象属性
getOwnPropertyDescriptor(target, propKey)
// 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。target是目标对象,propKey是对象属性,propDesc是描述符
defineProperty(target, propKey, propDesc)
// 拦截Object.preventExtensions(proxy),返回一个布尔值。target是目标对象
preventExtensions(target)
// 拦截Object.getPrototypeOf(proxy),返回一个对象。target是目标对象
getPrototypeOf(target)
// 拦截Object.isExtensible(proxy),返回一个布尔值。target是目标对象
isExtensible(target)
// 拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。target是目标对象
setPrototypeOf(target, proto)
// 拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。target是目标对象, object目标对象上下文(this), args是传入的参数
apply(target, object, args)
// 拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。target是目标对象,args是传入的参数,newTarget创造实例对象时,new命令作用的构造函数
construct(target, args,newTarget)
get()拦截方法
访问目标对象不存在,报错,如果没有拦截函数,就会返回 undefined
var person = {
name: '小明'
};
var proxy4 = new Proxy(person, {
get: function(target, property) {
if (property in target) {
return target[property];
} else {
throw new ReferenceError('Property "' + property + '" does not exist.');
}
}
});
console.log(proxy4.name);
// console.log(proxy4.age)
// 输出
// 小明
// Property "age" does not exist.
get()拦截方法是可以继承的
let proto = new Proxy(
{},
{
get(target, propertyKey, receiver) {
console.log('GET ' + propertyKey);
return target[propertyKey];
}
}
);
let obj3 = Object.create(proto);
obj3.foo;
// 输出
// GET foo
实现数组读取负数的索引
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');
console.log(arr[-1]);
// 输出
// c
设置 Proxy 以后,达到了将函数名链式使用的效果
var double = (n) => n * 2;
var pow = (n) => n * n;
var reverseInt = (n) =>
n
.toString()
.split('')
.reverse()
.join('') | 0;
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;
};
})();
console.log(pipe(3).double.pow.reverseInt.get);
// 输出->3*2->6*6->63
// 63
生成各种 DOM 节点的通用函数 dom
const dom = new Proxy(
{},
{
get(target, property) {
return function(attrs = {}, ...children) {
const el = document.createElement(property);
for (let prop of Object.keys(attrs)) {
el.setAttribute(prop, attrs[prop]);
}
for (let child of children) {
if (typeof child === 'string') {
child = document.createTextNode(child);
}
el.appendChild(child);
}
return el;
};
}
}
);
const el = dom.div({}, 'Hello, my name is ', dom.a({}, 'name'), '.I love:', dom.ul({}, dom.li({}, 'study')));
document.body.appendChild(el);
// 输出
// 往body中按照代码层级插入dom
get()拦截方法的第三个参数是 Proxy 实例
const proxy5 = new Proxy(
{},
{
get: function(target, property, receiver) {
return receiver;
}
}
);
console.log(proxy5.getReceiver === proxy5);
// 输出
// true
d 对象本身没有 a 属性,读取 d.a 的时候,会去 d 的原型 proxy 去读取,receiver 指向 d,可以看出 receiver 代表原始读取操作所在的那个对象
const proxy6 = new Proxy(
{},
{
get: function(target, property, receiver) {
return receiver;
}
}
);
const d = Object.create(proxy6);
console.log(d.a === d);
// 输出
// true
如果对象的 configurable 为 false,则 Proxy 不能修改该属性
const target7 = Object.defineProperties(
{},
{
foo: {
value: 123,
writable: false,
configurable: false
}
}
);
const handler7 = {
get(target, propKey) {
return 'abc';
}
};
const proxy7 = new Proxy(target7, handler7);
// console.log(proxy7.foo)
// 输出
// Proxy.js:277 Uncaught TypeError: 'get' on proxy: property 'foo' is a read-only and non-configurable data property on the proxy target
set()拦截方法
set 是拦截某个属性的赋值 操作,下例在赋值的时候拦截了大于 200 或者非数组的情况并报错
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 person1 = new Proxy({}, validator);
person1.age = 100;
console.log(person1.age);
// person1.age = 'young'
// person1.age = 300
// 输出
// 100
// 报错
// 报错
只要读写的属性名的第一个字符是下划线,一律抛错,从而达到禁止读写内部属性的目的
const handler3 = {
get(target, key) {
invariant(key, 'get');
return target[key];
},
set(target, key, value) {
invariant(key, 'set');
target[key] = value;
return true;
}
};
function invariant(key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
}
const target3 = {};
const proxy = new Proxy(target3, handler3);
proxy.prop = 1;
console.log(proxy.prop);
// proxy._prop = 1;
// 输出
// 1
// 报错
receiver 指的是原始的操作行为所在的那个对象,一般情况下是 proxy 实例本身
const handler4 = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
}
};
const proxy8 = new Proxy({}, handler4);
proxy8.foo = 'bar';
console.log(proxy8.foo === proxy8);
// 输出
// true
设置 myObj 属性的值的时候,myObj 并没有 foo 属性,会到原型链上去找 foo 属性,myObj 的原型是 proxy9 proxy9 是一个 Proxy,设置 foo 触发 set 拦截方法,把所有属性都指向 receiver,receiver 就是原始原始赋值行为所在的对象 myObj
const handler5 = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
}
};
const proxy9 = new Proxy({}, handler5);
const myObj = {};
Object.setPrototypeOf(myObj, proxy9);
myObj.foo = 'bar';
console.log(myObj.foo === myObj);
// 输出
// true
目标对象自身的某个属性,不可写且不可配置,那么 set 方法将不起作用
const obj4 = {};
Object.defineProperty(obj4, 'foo', {
value: 'bar',
writable: false
});
const handler6 = {
set: function(obj, prop, value, receiver) {
obj[prop] = 'baz';
}
};
const proxy10 = new Proxy(obj4, handler6);
proxy10.foo = 'baz';
console.log(proxy10.foo);
// 输出
// baz
严格模式下,set 代理如果没有返回 true,就会报错
// 'use strict';
const handler8 = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
// 无论有没有下面这一行,都会报错
return false;
}
};
const proxy11 = new Proxy({}, handler8);
proxy11.foo = 'bar123';
// 输出
// TypeError: 'set' on proxy: trap returned falsish for property 'foo'
apply()拦截方法
Proxy 的实例作为函数调用时,就会被 apply()方法拦截
var target1 = function() {
return 'I am the target';
};
var handler9 = {
apply: function() {
return 'I am the proxy';
}
};
var p = new Proxy(target1, handler9);
console.log(p());
// 输出
// I am the proxy
每当执行 proxy 函数(直接调用或 call 和 apply 调用),就会被 apply 方法拦截
ar twice = {
apply(target, ctx, args) {
// 等同于Function.prototype.apply.call(...arguments) *2
return Reflect.apply(...arguments) * 2;
}
};
function sum(left, right) {
return left + right;
};
var proxy12 = new Proxy(sum, twice);
console.log(proxy12(1, 2))
console.log(proxy12.call(null, 5, 6))
console.log(proxy12.apply(null, [7, 8]))
console.log(Reflect.apply(proxy12, null, [9, 10]))
// 输出
// (1+2)*2=6
// (5+6)*2=22
// (7+8)*2=30
// (9+10)*2=38
has()拦截方法
has 方法拦截的是 HasProperty 操作,而不是 HasOwnProperty 操作,即 has 方法不判断一个属性是对象自身的属性,还是继承的属性
原对象的属性名的第一个字符是下划线,proxy.has 就会返回 false,从而不会被 in 运算符发现
var handler10 = {
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
};
var target2 = {
_prop: 'foo',
prop: 'foo'
};
var proxy13 = new Proxy(target2, handler10);
console.log('_prop' in proxy13);
// 输出
// false
原对象不可配置或者禁止扩展,这时 has 拦截会报错
var obj = {
a: 10
};
Object.preventExtensions(obj);
var p = new Proxy(obj, {
has: function(target, prop) {
return false;
}
});
// console.log('a' in p)
// 输出
// 报错
has 拦截只对 in 运算符生效,虽然 for...in 循环也用到了 in 运算符,但是 has 拦截对 for...in 循环不生效
let stu1 = {
name: '张三',
score: 59
};
let stu2 = {
name: '李四',
score: 99
};
let handler11 = {
has(target, prop) {
if (prop === 'score' && target[prop] < 60) {
console.log(`${target.name} 不及格`);
return false;
}
return prop in target;
}
};
let oproxy1 = new Proxy(stu1, handler11);
let oproxy2 = new Proxy(stu2, handler11);
console.log('score' in oproxy1);
console.log('score' in oproxy2);
for (let a in oproxy1) {
console.log(oproxy1[a]);
}
for (let b in oproxy2) {
console.log(oproxy2[b]);
}
// 输出
// 第一行输出
// 张三 不及格
// false
// 第二行输出
// true
// 第三行输出
// 张三
// 59
// 第四行输出
// 李四
// 99
construct()拦截方法
construct 方法用于拦截 new 命令
var p = new Proxy(function() {}, {
construct: function(target, args, newTarget) {
console.log('called: ' + args.join(', '));
return {
value: args[0] * 10,
target: newTarget
};
}
});
console.log(new p(1).value);
console.log(new p(1).target);
// 输出
// called: 1
// 10
// ƒ () { [native code] }
construct 方法返回的必须是一个对象,否则会报错
var p = new Proxy(function() {}, {
construct: function(target, argumentsList) {
return 1;
}
});
// new p()
// 输出
// 报错
deleteProperty()拦截方法
deleteProperty 方法用于拦截 delete 操作,如果这个方法抛出错误或者返回 false,当前属性就无法被 delete 命令删除。
var handler12 = {
deleteProperty(target, key) {
invariant(key, 'delete');
delete target[key];
return true;
}
};
function invariant(key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
}
var target4 = {
_prop: 'foo'
};
var proxy14 = new Proxy(target4, handler12);
// console.log(delete proxy14._prop)
// 输出
// 报错
defineProperty()拦截方法
defineProperty 方法拦截了 Object.defineProperty 操作。
如果目标对象不可扩展(non-extensible),则 defineProperty 不能增加目标对象上不存在的属性,否则会报错。另外,如果目标对象的某个属性不可写(writable)或不可配置(configurable),则 defineProperty 方法不得改变这两个设置。
defineProperty 方法返回 false,导致添加新属性总是无效
var handler13 = {
defineProperty(target, key, descriptor) {
return true;
}
};
var target5 = {};
var proxy15 = new Proxy(target5, handler13);
proxy15.foo = 'bar';
console.log(proxy15.foo);
// 输出
// undefined
getOwnPropertyDescriptor()拦截方法
getOwnPropertyDescriptor 方法拦截 Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者 undefined。
var handler14 = {
getOwnPropertyDescriptor(target, key) {
if (key[0] === '_') {
return;
}
return Object.getOwnPropertyDescriptor(target, key);
}
};
var target6 = {
_foo: 'bar',
baz: 'tar'
};
var proxy16 = new Proxy(target6, handler14);
console.log(Object.getOwnPropertyDescriptor(proxy16, 'wat'));
console.log(Object.getOwnPropertyDescriptor(proxy16, '_foo'));
console.log(Object.getOwnPropertyDescriptor(proxy16, 'baz'));
// 输出
// undefined
// undefined
// {value: "tar", writable: true, enumerable: true, configurable: true}
getPrototypeOf()拦截方法
getPrototypeOf 方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作
注意,getPrototypeOf 方法的返回值必须是对象或者 null,否则报错。另外,如果目标对象不可扩展(non-extensible), getPrototypeOf 方法必须返回目标对象的原型对象。
var proto1 = {};
var p = new Proxy(
{},
{
getPrototypeOf(target) {
return proto1;
}
}
);
console.log(Object.getPrototypeOf(p) === proto1);
// 输出
// true
isExtensible()拦截方法
该方法只能返回布尔值,否则返回值会被自动转为布尔值
isExtensible 方法拦截 Object.isExtensible 操作
var p = new Proxy(
{},
{
isExtensible: function(target) {
console.log('called');
return true;
}
}
);
Object.isExtensible(p);
console.log(Object.isExtensible(proxy) === Object.isExtensible(target));
// 输出
// called
var p = new Proxy(
{},
{
isExtensible: function(target) {
return false;
}
}
);
// Object.isExtensible(p)
// 输出
// 报错
ownKeys 拦截方法
ownKeys 方法用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。
let target8 = {
a: 1,
b: 2,
c: 3
};
let handler15 = {
ownKeys(target) {
return ['a'];
}
};
let proxy17 = new Proxy(target8, handler15);
console.log(Object.keys(proxy17));
// 输出
// ["a"]
过滤下划线属性
let target9 = {
_bar: 'foo',
_prop: 'bar',
prop: 'baz'
};
let handler16 = {
ownKeys(target) {
return Reflect.ownKeys(target).filter((key) => key[0] !== '_');
}
};
let proxy18 = new Proxy(target9, handler16);
for (let key of Object.keys(proxy18)) {
console.log(target9[key]);
}
// 输出
// baz
使用 Object.keys 方法时,有三类属性会被 ownKeys 方法自动过滤,不会返回
let target10 = {
a: 1,
b: 2,
c: 3,
[Symbol.for('secret')]: '4'
};
Object.defineProperty(target10, 'key', {
enumerable: false,
configurable: true,
writable: true,
value: 'static'
});
let handler17 = {
ownKeys(target) {
return ['a', 'd', Symbol.for('secret'), 'key'];
}
};
let proxy19 = new Proxy(target10, handler17);
console.log(Object.keys(proxy19));
// 输出
// [a]
ownKeys 方法还可以拦截 Object.getOwnPropertyNames()
var p2 = new Proxy(
{},
{
ownKeys: function(target) {
return ['a', 'b', 'c'];
}
}
);
console.log(Object.getOwnPropertyNames(p2));
// 输出
// [ 'a', 'b', 'c' ]
for...in 循环也受到 ownKeys 方法的拦截
const obj5 = {
hello: 'world',
name: 'koa'
};
const proxy20 = new Proxy(obj5, {
ownKeys: function() {
return ['a', 'name'];
}
});
for (let key in proxy20) {
console.log(key);
}
// 输出
// name
ownKeys 方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。
var obj = {};
var p = new Proxy(obj, {
ownKeys: function(target) {
return [123, true, undefined, null, {}, []];
}
});
// Object.getOwnPropertyNames(p)
// 输出
// 报错
如果目标对象自身包含不可配置的属性,则该属性必须被 ownKeys 方法返回,否则报错。
var obj = {};
Object.defineProperty(obj, 'a', {
configurable: false,
enumerable: true,
value: 10
});
var p = new Proxy(obj, {
ownKeys: function(target) {
return ['b'];
}
});
// for (let key in p) {
// console.log(key);
// }
// 输出
// 报错
如果目标对象是不可扩展的(non-extensible),这时 ownKeys 方法返回的数组之中,必须包含原对象的所有属性,且不能包含多余的属性,否则报错。
var obj = {
a: 1
};
Object.preventExtensions(obj);
var p = new Proxy(obj, {
ownKeys: function(target) {
return ['a', 'vc'];
}
});
// console.log(Object.getOwnPropertyNames(p))
// 输出
// 报错
preventExtensions()拦截方法
preventExtensions 方法拦截 Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值。
只有目标对象不可扩展,proxy.preventExtensions 才能返回 true
var proxy21 = new Proxy(
{},
{
preventExtensions: function(target) {
return true;
}
}
);
// Object.preventExtensions(proxy21)
// 输出
// 报错
通常要在 proxy.preventExtensions 方法里面,调用一次 Object.preventExtensions
var proxy22 = new Proxy(
{},
{
preventExtensions: function(target) {
console.log('called');
Object.preventExtensions(target);
return true;
}
}
);
console.log(Object.preventExtensions(proxy22));
// 输出
// called
// Proxy {}
setPrototypeOf()拦截方法
setPrototypeOf 方法主要用来拦截 Object.setPrototypeOf 方法。
该方法只能返回布尔值,否则会被自动转为布尔值。另外,如果目标对象不可扩展(non-extensible),setPrototypeOf 方法不得改变目标对象的原型。
只要修改原型对象就报错
var handler18 = {
setPrototypeOf(target, proto) {
throw new Error('Changing the prototype is forbidden');
}
};
var proto3 = {};
var target11 = function() {};
var proxy23 = new Proxy(target11, handler18);
// Object.setPrototypeOf(proxy23, proto3);
// 输出
// 报错
Proxy.revocable()
Proxy.revocable()方法返回一个可取消的 Proxy
proxy 属性是 Proxy 实例,revoke 属性是一个函数,可以取消 Proxy,执行 revoke 后,再访问 Proxy 实例就会报错
let target12 = {};
let handler19 = {};
let { proxy24, revoke } = Proxy.revocable(target12, handler19);
// proxy24.foo = 123;
// console.log(proxy24.foo)
// revoke();
// proxy24.foo
// 输出
// 123
// 报错
this 问题
在不做任何拦截的情况下,this 指向 Proxy 代理
const target13 = {
m: function() {
console.log(this === proxy25);
}
};
const handler20 = {};
const proxy25 = new Proxy(target13, handler20);
target13.m();
proxy25.m();
// 输出
// false
// true
没 Proxy 代理前,可以访问到 name 属性,代理 jane 后,并且没有任何拦截,导致 this 指向变化, 不指向构造函数,而是指向 Proxy,Proxy 没有 name 属性,就返回 undefined
const _name = new WeakMap();
class Person {
constructor(name) {
_name.set(this, name);
}
get name() {
return _name.get(this);
}
}
const jane = new Person('Jane');
console.log(jane.name);
const proxy26 = new Proxy(jane, {});
console.log(proxy26.name);
// 输出
// jane
// undefined
有些原生属性,Proxy 是没办法代理这些原生对象的属性
const target14 = new Date();
const handler21 = {};
const proxy27 = new Proxy(target14, handler21);
// proxy27.getDate();
// 输出
// 报错
为了解决上述没办法代理原生对象的属性问题,在获取属性的拦截方法中,重新改变 this 指向,就可以解决这个问题
const target15 = new Date('2015-01-01');
const handler22 = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);
}
return Reflect.get(target, prop);
}
};
const proxy28 = new Proxy(target15, handler22);
console.log(proxy28.getDate());
// 输出
// 1
web 服务端问题
Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论