爬虫&逆向--Day10--Proxy代理机制

一、Proxy代理机制

1.1、概念

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

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

1.2、代理操作

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

//创建一个将要被代理的对象
var User = {
    username: "bobo",
    age: 20
}
//创建代理对象,Proxy的参数1:被代理对象。参数2:处理器
user_1 = new Proxy(User, {
    // target 被代理对象,其实就是User  user_1是代理对象
    // p 被代理对象的内部成员(属性)
    get(target, p) {
        console.log(`获取属性${p}操作`)
        //返回被代理对象的p属性值
        // Reflect.get用于设置对象属性值的机制
        return Reflect.get(target, p);

    },
    set(target, p, value) {
        console.log(`设置属性${p}操作`)
        //Reflect是一个用于设置对象属性值的机制
        Reflect.set(target, p, value);
    }
});

console.log(user_1.username);
/*
    获取属性username操作
    bobo
*/
console.log(user_1.age);
/*
    获取属性age操作
    20
*/
user_1.username = "Jay"
user_1.age = 18
console.log(user_1.username);
console.log(user_1.age);
/*
    设置属性username操作
    设置属性age操作
    获取属性username操作
    Jay
    获取属性age操作
    18
*/

 Reflect与直接赋值的区别:与直接使用target[p] = value赋值相比,Reflect.set提供了更明确的语义和更好的错误处理机制。

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

//创建一个将要被代理的对象
var User = {
    username: "bobo",
    age: 20
}
//创建代理对象,Proxy的参数1:被代理对象。参数2:处理器
user_1 = new Proxy(User, {
    // target 被代理对象,其实就是User  user_1是代理对象
    // p 被代理对象的内部成员(属性)
    get(target, p) {
        console.log(`获取属性${p}操作`)
        //返回被代理对象的p属性值
        // Reflect.get用于设置对象属性值的机制
        return Reflect.get(target, p);

    },
    set(target, p, value) {
        console.log(`设置属性${p}操作`)
        //Reflect是一个用于设置对象属性值的机制
        Reflect.set(target, p, value);
    }
});

console.log(user_1.username);
console.log(user_1.age);
console.log(user_1.address);
/*
    获取属性username操作
    bobo
    获取属性age操作    
    20
    获取属性address操作
    undefined
*/

 输出结果为:

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

1.3、属性描述符

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

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

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

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

user_1 = new Proxy(User, {
    get(target, p) {
        console.log(`获取属性${p}操作`)
        return Reflect.get(target, p);
    },
    set(target, p, value) {
        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_1.username);   // 通过普通的方式获取被代理对象的值
/*
    获取属性username操作
    bobo
*/

 user_1.age = 30;                // 通过普通的方式设置被代理对象的值
 console.log(user_1.age);
/*
    设置属性age操作
    获取属性age操作
    30
*/
console.log(Object.getOwnPropertyDescriptor(user_1, "address").value);
/*
    通过属性描述符获取属性address操作
    Bj
*/
  • defineProperty对象属性的定义

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

// 通过属性描述符给被代理对象User对象定义一个sex属性
Object.defineProperty(user_1, 'sex', {
      value: 'man',
      writable: true,       // 允许修改属性值
      enumerable: true,     // 允许枚举属性
      configurable: true    // 允许删除或修改属性描述符
    }
);

// 通过属性描述符设置属性sex操作

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

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

user_1 = new Proxy(User, {
    get(target, p) {
        console.log(`获取属性${p}操作`)
        return Reflect.get(target, p);
    },
    set(target, p, value) {
        console.log(`设置属性${p}操作`)
        Reflect.set(target, p, value);
    },
    getOwnPropertyDescriptor(target, p){
        let result;
        result = Reflect.getOwnPropertyDescriptor(target, p);
        console.log(`通过属性描述符获取属性${p}操作`)
        return result;
    },
    defineProperty(target, p, attributes) {
        console.log(`通过属性描述符设置属性${p}操作`)
        let result;
        result = Reflect.defineProperty(target, p, attributes);
        return result;
    }
    /*
        attributes参数通常与属性描述符(Property Descriptor)相关。属性描述符是一个对象,它定义了属性的特性,
        如是否可写、可枚举、可配置等。在defineProperty方法中,attributes参数就是用来指定这些特性的。
        或者简单讲,attributes是一个对象,它包含了要定义或修改的属性的特性
    */
});

//测试
console.log(user_1.username);   // 通过普通的方式获取被代理对象的值
/*
    获取属性username操作
    bobo
*/

 user_1.age = 30;                // 通过普通的方式设置被代理对象的值
 console.log(user_1.age);
/*
    设置属性age操作
    获取属性age操作
    30
*/
console.log(Object.getOwnPropertyDescriptor(user_1, "address").value);
/*
    通过属性描述符获取属性address操作
    Bj
*/

// 通过属性描述符给被代理对象User对象定义一个sex属性
Object.defineProperty(user_1, 'sex', {
      value: 'man',
      writable: true,       // 允许修改属性值
      enumerable: true,     // 允许枚举属性
      configurable: true    // 允许删除或修改属性描述符
    }
);

// 通过属性描述符设置属性sex操作

/*
     因此后期在进行代理操作的时候,以上的四个函数都必须全部写出来,因为不确定JS代码是
     通过常规的方式设置的还是通过属性描述符设置的
*/

1.4、函数调用拦截监控

拦截监控指定函数的调用

Add = function(a,b){
    console.log("Add函数正在被调用");
    return a+b;
}
add = new Proxy(Add, {
    /*
        target:被代理对象
        thisArg:是一个函数指针,就等同于this
        argList:函数的参数 a b
    */

    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);

/*
    Add函数正在被调用        
    Add函数被调用,参数为:1,2
*/

1.5、对象构造方法拦截监控

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

//
function Animal() {
    this.name = "mini";
    this.age = 20
}

//对象被创建的监控:如何监控对象被创建的行为
// 创建一个代理对象:专门监控被代理对象是否被实例化
Animal_proxy = new Proxy(Animal, {
        // target, 被代理对象
        // argArray 参数列表
        // newTarget 可选参数,不写也可以
        // construct:function (target, argArray, newTarget) {
        construct: function (target, argArray) {
            console.log('对象被创建');
            console.log(`${target.name}函数被调用,参数为:${argArray}`);
            let ret = Reflect.construct(target, argArray);
            return ret;
        }
    }
)

// 实例化一个对象:调用对象的构造方法
// animal = new Animal()
animal = new Animal_proxy()

/*
    对象被创建
    Animal函数被调用,参数为:
*/
// 扩展一、
function People(name, age) {
    this.name = name; //this表示对象的调用者
    this.age = age;
    this.chi = function () {
        console.log(this.name, "在吃东西")
    }
}

people = new Proxy(People, {
    get(target, p) {
        console.log(`获取属性${p}操作`)
        return Reflect.get(target, p);
    },
    set(target, p, value) {
        console.log(`设置属性${p}操作`)
        //Reflect是一个用于设置对象属性值的机制
        Reflect.set(target, p, value);
    },
    apply: function (target, thisArg, argList) {
        let result = Reflect.apply(target, thisArg, argList);
        console.log(`${target.name}函数被调用,参数为:${argList}`);
        return result;
    },
    construct: function (target, argArray, newTarget) {
        let result = Reflect.construct(target, argArray, newTarget);
        console.log(`${target.name}函数被调用--1,参数为:${argArray}`);
        return result;
    }
})

// p1 = new People("alex", 18);
// p1.chi();
p2 = new people("wusir", 18);
p2.chi();
/*
    Constructor names usually start with an uppercase letter. A typo?
    构造函数名称通常以大写字母开头。拼写错误?
    
    获取属性prototype操作
    People函数被调用--1,参数为:wusir,18
    wusir 在吃东西
*/
//扩展二、
var Person = {
                name : "alvin",
                age : 18,
                say: function(){
                    console.log("say---被打印了")
                  }
             };

// console.log(Person.say())

people = new Proxy(Person, {
    get(target, p) {
        console.log(`获取属性${p}操作`)
        return Reflect.get(target, p);
    },
    set(target, p, value) {
        console.log(`设置属性${p}操作`)
        //Reflect是一个用于设置对象属性值的机制
        Reflect.set(target, p, value);
    },
    apply: function (target, thisArg, argList) {
        let result = Reflect.apply(target, thisArg, argList);
        console.log(`${target.name}函数被调用,参数为:${argList}`);
        return result;
    },
    construct: function (target, argArray, newTarget) {
        let result = Reflect.construct(target, argArray, newTarget);
        console.log(`${target.name}函数被调用--1,参数为:${argArray}`);
        return result;
    }
})


console.log(people.say())
/*
    获取属性say操作
    say---被打印了
    undefined 
*/

 

posted @ 2025-07-11 17:36  L遇上J  阅读(79)  评论(0)    收藏  举报