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); // hdy console.log(proxy.age); // 18 console.log(proxy.height); // 还未定义height属性
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 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); // { proxy: {}, revoke: [Function (anonymous)] } const proxy = ans.proxy; console.log(proxy.ownner); // ownner: hdy console.log(proxy.name); // name: hdy /** * 撤销以后再用就报错: * Cannot perform 'get' on a proxy that has been revoked */ ans.revoke(); // console.log(proxy.name); // error
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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); // true Reflect.set(target, key, value, receiver); } } const proxy = new Proxy({}, handler); proxy.name = 'hdy';
复制成功
1
2
3
4
5
6
7
8
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
使用
writable
get
Reflect.get
- 获取属性加上标识
const proxy = new Proxy({ age: 19 }, { get(target, prop, proxy) { return `读取到${prop}的属性值是${target[prop]}`; } }) console.log(proxy.age); // 读取到age的属性值是19
复制成功
1
2
3
4
5
6
7
2
3
4
5
6
7
# set
- 作用:劫持赋值操作
- 定义:set(target, key, value, receiver)
- 入参:Object, String, any, Object
- 返回:Boolean
- 调用:proxy.key = value
receiver
- 直接调用proxy是proxy本身
- proxy作为原型链,后代对象set属性也会调用此劫持,但receiver是后代对象
使用
原型链调用
this
Reflect.set
const proxy = new Proxy({}, { set(target, key, value, receiver) { console.log('劫持操作'); Reflect.set(target, key, value, receiver); } }) console.log(proxy.name = 'hdy'); /** * 劫持操作 * hdy */
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# has
- 作用:针对【in】方法的劫持
- 定义:has(target, key)
- 入参:Object, String
- 返回:Boolean
- 调用:key in target
使用
Reflect.has
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); // true console.log('_name' in proxy); // false console.log(proxy.name); // hdy 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
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
# apply
- 作用:劫持函数调用操作
- 定义:apply(target, thisArg, arguments)
- 入参:Object, Object, Array
- 返回:any
- 调用:proxy() | proxy.apply() | proxy.call()
- tip:被代理的一定要是函数,否则自身都没有apply方法
使用
thisArg
call/apply
Reflect.apply
- 将所有入参加上代理【(proxy)】标识
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'); // I said (proxy)hdy and (proxy)cool!
复制成功
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# ownKeys
- 作用:劫持获取对象keys值操作
- 定义:ownKeys(target)
- 入参:Object
- 返回:Iterator
- 调用:
- Reflect.ownKeys(proxy)
- Object.keys(proxy)
- Object.getOwnPropertyNames(proxy)
- Object.getOwnPropertySymbols(proxy)
- 必须返回一个
可枚举对象
使用
Reflect.ownKeys
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)); // [ 'name', 'age' ]
复制成功
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# construct
- 作用:劫持原生的constructor函数
- 定义:construct(target, arguments, newTarget)
- 入参:Object, Array, Object(本Proxy对象)
- 返回:Object
- 调用:new proxy(args);
- tip:target必须有constructor(是函数\类)
使用
拦截验证
Reflect.constrcut
- 劫持new操作符,做更多的事
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); // People { head: 1, hand: 2, foot: 2 }
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# getPropertypeOf
- 作用:劫持访问原型链对象操作
- 定义:getPropertypeOf(target)
- 入参:Object
- 返回:Object | null
- 调用:
- Object.getPropertyOf(proxy)
- Reflect.getPrototypeOf(proxy)
- instanceof
- proxy._ _ proto _ _
- obj.isPrototypeOf(proxy)
限制
- 必须返回Object或null
- target不可扩展的情况下禁止撒谎
使用
禁止撒谎
多种触发方式
Reflect.getPropertypeOf
- 撒谎
const father = { name: 'hdy' }; const child = Object.create(father); const proxy = new Proxy(child, { getPrototypeOf(target) { return { value: '就不告诉你我爹是谁' }; } }) console.log(Object.getPrototypeOf(proxy)); // { value: '就不告诉你我爹是谁' } console.log(Object.getPrototypeOf(child)); // { name: 'hdy' }
复制成功
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# setPrototypeOf
- 作用:拦截Object.setProtypeOf()
- 定义:setPrototypeOf(target, prototype)
- 入参:Object, Object | null
- 返回:Boolean
- 调用:Object.setPrototypeOf(proxy, proto)
使用
Reflect.setPrototypeOf
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)); // true console.log(father.isPrototypeOf(child)); // true
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# defineProperty
- 作用:拦截原生的defineProperty方法
- 定义:defineProperty(target, property, descriptor)
- 入参:Object, String, Object
- 返回:Boolean (属性操作是否成功)
- 调用:Object.defineProperty(proxy, name, descriptor)
使用
Reflect.defineProperty
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; } }); // [object Object]设置了age console.log(proxy.age); // 0岁 proxy.age = 100; // [object Object]设置了_age console.log(proxy.age); // 100岁
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# deleteProperty
- 作用:劫持delete操作
- 定义:deleteProperty(target, name)
- 入参:Object, String
- 返回:Boolean (是否删除成功)
- 调用:delete proxy.prop
使用
属性不存在
Reflect.deleteProperty
- 劫持了但是没有调用原生,所以删除无效
const proxy = new Proxy({ name: 'hdy' }, { deleteProperty(target, name) { console.log(`不能删除${name}属性!`); } }); console.log(proxy.name); // hdy console.log(delete proxy.name); // 不能删除name属性! false console.log(proxy.name); // hdy
复制成功
1
2
3
4
5
6
7
8
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); // hdy console.log(delete proxy.name); // 删除了name属性! true console.log(proxy.name); // undefined
复制成功
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# getOwnPropertyDescriptor
- 作用:拦截Object.getOwnPropertyDescriptor(target, name)
- 定义:getOwnPropertyDescriptor(target, name)
- 入参:Object, String
- 返回:Object | undefined
- 调用:Object.getOwnPropertyDescriptor(proxy, name)
限制:configurable相关不能撒谎
- 必须返回Object或undefined
- 不可配置属性不可以返回可配置
- 属性存在且target不可扩展,不可返回不存在
- 属性不存在的属性且target不可扩展,不可返回存在
- 可配置或不存在的属性,不可以返回不可配置
使用
configurable撒谎
Reflect.getOwnPropertyDescriptor
- 给所有属性加一个【proxy】标识
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')); /** { value: 'proxy:hdy', writable: false, enumerable: true, configurable: true } */
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# isExtensible
- 作用:劫持Object.isExtensible(target)
- 定义:isExtensible(target)
- 入参:Object
- 返回:Boolean
- 调用:Object.isExtensible(target)
- 只能做操作,不能修改值
使用
不能撒谎
Reflect.isExtensible
const proxy = new Proxy({}, { isExtensible(target) { console.log('自定义操作!'); return Reflect.isExtensible(target); } }) console.log(Object.isExtensible(proxy)); // 自定义操作! true
复制成功
1
2
3
4
5
6
7
2
3
4
5
6
7
# preventExtensions
- 作用:劫持限制对象扩展类操作
- 定义:preventExtensions(target)
- 入参:Object
- 返回:Boolean
- 调用:
- Object.preventExtensions(proxy)
- Object.seal(proxy)
- Object.freeze(proxy)
使用
Reflect.preventExtensions
const proxy = new Proxy({}, { preventExtensions(target) { throw Error('此对象不可禁止扩展'); } }) // Object.preventExtensions(proxy); // Error: 此对象不可禁止扩展 // Object.seal(proxy); // Error: 此对象不可禁止扩展 // Object.freeze(proxy); // Error: 此对象不可禁止扩展
复制成功
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
v1.4.16