Proxy
- Proxy:用于创建一个对象的代理,从而实现对目标对象操作的拦截。
- target:被代理的对象,一般理解为存储后端。
- handler:处理器对象。
- traps:handler提供的拦截原生操作方法。
声明
构造函数
- 调用:new Proxy(target, handler)
- 入参:Object, Object
- get入参的属性名默认时String类型:get(target: object, name: string) {}
- 数组要将String转化为Number才能计算下标
const obj = {
name: 'hdy',
age: 18
}
const proxy = new Proxy(obj, {
get (target, name) {
return target[name] ? target[name] : `还未定义${name}属性`;
}
})
console.log(proxy.name);
console.log(proxy.age);
console.log(proxy.height);
1
2
3
4
5
6
7
8
9
10
11
12
计算索引需要先将字符串转换成数字
const arr = [1, 2, 3];
const proxy = new Proxy(arr, {
get (target, index) {
return index >= 0 ? target[index] : target[+index + target.length];
}
})
console.log(proxy[1]);
console.log(proxy[-1]);
1
2
3
4
5
6
7
8
const obj = {
name: 'hdy',
email: ''
}
const proxy = new Proxy(obj, {
set(target, name, value) {
if (name === 'email') {
const reg = /(?<=^\b|\s+)\w+@[\w\.]+\b/gi;
if (reg.test(value.trim())) {
target.email = value.match(reg)[0];
} else {
throw Error('好好检查一下邮箱格式!')
}
}
}
})
proxy.email = '986005715@qq.com';
console.log(proxy.email);
console.log(obj.email);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 不设置任何handler处理函数,那么proxy接受到的操作就会原样传递给target
const obj = {};
const proxy = new Proxy(obj, {});
proxy.name = 'hdy';
console.log(obj.name);
1
2
3
4
revocable
- 作用:创建一个可撤销的代理对象
- 调用:Proxy.revocable(target, handler)
- 入参:Object, Object
- 返回:{ proxy: proxy, revoke: revokeFn }
- tip:和new Proxy()创建的一样,只是这个可以被删除
const ans = Proxy.revocable({}, {
get (target, name) {
return `${name}: hdy`;
}
})
console.log(ans);
const proxy = ans.proxy;
console.log(proxy.ownner);
console.log(proxy.name);
ans.revoke();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Reflect
- Reflect提供JS原生操作,并且只有静态方法
- Reflect拥有的静态方法都和handler提供的方法一一对应,可以用来proxy劫持操作后再执行target原生JS操作
this
- handler里面的traps函数里的this已经被绑定到handler上了。不是proxy也不是target也不是receiver
const handler = {
set(target, key, value, receiver) {
console.log(this === handler);
Reflect.set(target, key, value, receiver);
}
}
const proxy = new Proxy({}, handler);
proxy.name = 'hdy';
1
2
3
4
5
6
7
8
handler方法
get
- 作用:拦截读取属性
- 定义:get(target, prop, receiver)
- 入参:Object, String, Object
- 返回:any
- 调用:proxy.XX | proxy[XX]
- receiver:如果target里面有getter,那么getter里面的this就是receiver
约束
- 如果target属性的writable是false,则proxy不可改动返回值
- 如果target属性的get是undefined,那么proxy也要返回undefined
const proxy = new Proxy({ age: 19 }, {
get(target, prop, proxy) {
return `读取到${prop}的属性值是${target[prop]}`;
}
})
console.log(proxy.age);
1
2
3
4
5
6
7
- 如果target属性的writable是false,则proxy不可改动返回值
const obj = Object.defineProperty({}, 'age', {
value: 18,
writable: false
})
const proxy = new Proxy(obj, {
get(target, name, proxy) {
return target[name] + 1;
}
})
console.log(proxy.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 如果target属性的get是undefined,那么proxy也要返回undefined
const obj = Object.defineProperty({}, 'age', {
set(){}
})
const proxy = new Proxy(obj, {
get(target, name, proxy) {
return 111;
}
})
console.log(proxy.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 读取对象属性
- receiver:如果target里面有getter,那么getter里面的this就是receiver
const obj = {
name: 'hdy'
}
console.log(obj.name);
console.log(Reflect.get(obj, 'name'));
1
2
3
4
5
set
- 作用:劫持赋值操作
- 定义:set(target, key, value, receiver)
- 入参:Object, String, any, Object
- 返回:Boolean
- 调用:proxy.key = value
receiver
- 直接调用proxy是proxy本身
- proxy作为原型链,后代对象set属性也会调用此劫持,但receiver是后代对象
const proxy = new Proxy({}, {
set(target, key, value, receiver) {
console.log('劫持操作');
Reflect.set(target, key, value, receiver);
}
})
console.log(proxy.name = 'hdy');
1
2
3
4
5
6
7
8
9
10
11
12
const proxy = new Proxy({ name: 'hdy' }, {
set(target, key, value, receiver) {
console.log(receiver === proxy);
return Reflect.set(target, key, value, receiver);
}
});
const child = Object.create(proxy);
proxy.name = '张三';
child.name = '李四';
child.age = 18;
console.log(Object.keys(child));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- proxy里面函数的this都被绑定为handler
const handler = {
set(target, key, value, receiver) {
console.log(this === handler);
Reflect.set(target, key, value, receiver);
}
}
const proxy = new Proxy({}, handler);
proxy.name = 'hdy';
1
2
3
4
5
6
7
8
- 如果遇到 setter,receiver则为setter调用时的this值。
const obj = {};
Reflect.set(obj, 'age', 18);
console.log(obj);
1
2
3
has
- 作用:针对【in】方法的劫持
- 定义:has(target, key)
- 入参:Object, String
- 返回:Boolean
- 调用:key in target
const obj = Object.defineProperties(
{
['_name']: 'hdy'
},
{
name: {
get() {
return this['_name'];
},
set(value) {
this['_name'] = value;
}
}
}
)
const proxy = new Proxy(obj, {
has(target, key) {
return key[0] !== '_';
}
})
console.log('_name' in obj);
console.log('_name' in proxy);
console.log(proxy.name);
proxy.name = '张三';
console.log(proxy.name);
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
26
27
const obj = { name: 'hdy' };
console.log(Reflect.has(obj, 'name'));
console.log('name' in obj);
1
2
3
apply
- 作用:劫持函数调用操作
- 定义:apply(target, thisArg, arguments)
- 入参:Object, Object, Array
- 返回:any
- 调用:proxy() | proxy.apply() | proxy.call()
- tip:被代理的一定要是函数,否则自身都没有apply方法
function say(word, word2) {
console.log(`I said ${word} and ${word2}!`);
}
const proxy = new Proxy(say, {
apply(target, thisArg, args) {
args = args.map(item => `(proxy)${item}`);
return target.call(thisArg, ...args);
}
})
proxy('hdy', 'cool');
1
2
3
4
5
6
7
8
9
10
11
function finished(work) {
console.log(`${this.name} finished ${work}`);
}
const proxy = new Proxy(finished, {
apply(target, thisArg, args) {
return target.call(thisArg, args);
}
})
proxy('homework');
const obj = {
name: '张三',
finished: proxy
};
obj.finished('coding');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 除了new调用的是construct代理,其他函数调用方式都是触发apply代理
function finished(work) {
console.log(`${this.name} finished ${work}`);
}
const proxy = new Proxy(finished, {
apply(target, thisArg, args) {
return target.call(thisArg, args);
}
})
const obj = {
name: '张三',
};
proxy.call(obj, '洗衣服');
proxy.apply(obj, ['洗衣服']);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 作用:类似原生的Function.prototype.apply()
- 调用:Reflect.apply(target, thisArg, args)
- 入参:Object, Object, Array
- 返回:any
const obj = { name: 'hdy' };
function sayValue(key) {
console.log(this[key]);
}
Reflect.apply(sayValue, obj, ['name']);
1
2
3
4
5
6
ownKeys
- 作用:劫持获取对象keys值操作
- 定义:ownKeys(target)
- 入参:Object
- 返回:Iterator
- 调用:
- Reflect.ownKeys(proxy)
- Object.keys(proxy)
- Object.getOwnPropertyNames(proxy)
- Object.getOwnPropertySymbols(proxy)
const obj = {
name: 'hdy',
age: 18,
['_height']: 180
}
const proxy = new Proxy(obj, {
ownKeys(target) {
return Reflect.ownKeys(target).filter(item => !item.startsWith('_'));
}
})
console.log(Object.keys(proxy));
1
2
3
4
5
6
7
8
9
10
11
- 相当于Object.getOwnPropertyNames()
const obj = Object.defineProperties({ name: 'hdy' }, {
age: {
value: 18
}
})
console.log(Reflect.ownKeys(obj));
console.log(Object.getOwnPropertyNames(obj));
console.log(Object.keys(obj));
1
2
3
4
5
6
7
8
9
construct
- 作用:劫持原生的constructor函数
- 定义:construct(target, arguments, newTarget)
- 入参:Object, Array, Object(本Proxy对象)
- 返回:Object
- 调用:new proxy(args);
- tip:target必须有constructor(是函数\类)
class People {
constructor() {
this.head = 1;
}
}
const proxy = new Proxy(People, {
construct(target, args, newTarget) {
const obj = new target(...args);
obj.hand = 2;
obj.foot = 2;
return obj;
}
})
const a = new proxy();
console.log(a);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class User {
constructor(account, password, email) {
this.account = account;
this.password = password;
this.email = email;
}
}
const u1 = new User('123', '456我是哈哈', '999');
console.log(u1);
const UserProxy = new Proxy(User, {
construct(target, args) {
const nameReg = /\w{8,16}/g;
const pwdReg = /[\w,\.\/\?\>\<\!@#\$%\^\&\*\(\)]{8,}/g;
const emailReg = /\w+@[\w\.]+\b/g;
if (!(nameReg.test(args[0]) && pwdReg.test(args[1]) && emailReg.test(args[2]))) {
throw Error('格式不正确!')
}
return new target(...args);
}
})
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
26
27
- 作用:相当于new target()
- 调用:Reflect.construct(target, args, newTarget)
- 入参:Function, Array, Function(新对象原型的constructor属性,默认是参数1)
- 返回:Object | null
class Animal {}
Animal.prototype.protoIs = 'Animal的原型';
class People {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
People.prototype.protoIs = 'People的原型';
const p1 = Reflect.construct(People, ['hdy', 18], Animal);
console.log(p1);
console.log(p1.protoIs);
console.log(p1 instanceof Animal);
console.log(p1 instanceof People);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
getPropertypeOf
- 作用:劫持访问原型链对象操作
- 定义:getPropertypeOf(target)
- 入参:Object
- 返回:Object | null
- 调用:
- Object.getPropertyOf(proxy)
- Reflect.getPrototypeOf(proxy)
- instanceof
- proxy._ _ proto _ _
- obj.isPrototypeOf(proxy)
限制
- 必须返回Object或null
- target不可扩展的情况下禁止撒谎
const father = { name: 'hdy' };
const child = Object.create(father);
const proxy = new Proxy(child, {
getPrototypeOf(target) {
return { value: '就不告诉你我爹是谁' };
}
})
console.log(Object.getPrototypeOf(proxy));
console.log(Object.getPrototypeOf(child));
1
2
3
4
5
6
7
8
9
10
11
- 禁止:target不可扩展的情况下不返回target本身的原型
const father = { name: 'hdy' };
const child = Object.create(father);
Object.preventExtensions(child);
const proxy = new Proxy(child, {
getPrototypeOf(target) {
return { value: '就不告诉你我爹是谁' };
}
})
console.log(Object.getPrototypeOf(proxy));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a instanceof b // a._ _ proto _ _ 往上能不能找到 b.prototype
b.isPrototypeOf(a) // a._ _ proto _ _ 往上能不能找到 b 本身
class lying {}
lying.prototype = { value: '我是假原型' };
const father = { name: 'hdy' };
const child = Object.create(father);
const proxy = new Proxy(child, {
getPrototypeOf(target) {
return lying.prototype;
}
})
console.log(Object.getPrototypeOf(proxy) === lying.prototype);
console.log(Reflect.getPrototypeOf(proxy) === lying.prototype);
console.log(proxy.__proto__ === lying.prototype);
console.log(proxy instanceof lying);
console.log(lying.prototype.isPrototypeOf(proxy));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const arr = [1, 2, 3];
console.log(Reflect.getPrototypeOf(arr) === Object.getPrototypeOf(arr));
1
2
setPrototypeOf
- 作用:拦截Object.setProtypeOf()
- 定义:setPrototypeOf(target, prototype)
- 入参:Object, Object | null
- 返回:Boolean
- 调用:Object.setPrototypeOf(proxy, proto)
const father = { name: 'hdy' };
const child = { age: 18 };
const proxy = new Proxy(child, {
setPrototypeOf(target, proto) {
console.log('设置原型劫持操作');
return Reflect.setPrototypeOf(target, proto);
}
})
Object.setPrototypeOf(proxy, father);
console.log(father.isPrototypeOf(proxy));
console.log(father.isPrototypeOf(child));
1
2
3
4
5
6
7
8
9
10
11
12
const father = {};
const child = {};
console.log(Reflect.setPrototypeOf(child, father));
console.log(father.isPrototypeOf(child));
1
2
3
4
defineProperty
- 作用:拦截原生的defineProperty方法
- 定义:defineProperty(target, property, descriptor)
- 入参:Object, String, Object
- 返回:Boolean (属性操作是否成功)
- 调用:Object.defineProperty(proxy, name, descriptor)
const proxy = new Proxy({}, {
defineProperty(target, prop, descriptor) {
console.log(`${target}设置了${prop}`);
return Reflect.defineProperty(target, prop, descriptor);
}
})
Object.defineProperty(proxy, 'age', {
get() {
return this._age ? this._age + '岁' : '0岁';
},
set(value) {
this._age = value;
}
});
console.log(proxy.age);
proxy.age = 100;
console.log(proxy.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 和Object.defineProperty唯一不同的是返回值是Boolean(Object.defineProperty返回Object)
const obj = {};
const ans = Reflect.defineProperty(obj, 'name', { value: 'hdy' });
console.log(ans);
console.log(obj.name);
1
2
3
4
deleteProperty
- 作用:劫持delete操作
- 定义:deleteProperty(target, name)
- 入参:Object, String
- 返回:Boolean (是否删除成功)
- 调用:delete proxy.prop
const proxy = new Proxy({ name: 'hdy' }, {
deleteProperty(target, name) {
console.log(`不能删除${name}属性!`);
}
});
console.log(proxy.name);
console.log(delete proxy.name);
console.log(proxy.name);
1
2
3
4
5
6
7
8
const proxy = new Proxy({ name: 'hdy' }, {
deleteProperty(target, name) {
const res = Reflect.deleteProperty(target, name);
console.log(res ? `删除了${name}属性!` : `不能删除${name}属性!`);
return res;
}
});
console.log(proxy.name);
console.log(delete proxy.name);
console.log(proxy.name);
1
2
3
4
5
6
7
8
9
10
11
- 属性不存在,Reflect.deleteProperty()依然返回true
- 只有被冻结或被限制删除的属性才会返回false
const obj = {};
console.log(delete obj.name);
Object.defineProperty(obj, 'age', {
value: 18
});
console.log(Reflect.deleteProperty(obj, 'age'));
1
2
3
4
5
6
7
8
9
10
如果target不是Object会报错
const obj = {
name: 'hdy',
age: 18
}
console.log(Reflect.deleteProperty(obj, 'height'));
console.log(Reflect.deleteProperty(obj, 'name'));
console.log(obj.name);
Object.freeze(obj);
console.log(Reflect.deleteProperty(obj, 'age'));
console.log(obj.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
getOwnPropertyDescriptor
- 作用:拦截Object.getOwnPropertyDescriptor(target, name)
- 定义:getOwnPropertyDescriptor(target, name)
- 入参:Object, String
- 返回:Object | undefined
- 调用:Object.getOwnPropertyDescriptor(proxy, name)
限制:configurable相关不能撒谎
- 必须返回Object或undefined
- 不可配置属性不可以返回可配置
- 属性存在且target不可扩展,不可返回不存在
- 属性不存在的属性且target不可扩展,不可返回存在
- 可配置或不存在的属性,不可以返回不可配置
const proxy = new Proxy({ name: 'hdy' }, {
getOwnPropertyDescriptor(target, name) {
const descriptor = Object.getOwnPropertyDescriptor(target, name);
descriptor.value = `proxy:${descriptor.value}`;
descriptor.writable = false;
return descriptor;
}
})
console.log(Object.getOwnPropertyDescriptor(proxy, 'name'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const proxy = new Proxy({ name: 'hdy' }, {
getOwnPropertyDescriptor(target, name) {
const descriptor = Object.getOwnPropertyDescriptor(target, name);
descriptor.configurable = false;
return descriptor;
}
})
console.log(Object.getOwnPropertyDescriptor(proxy, 'name'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- 与Object.getOwnPropertyDescriptor的不同:
第一个参数如果不是对象,Reflect.getOwnPropertyDescriptor会报错
第一个参数如果不是对象,Object.getOwnPropertyDescriptor会转化为对象处理
const obj = { name: 'hdy' };
console.log(Reflect.getOwnPropertyDescriptor(obj, 'name'));
1
2
3
4
isExtensible
- 作用:劫持Object.isExtensible(target)
- 定义:isExtensible(target)
- 入参:Object
- 返回:Boolean
- 调用:Object.isExtensible(target)
const proxy = new Proxy({}, {
isExtensible(target) {
console.log('自定义操作!');
return Reflect.isExtensible(target);
}
})
console.log(Object.isExtensible(proxy));
1
2
3
4
5
6
7
const proxy = new Proxy({}, {
isExtensible(target) {
return !Reflect.isExtensible(target);
}
})
console.log(Object.isExtensible(proxy));
1
2
3
4
5
6
7
8
9
10
11
第一个参数不是对象时:Object.isExtensible会转化成对象判断
第一个参数不是对象时:Reflect.isExtensible会报错
const obj = {};
console.log(Reflect.isExtensible(obj));
Object.freeze(obj);
console.log(Reflect.isExtensible(obj));
1
2
3
4
5
preventExtensions
- 作用:劫持限制对象扩展类操作
- 定义:preventExtensions(target)
- 入参:Object
- 返回:Boolean
- 调用:
- Object.preventExtensions(proxy)
- Object.seal(proxy)
- Object.freeze(proxy)
const proxy = new Proxy({}, {
preventExtensions(target) {
throw Error('此对象不可禁止扩展');
}
})
1
2
3
4
5
6
7
8
9
- 与Object.preventExtensions不同点是第一个参数如果不是对象会报错
const obj = { name: 'hdy' };
Reflect.preventExtensions(obj);
obj.age = 18;
console.log(obj.age);
1
2
3
4