proxy 代理机制

概念

JavaScript中的Proxy是一种内置对象,它允许你在访问或操作对象之前拦截和自定义底层操作的行为。通过使用Proxy,你可以修改对象的默认行为,添加额外的逻辑或进行验证,以实现更高级的操作和控制。

Proxy对象包装了另一个对象(目标对象),并允许你定义一个处理程序(handler)来拦截对目标对象的操作。处理程序是一个带有特定方法的对象,这些方法被称为"捕获器"(traps),它们会在执行相应的操作时被调用。

代理操作

  • 给User对象设置代理,监控该对象 “已有/存在” 属性值的相关操作

// 创建user对象
var User = {
    username: "Bob",
    age: 20
}
// 创建代理对象,Proxy的参数1:被代理对象。参数2:处理器
User = new Proxy(User, {
    // target:被代理对象。p:属性。receiver:代理后的对象就是User
    get(target, p, receiver) {
        console.log(`获取属性${p}操作`)
        //返回代理对象的p属性值
        return Reflect.get(target, p);
    },
    set(target, p, value, receiver) {
        console.log(`设置属性${p}操作`)
        Reflect.set(target, p, value);
    }
});

console.log(User.username);
console.log(User.age);
User.username = "Jay"
User.age = 18
console.log(User.username);
console.log(User.age);
  • 给User对象设置代理,监控该对象 “不存在” 属性值的相关操作

// 创建user对象
var User = {
    username: "Bob",
    age: 20
}

User = new Proxy(User, {
    get(target, p, receiver) {
        console.log(`获取属性${p}操作`)
        return Reflect.get(target, p);
    },
    set(target, p, value, receiver) {
        console.log(`设置属性${p}操作`)
        Reflect.set(target, p, value);
    }
});

console.log(User.username);
console.log(User.age);
console.log(User.address);

输出结果为:

说明该对象中不存在address这个属性值,则需要给该对象进行address属性的补充。同理可以作用在逆向的补环境中,例如:对window对象进行代理时,发现window的xxx属性返回的是空,则需要在逆向js代码中的window对象中补上xxx属性即可。

属性描述符

  • getOwnPropertyDescriptor对象属性的获取

有些时候,一些网站的js中是通过属性描述符的方式获取一个对象的属性值。如下所示:

var Stu = {
    "name": "小明"
};
//通过属性描述符获取Stu对象中的name属性值
console.log(Object.getOwnPropertyDescriptor(Stu, "name").value);

那么,这个时候我们该如何通过代理去监控通过属性描述符对属性进行的操作行为呢?(拦截获取属性描述符)

// 创建user对象
var User = {
    username: "Bob",
    age: 20,
    address: "Bj"
}

User = new Proxy(User, {
    get(target, p, receiver) {
        console.log(`获取属性${p}操作`)
        return Reflect.get(target, p);
    },
    set(target, p, value, receiver) {
        console.log(`设置属性${p}操作`)
        Reflect.set(target, p, value);
    },
    getOwnPropertyDescriptor(target, p) {
        let result;
        result = Reflect.getOwnPropertyDescriptor(target, p);
        console.log(`通过属性描述符获取属性${p}操作`)
        return result;
    }
});

// 测试
console.log(User.username);
User.age = 30;
console.log(Object.getOwnPropertyDescriptor(User, "address").value);
  • defineProperty对象属性的定义

有些时候,一些网站的js中是通过属性描述符的方式对属性值的定义。如下所示:

var Person = {};

// 通过属性描述符给Person对象定义一个name属性
Object.defineProperty(Person, 'name', {
        value: 'Bob',
        writable: true, // 允许修改属性值
        enumerable: true, // 允许枚举属性
        configurable: true // 允许删除或修改属性描述符
    }
);
console.log(Person)

那么,这个时候我们该如何通过代理去监控通过属性描述符对属性进行的定义行为呢?(拦截属性定义)

var Person = {};

Person = new Proxy(Person, {
    get(target, p, receiver) {
        console.log(`获取属性${p}操作`)
        return Reflect.get(target, p);
    },
    set(target, p, value, receiver) {
        console.log(`设置属性${p}操作`)
        Reflect.set(target, p, value);
    },
    getOwnPropertyDescriptor(target, p) {
        let result;
        result = Reflect.getOwnPropertyDescriptor(target, p);
        console.log(`通过属性描述符获取属性${p}操作`)
        return result;
    },
    defineProperty: function (target, p, descriptor) {
        console.log(`通过属性描述符设置属性${p}操作`)
        let result;
        result = Reflect.defineProperty(target, p, descriptor);
        return result;
    }

});

// 通过属性描述符给Person对象定义一个name属性
Object.defineProperty(Person, 'name', {
        value: 'Bob',
        writable: true, // 允许修改属性值
        enumerable: true, // 允许枚举属性
        configurable: true // 允许删除或修改属性描述符
    }
);
console.log(Person)

函数调用拦截监控

拦截监控指定函数的调用

add = function (a, b) {
    console.log("add函数正在被调用");
    return a + b;
}
add = new Proxy(add, {
    apply: function (target, thisArg, argList) {
        // target: 函数对象。thisArg: 调用函数的this指针。argList:函数参数数组
        let result;
        result = Reflect.apply(target, thisArg, argList);
        console.log(`${target.name}函数被调用,参数为:${argList}`);
        return result;
    }
});
add(1, 2);

对象构造方法拦截监控

拦截new关键字。基于new创建的对象就是在调用该对象的构造方法。

function Animal() {
}

Animal = new Proxy(Animal, {
    construct: function (target, argArray, newTarget) {
        //target: 函数对象。argArray: 参数列表。newTarget: 代理后的对象
        let result = Reflect.construct(target, argArray, newTarget);

        console.log(`${target.name}对象被创建`);
        return result;
    }
});
animal = new Animal();

Last updated