一、概述
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
var proxy = new Proxy(target, handler);
new Proxy()
表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
二、方法
1.get()
get
方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(可选)。get
方法可继承,如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get(target, propKey) {
if (propKey in target) {
return target[propKey];
} else {
throw new ReferenceError("Prop name \"" + propKey + "\" does not exist.");
}
}
});
proxy.name // "张三"
proxy.age // 抛出一个错误
2.set()
set
方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身(可选)。
3.apply()
apply
方法拦截函数的调用、call和apply操作,接受三个参数,分别是目标对象、目标对象的上下文对象(this
)和目标对象的参数数组
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"
4.has()
has()
方法用来拦截HasProperty
操作,即判断对象是否具有某个属性时,这个方法会生效,该方法可以接受两个参数,分别是目标对象、需查询的属性名。
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
5.construct()
construct()
方法用于拦截new
命令,接受三个参数,分别是目标对象、构造函数的参数数组和创造实例时new作用的构造函数。该方法必须返回一个对象,其中this
指向Proxy中的handler
。
const 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
6.deleteProperty()
该方法用于拦截delete
操作
var handler = {
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 target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: Invalid attempt to delete private "_prop" property
7.defineProperty()
拦截了Object.defineProperty()
操作
var handler = {
defineProperty (target, key, descriptor) {
return false;
}
};
var target = {};
var proxy = new Proxy(target, handler);
proxy.foo = 'bar' // 不会生效
proxy.foo //undefined
8.getOwnPropertyDescriptor()
拦截Object.getOwnPropertyDescriptor()
,返回一个属性描述对象或者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, '_foo')
// undefined
Object.getOwnPropertyDescriptor(proxy, 'baz')
// { value: 'tar', writable: true, enumerable: true, configurable: true }
9.getPrototypeOf()
主要用来拦截获取对象原型,具体是以下操作:
Object.prototype.__proto__
Object.prototype.isPrototypeOf()
Object.getPrototypeOf()
Reflect.getPrototypeOf()
instanceof
var proto = {};
var p = new Proxy({}, {
getPrototypeOf(target) {
return proto;
}
});
Object.getPrototypeOf(p) === proto // true
10.isExtensible()
拦截Object.isExtensible()
操作,该方法只能返回布尔值,否则返回值会被自动转为布尔值
var p = new Proxy({}, {
isExtensible: function(target) {
console.log("called");
return true;
}
});
Object.isExtensible(p)
// "called"
// true
11.ownKeys()
拦截对象自身属性的读取操作,ownKeys()
方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错,具体拦截的是以下操作:
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
for...in
循环
let target = {
_bar: 'foo',
_prop: 'bar',
prop: 'baz'
};
let handler = {
ownKeys (target) {
return Reflect.ownKeys(target).filter(key => key[0] !== '_');
}
};
let proxy = new Proxy(target, handler);
for (let key of Object.keys(proxy)) {
console.log(target[key]);
}
// "baz"
使用Object.keys()
方法时,有三类属性会被ownKeys()
方法自动过滤:目标对象上不存在的属性、属性名为 Symbol 值、不可遍历(enumerable)的属性
12.preventExtensions()
拦截Object.preventExtensions()
,该方法必须返回一个布尔值,否则会被自动转为布尔值。
13.setPrototypeOf()
拦截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
三、Proxy.revocable()
Proxy.revocable()
方法返回一个可取消的 Proxy 实例,一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
Proxy.revocable()
方法返回一个对象,该对象的proxy
属性是Proxy
实例,revoke
属性是一个函数,可以取消Proxy
实例
四、this
在 Proxy
代理的情况下,目标对象内部的this
会指向Proxy
代理,Proxy
拦截函数内部的this,指向的是handler
对象