深究补环境

前言

补环境就是浏览器上的js代码在我们本地nodejs环境里直接运行运行不了,那么我们就要补充浏览器的webAPI,使得代码可以运行,这个过程就是补环境。

环境代理

通过 Proxy 对象实现对浏览器对象的代理,可以监控和记录所有属性的访问和修改操作。代理深度决定了环境模拟的精细程度,包括:

  • 一级代理:只代理顶层对象(如 document, window, navigator)
  • 多级代理:递归代理嵌套对象(如 document.documentElement, navigator.plugins)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function wacth(obj, name){
    return new Proxy(obj, {
    get(target, p, receiver){
    if (p === "Math" || p === "isNaN" || p === "encodeURI" || p === "Uint8Array" || p.toString().indexOf("Symbol(Symbol.") != -1){
    let val = Reflect.get(...arguments);
    return val
    } else {
    let val = Reflect.get(...arguments);
    console.log(`取值:`,name, '.', p, ` =>`, val);
    return val
    }
    },
    set(target, p, value, receiver){
    let val = Reflect.get(...arguments);
    console.log(`设置值:${name}.${p}, ${val} => ${value}`);
    return Reflect.set(...arguments)
    }
    })
    }
    window = wacth(window, "window");
    这个就是给window对象代理,然后就可以在控制台查看到所有访问和取值操作了。

模拟浏览器对象&&深度代理


我们以document对象为例,

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
document={
all:(function(){
const collection = [];
collection.item = function(index) {
console.log("调用方法:item", "对象:document.all", "参数:", index);
return this[index] || null;
};
collection.namedItem = function(name) {
console.log("调用方法:namedItem", "对象:document.all", "参数:", name);
return null;
};
collection.length = 0;
return collection;
})(),
createElement:function(arg1,arg2){
console.log("调用方法:createElement","对象:document","参数1:",arg1);
if(arg1==="span"){
return wacth({
classList:[]
},"document.createElement.span")
}
if(arg1==="canvas"){
return wacth({
getContext:function(arg1,arg2){
console.log("调用方法:getContext","对象:canvas","参数1:",arg1,"参数2:",arg2);
}
},"document.createElement.canvas")
}
},
createEvent:function(arg1,arg2){
console.log("调用方法:createEvent","对象:document","参数1:",arg1,"参数2:",arg2);
},
documentElement:wacth({
getAttribute:function(arg1,arg2){
console.log("调用方法:getAttribute","对象:document.documentElement","参数1:",arg1,"参数2:",arg2);
if(arg1==="webgl"){
return wacth({
getContext:function(arg1,arg2){
console.log("调用方法:getContext","对象:webgl","参数1:",arg1,"参数2:",arg2);
}
},"document.documentElement.getAttribute.webgl")
}
}
},"document.documentElement.getAttribute"),
cookie:'abRequestId=dd99ef57-7946-5910-8855-5288a027ee6b; xsecappid=xhs-pc-web; a1=196ce57b268q9gnxa31qo24mnz813ii8tq00m2spq50000330382; webId=a6ef2df7b446685f919cace4c60567a3; gid=yjKSd2WSdY9WyjKSd2WDJAJMKYAjkIC0qyAl8UAJ46IuY828yq33Y6888qq8qYJ8888d880d; webBuild=4.63.0; loadts=1748249829168; websectiga=8886be45f388a1ee7bf611a69f3e174cae48f1ea02c0f8ec3256031b8be9c7ee; sec_poison_id=d50dd0f5-a579-4e2d-985f-ab4a461b99b2; unread={%22ub%22:%226418197a0000000007038e44%22%2C%22ue%22:%226405d6930000000013003e52%22%2C%22uc%22:15}',
querySelectorAll:function(arg1,arg2){
console.log("调用方法:querySelectorAll","对象:document","参数1:",arg1,"参数2:",arg2);
if(arg1==="*"){
return []
}
}

};

模拟document对象,并添加了代理,这样我们就可以查看所有访问和取值操作了,在取值和对象创建时,我们可以添加代理,这样我们就可以查看所有访问和取值操作并实现深度代理的目的从而可以更好的补充原型链,但document.all这个有大学问我们以后重点讲下

代码运行

我们就以小红书,腾讯,字节的代码为例这些代码都是有VMP的
小红书:

腾讯:

字节: