MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
1
| const p = new Proxy(target, handler)
|
target
:要使用Proxy
包装的目标对象(可以是任何类型的对象,包括原生数组、函数、甚至另一个代理)。
handler
:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理p
的行为。
方法
创建一个可撤销的Proxy
对象。
handler对象的方法
handler
对象是一个容纳一批特定属性的占位符对象。它包含有Proxy
的各个捕获器(trap)。
所有的捕获器是可选的。如果没有定义某个捕获器,那么就会保留源对象的默认行为。
getPrototypeOf()
handler.getPrototypeOf()
是一个代理(Proxy)方法,当读取代理对象的原型时,该方法就会被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const monster1 = { eyeCount: 4 };
const monsterPrototype = { eyeCount: 2 };
const handler = { getPrototypeOf(target) { return monsterPrototype; } };
const proxy1 = new Proxy(monster1, handler);
console.log(Object.getPrototypeOf(proxy1) === monsterPrototype);
console.log(Object.getPrototypeOf(proxy1).eyeCount);
|
setPrototypeOf()
handler.setPrototypeOf()
方法主要用来拦截 Object.setPrototypeOf()
.
1 2 3 4
| const p = new Proxy(target, { setPrototypeOf: function(target, prototype) { } });
|
isExtensible()
handler.isExtensible() 方法用于拦截对对象的 Object.isExtensible()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const monster1 = { canEvolve: true };
const handler1 = { isExtensible(target) { return Reflect.isExtensible(target); }, preventExtensions(target) { target.canEvolve = false; return Reflect.preventExtensions(target); } };
const proxy1 = new Proxy(monster1, handler1);
console.log(Object.isExtensible(proxy1));
console.log(monster1.canEvolve);
Object.preventExtensions(proxy1);
console.log(Object.isExtensible(proxy1));
console.log(monster1.canEvolve);
|
preventExtensions()
handler.preventExtensions()
方法用于设置对Object.preventExtensions()
的拦截。
Object.preventExtensions()
方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
1 2 3 4
| var p = new Proxy(target, { preventExtensions: function(target) { } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const monster1 = { canEvolve: true };
const handler1 = { preventExtensions(target) { target.canEvolve = false; Object.preventExtensions(target); return true; } };
const proxy1 = new Proxy(monster1, handler1);
console.log(monster1.canEvolve);
Object.preventExtensions(proxy1);
console.log(monster1.canEvolve);
|
getOwnPropertyDescriptor()
handler.getOwnPropertyDescriptor()
方法是 Object.getOwnPropertyDescriptor()
的钩子。
1 2 3 4
| var p = new Proxy(target, { getOwnPropertyDescriptor: function(target, prop) { } });
|
1 2 3 4 5 6 7 8 9 10
| const p = new Proxy({ a: 20 }, { getOwnPropertyDescriptor(target, prop) { console.log('called: ' + prop) return { configurable: true, enumerable: true, value: 10 } } })
console.log(Object.getOwnPropertyDescriptor(p, 'a').value)
|
defineProperty()
handler.defineProperty()
用于拦截对象的 Object.defineProperty()
操作。
vue2的双向绑定就是通过 Object.defineProperty()
实现的。
1 2 3 4
| var p = new Proxy(target, { defineProperty: function(target, property, descriptor) { } });
|
has()
handler.has()
方法是针对 in
操作符的代理方法。
示例,_
开头的属性为私有属性,使用in
判断的时候返回false
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const handler1 = { has(target, key) { if (key[0] === '_') { return false; } return key in target; } };
const monster1 = { _secret: 'easily scared', eyeCount: 4 };
const proxy1 = new Proxy(monster1, handler1);
console.log('eyeCount' in proxy1);
console.log('_secret' in proxy1);
console.log('_secret' in monster1);
|
get()
handler.get()
方法用于拦截对象的读取属性操作。
1 2 3 4
| var p = new Proxy(target, { get: function(target, property, receiver) { } });
|
1 2 3 4 5 6 7
| const p = new Proxy({ a: 10 }, { get(target, prop, receiver) { return target[prop] * 2 } })
console.log(p.a)
|
set()
handler.set()
方法是设置属性值操作的捕获器。
1 2 3 4
| const p = new Proxy(target, { set: function(target, property, value, receiver) { } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const monster1 = { eyeCount: 4 };
const handler1 = { set(obj, prop, value) { if ((prop === 'eyeCount') && ((value % 2) !== 0)) { console.log('必须是偶数'); } else { return Reflect.set(...arguments); } } };
const proxy1 = new Proxy(monster1, handler1);
proxy1.eyeCount = 1;
console.log(proxy1.eyeCount);
proxy1.eyeCount = 2; console.log(proxy1.eyeCount);
|
deleteProperty()
handler.deleteProperty()
方法用于拦截对对象属性的 delete
操作。
1 2 3 4
| var p = new Proxy(target, { deleteProperty: function(target, property) { } });
|
ownKeys()
handler.ownKeys()
方法用于拦截 Reflect.ownKeys()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const monster1 = { _age: 111, [Symbol('secret')]: 'I am scared!', eyeCount: 4 }
const proxy1 = new Proxy(monster1, { ownKeys(target) { return Reflect.ownKeys(target) } })
for (const key of Object.keys(proxy1)) { console.log(key) }
|
apply()
handler.apply()
方法用于拦截函数的调用。
1 2 3 4 5 6 7 8 9 10
| const sum = (a, b) => a + b const handler = { apply: (target, _, args) => { return target(...args) * 10 },
} const proxy = new Proxy(sum, handler) console.log(sum(1, 2)) console.log(proxy(1, 2))
|
construct()
handler.construct()
方法用于拦截 new
操作符。为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target
必须是有效的)。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function sum(a, b) { return { a, b } } const handler = { construct: (target, args) => { for (let index = 0; index < args.length; index++) { args[index] += 1 } return new target(...args) } } const proxy = new Proxy(sum, handler) console.log(new proxy(1, 2))
|
上例中,sum不能是只能是普通函数,不能是箭头函数,因为箭头函数不能new。