Skip to content
On this page

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 清华大学

代理失效问题

代理失效的原因有很多,会一直更新,目前已知如下

  1. 在浏览器环境中,由于某些对象不可复写(也就是不能被覆盖),那么代理会失效;
  2. 如果只使用了 get 拦截器,有些网站会使用 Object.getOwnPropertyDescriptor 来获取描述符中的 value,会导致拦截不到;

常见问题

  1. 关于 Symbol 输出的问题,例如在 get 方法中,Symbol 不能输出,可以通过 Symbol.toString() 进行输出;

总结

Released under the MIT License.