Appearance
Proxy代理完整使用
在 《Proxy代理器的使用》中介绍了 Proxy 代理的使用,在补浏览器环境框架中,希望可以写一个完整的 Proxy 代理函数,用来动态的配置需要代理某些对象,从而获取指定的关键信息,还有一些代理失效的问题,接下来一起看下。
完整的 Proxy 代码
js
function addProxy(target, name) {
return new Proxy(target, {
get: function (target, prop, receiver) {
console.log(`${name} get ${prop} result ${target[prop]}`);
return Reflect.get(target, prop, receiver);
},
set: function (target, prop, value, receiver) {
console.log(`${name} set ${prop} value ${value}`);
return Reflect.set(target, prop, value, receiver);
},
apply: function (target, thisArgument, argumentsList) {
const result = Reflect.apply(target, thisArgument, argumentsList);
console.log(`${name} apply args ${argumentsList} result ${result}`);
return result;
},
construct: function (target, argumentsList, newTarget) {
const result = Reflect.construct(target, argumentsList, newTarget);
console.log(`${name} construct args ${argumentsList} result ${result}`);
return result;
},
defineProperty: function (target, prop, attributes) {
console.log(`${name} defineProperty ${prop} attributes ${JSON.stringify(attributes)}`);
return Reflect.defineProperty(target, prop, attributes);
},
deleteProperty: function (target, prop) {
const result = Reflect.deleteProperty(target, prop);
console.log(`${name} deleteProperty ${prop} result ${result}`);
},
getOwnPropertyDescriptor: function (target, prop) {
const result = Reflect.getOwnPropertyDescriptor(target, prop);
console.log(`${name} getOwnPropertyDescriptor ${prop} result ${JSON.stringify(result)}`);
return result;
},
getPrototypeOf: function (target) {
const result = Reflect.getPrototypeOf(target);
console.log(`${name} getPrototypeOf result ${result}`);
return result;
},
has: function (target, prop) {
const result = Reflect.has(target, prop);
console.log(`${name} has ${prop} result ${result}`);
return result;
},
isExtensible: function (target) {
const result = Reflect.isExtensible(target);
console.log(`${name} isExtensible result ${result}`);
return result;
},
ownKeys: function (tar) {
const result = Reflect.ownKeys(tar);
console.log(`${name} ownKeys result ${result}`);
return result;
},
preventExtensions: function (target) {
const result = Reflect.preventExtensions(target);
console.log(`${name} preventExtensions result ${result}`);
return result;
},
setPrototypeOf: function (target, prototype) {
const result = Reflect.setPrototypeOf(target, prototype);
console.log(`${name} setPrototypeOf result ${result}`);
return result;
},
});
}
let person = {
age: 18,
};
function sum(a,b){
return a + b;
}
let Animal = function (name, type) {
this.name = name;
this.type = type;
}
person = addProxy(person, 'person');
sum = addProxy(sum, 'sum');
Animal = addProxy(Animal, 'Animal');
// get、set 触发
person.age;
person.name = '张三';
// apply 触发
sum(1, 2);
// construct 触发
let anima = new Animal('哈士奇', '狗');
// defineProperty 触发
Object.defineProperty(person, 'height', {
value: 180,
writable: true,
enumerable: true,
configurable: true
})
// deleteProperty 触发
delete person.height;
// getOwnPropertyDescriptor 触发
Object.getOwnPropertyDescriptor(person, 'age');
// getPrototypeOf 触发
Object.getPrototypeOf(person);
// has 触发
'age' in person;
// isExtensible 触发
Object.isExtensible(person);
// ownKeys 触发
Object.keys(person);
// preventExtensions 触发
Object.preventExtensions(person);
// setPrototypeOf 触发
Object.setPrototypeOf(anima, Array.prototype);
输出内容
person get age result 18
person set name value 张三
person getOwnPropertyDescriptor name result undefined
person defineProperty name attributes {"value":"张三","writable":true,"enumerable":true,"configurable":true}
sum apply args 1,2 result 3
Animal get prototype result [object Object]
Animal construct args 哈士奇,狗 result [object Object]
person defineProperty height attributes {"value":180,"writable":true,"enumerable":true,"configurable":true}
person deleteProperty height result true
person getOwnPropertyDescriptor age result {"value":18,"writable":true,"enumerable":true,"configurable":true}
person getPrototypeOf result [object Object]
person has age result true
person isExtensible result true
person ownKeys result age,school,name
person getOwnPropertyDescriptor age result {"value":18,"writable":true,"enumerable":true,"configurable":true}
person getOwnPropertyDescriptor school result {"value":{"name":"清华大学","address":"北京市海淀区"},"writable":true,"enumerable":true,"configurable":true}
person getOwnPropertyDescriptor name result {"value":"张三","writable":true,"enumerable":true,"configurable":true}
person preventExtensions result true
深层 Proxy 代码
上述的代码有个问题,当把 person
对象改为如下代码时
js
let person = {
age: 18,
school: {
name: '清华大学',
address: '北京市海淀区'
}
};
在读取 person.school.name
属性的时候,只会输出 person get school result [object Object]
,拦截的不过精细,现在修改 get
方法如下
js
get: function (target, prop, receiver) {
let result = Reflect.get(target, prop, receiver);
console.log(`${name} get ${prop} result ${result}`);
// 如果是嵌套对象,这里需要进行递归操作
if (result instanceof Object) {
result = addProxy(result, `${name}.${prop}`);
}
return result;
},
这里进行了一个简单的判断,当获取的到的结果是 Object
对象时,再次添加代理,这样就形成了一个递归的效果,再次运行得出
person get school result [object Object]
person.school get name result 清华大学
代理失效问题
代理失效的原因有很多,会一直更新,目前已知如下
- 在浏览器环境中,由于某些对象不可复写(也就是不能被覆盖),那么代理会失效;
- 如果只使用了
get
拦截器,有些网站会使用Object.getOwnPropertyDescriptor
来获取描述符中的 value,会导致拦截不到;
常见问题
- 关于
Symbol
输出的问题,例如在get
方法中,Symbol
不能输出,可以通过Symbol.toString()
进行输出;