1、初始化的几种方式
1.1、Proxy关联原始对象
let obj = {
name: 'sian'
}
/**
* new Proxy(target, handler);
* 第一个参数为原始对象
* 第二个参数为拦截行为
*/
let proxy = new Proxy(obj, {
get: function(target, prop) {
return 'xxx'
}
});
object.name // sian
proxy.name // xxx
1.2、通过继承的方式实现代理
即:Proxy做为其他对象的原型对象
let proxy = new Proxy({}, {
get: function(target, prop) {
return 'xxx'
}
})
let obj = Object.create(proxy);
// obj并没有time属性,根据原型链,最终会到proxy对象上读取
obj.name // xxx
let person = {
age: 10
}
Object.setPrototypeOf(person, proxy);
person.name // xxx
2、实例方法
2.1、get(target, propKey, receiver)
拦截对象属性的读取,比如proxy.foo
和proxy['foo']
- 数组负数索引实现倒序取值(如:-1取最后一个对象)
function CreateArray(...elements) {
let target = [];
let handler = {
get: function (target, propKey, receiver) {
let index = Number(propKey);
if (index < 0) {
index = target.length + index;
}
return Reflect.get(target, String(index), receiver);
}
}
target.push(...elements);
let proxy = new Proxy(target, handler);
return proxy;
}
let array = CreateArray(1, 2, 3, 4, 5);
console.log('array[-1]: ', array[-1]) // array[-1]: 5
- 读取操作转为执行某个函数,实现属性的链式操作
function Pipe(value) {
// 存储链式调用函数
let funcStack = [];
let proxy = new Proxy({}, {
get: function (target, propKey) {
// 取值
if (propKey === 'get') {
return funcStack.reduce((preValue, func) => func(preValue), value)
}
// 链式调用,将函数压入调用栈,取值时逐个调取
else {
funcStack.push(window[propKey])
}
return proxy;
}
})
return proxy;
}
var double = value => value * 2;
var pow = value => value * value;
let ret = Pipe(3).double.pow.get;
console.log('ret: ', ret); // ret: 36
- get方法的第三个参数
receiver
指的是调用者本身,如果是proxy对象调用则为proxy对象,如果原始对象调用则为原始对象
let proxy = new Proxy({}, {
get: function(target, propKey, receiver) {
return receiver;
}
})
// 代理对象调用方法,则receiver为代理对象
let ret = proxy.getReceiver === proxy
console.log('ret: ', ret);
// 通过原型链的方式创建代理,使用原始对象调用方法,receiver为原始对象
let obj = Object.create(proxy);
let ret2 = obj.getReceiver === obj;
console.log('ret2: ', ret2);
2.2、set(target, proKey, value, receiver)
拦截对象属性的设置,比如proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值
- 赋值校验
let proxy = new Proxy({}, {
set:function(target, propKey, value) {
// 大于100的都赋值为100
if (value > 100) {
value = 100;
}
target[propKey] = value;
return true;
}
})
let person = Object.create(proxy);
person.age = 120;
console.log('person.age: ', person.age)
2.3、has(target, propKey)
拦截propKey in proxy
操作,返回一个布尔值
- 调用代理时触发该方法
let handler = {
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
}
let target = {
_prop : 'xxx',
prop : 'zzz'
}
let proxy = new Proxy(target, handler);
console.log('prop' in proxy) // true
console.log('_prop' in proxy) // false
- 注意:如果是通过继承的方式实现的代理,该方法不会被调用
let handler = {
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
}
let target = {
_prop : 'xxx',
prop : 'zzz'
}
Object.setPrototypeOf(target, new Proxy({}, handler));
console.log('prop' in target) // true
console.log('_prop' in target) // true
- 该方法对
for...in
循环不生效
let handler = {
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
}
let target = {
_prop : 'xxx',
prop : 'zzz'
}
let proxy = new Proxy(target, handler);
for(let key in target) {
// 两个属性均会输出
console.log('key: ', key)
}
2.4、deleteProperty(target, propKey)
拦截delete proxy[propKey]
的操作,返回一个布尔值
let handler = {
deleteProperty(target, key){
console.log('deleteProperty');
return Reflect.deleteProperty(target, key);
}
}
let person = {
age: 12
}
let proxy = new Proxy(person, handler);
console.log('person: ', person); // person: {age: 12}
delete proxy.age; // deleteProperty
console.log('person: ', person); // person: {}
2.5、ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性
let handler = {
ownKeys(target) {
console.log('ownKeys');
return Reflect.ownKeys(target);
}
}
let person = {
name: 'xxx',
age: 18
}
let proxy = new Proxy(person, handler);
// Object.keys
Object.keys(proxy);
// for...in
for(let key in proxy) {
console.log('key: ', key)
}
// getOwnPropertyNames()
Object.getOwnPropertyNames(proxy)
// getOwnPropertySymbols()
Object.getOwnPropertySymbols(proxy);
for...in
中如果返回的数组中不包含原对象的属性,则不会遍历出值
let handler = {
ownKeys(target) {
console.log('ownKeys');
// 数组中必须是字符串或Symbol否则会报错
return ['a', 'b'];
}
}
let person = {
name: 'xxx',
age: 12
}
let proxy = new Proxy(person, handler);
// 不会有输出
for(let key in proxy) {
console.log('key: ', key);
}
2.6、getOwnPropertyDescriptor(target, propKey)
拦截Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象
- 当前对象自己属性的描述
let handler = {
getOwnPropertyDescriptor(target, key) {
console.log('getOwnPropertyDescriptor...');
return Reflect.getOwnPropertyDescriptor(target, key);
}
}
let person = {
age: 12
}
let proxy = new Proxy(person, handler);
let age = Object.getOwnPropertyDescriptor(proxy, 'age');
let name = Object.getOwnPropertyDescriptor(proxy, 'name');
console.log('age: ', age); // {value: 12, writable: true, enumerable: true, configurable: true}
console.log('name: ', name); // undefined
2.7、defineProperty(target, propKey, propDesc)
拦截Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值
let handler = {
defineProperty(target, key, descriptor) {
console.log('defineProperty')
return Reflect.defineProperty(target, key, descriptor);
}
}
let person = {
age: 10
}
let proxy = new Proxy(person, handler);
// 两种方式增加属性都会触发
proxy.height = 180;
Object.defineProperty(proxy, 'weight', {
value: 70
});
console.log('person: ', person)
2.8、preventExtensions(target)
拦截Object.preventExtensions(proxy)
,返回一个布尔值
let handler = {
preventExtensions(target) {
console.log('perventExtensions...');
return Reflect.preventExtensions(target);
}
}
let person = {
name: 'xxx',
age: 13
}
let proxy = new Proxy(person, handler);
Object.preventExtensions(proxy);
2.9、getPrototypeOf(target)
拦截Object.getPrototypeOf(proxy),返回一个布尔值
Object.prototype.__proto__
Object.prototype.isPrototypeOf()
Object.getPrototypeOf()
Reflect.getPrototypeOf()
instanceof
let handler = {
getPrototypeOf(target) {
console.log('getPrototypeOf....');
return Reflect.getPrototypeOf(target);
}
}
let target = {
name: 'xxx'
}
let proxy = new Proxy(target, handler);
// 以下4句代码都会触发handler中的getPrototypeOf方法
Object.getPrototypeOf(proxy)
Reflect.getPrototypeOf(proxy)
proxy instanceof Object
// Object.prototype是否是proxy的原型
Object.prototype.isPrototypeOf(proxy)
2.10、isExtensible(target)
拦截Object.isExtensible(proxy),返回一个布尔值
let handler = {
isExtensible(target) {
console.log('isExtensible...');
// 这里如果返回和原对象的结果不一致会报错
// 如果Object.preventExtensions(target),则必须返回false,否则必须返回true
return Reflect.isExtensible(target);
}
}
let person = {
name: 'xxx'
}
// 禁止扩展
Object.preventExtensions(person)
let proxy = new Proxy(person, handler);
let ret = Object.isExtensible(proxy);
console.log('ret: ', ret) // false
2.11、setPrototypeOf(target, proto)
拦截Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截
let handler = {
setPrototypeOf(target, proto) {
console.log('setPrototypeOf', proto)
// 返回一个布尔值
return true;
}
}
let person = {
name: 'xxx',
age: 13
}
let proxy = new Proxy(person, handler);
Object.setPrototypeOf(proxy, new Function())
2.12、apply(target, object, args)
拦截Proxy实例作为函数调用的操作,比如proxy(...args)
、proxy.call(object, ...args)
、proxy.apple(...)
- 该方法作用的是函数,即Proxy()的第一个参数是函数
let handler = {
apply(target, ctx, args) {
return Reflect.apply(target, ctx, args) * 2;
}
}
function sum(a, b) {
return a + b;
}
let proxy = new Proxy(sum, handler);
console.log(proxy(1, 2)); // 6
console.log(proxy.call(null, 1, 2)); // 6
console.log(proxy.apply(null, [1, 2])); // 6
console.log(Reflect.apply(proxy, null, [1, 2])); // 6
2.13、construct(target, args, newTarget)
拦截Proxy实例作为构造函数调用的操作,比如new Proxy(...args)
, target:目录对象
let handler = {
construct(target, args, newTarget){
console.log('target: ', target, 'args: ', args, 'newTarget: ', newTarget);
return Reflect.construct(target, args, newTarget);
}
}
function Target(age) {
this.age = age;
}
let Person = new Proxy(Target, handler);
let p = new Person(1);
console.log('p: ', p)// Target {age: 1}
3、其他特性
3.1、revocable方法
Proxy
的静态方法revocable
返回proxy
对象与revoca
函数
let handler = {
get:function(target, propKey, receiver) {
return 'xxx';
}
}
let person = {
name: 'name'
}
let {proxy, revoca} = Proxy.revocable(person, handler);
console.log('proxy.name: ', proxy.name)
revoca();
// 再次访问报错
console.log('proxy.name: ', proxy.name)
3.2、this指针
- target内部,一般情况下this指向的是调用者,即target调用则指向target,proxy调用则指向proxy
let target = {
test() {
console.log(this === target)
}
}
let proxy = new Proxy(target, {});
target.test(); // true
proxy.test(); // false
- handler内方法中this指向的是handler对象
let handler = {
get(target, propKey, receiver) {
console.log(this == handler);
}
}
let proxy = new Proxy({}, handler);
proxy.foo; // 触发get方法,控制台输出true
- 有些原生对象的内部属性只有this指针才能访问,从而proxy无法代理
let date = new Date('2021-01-01');
console.log(date.getDate()); // 1
let proxy = new Proxy(date, {})
console.log(proxy.getDate()) // Uncaught TypeError: this is not a Date object.
// 可以通过this绑定原生对象来解决这个问题
let date = new Date('2021-01-01');
let proxy = new Proxy(date, {
get(target, propKey, receiver) {
if (propKey === 'getDate') {
// 绑定this为原始对象
return target[propKey].bind(target);
}
return Reflect.get(target, propKey, receiver);
}
})
console.log(proxy.getDate()) // 1