当前位置: 首页 > news >正文

【补环境框架】序

补环境框架的核心问题与优化方案

最近在研究补环境框架的实现,发现了一些有意思的东西。现有的框架虽然能用,但代码量大得离谱。本文会深入分析现有方案的工作原理和致命缺陷,最后提出一个基于V8魔改的优化思路。

一、现有框架怎么工作的

调用链路分析

拿navigator.webdriver举例,看看一次属性访问要经过多少层:

// 用户代码console.log(navigator.webdriver);// 实际执行路径navigator// 1. 访问对象→ Proxy.get handler// 2. 代理拦截→ Object.getOwnPropertyDescriptor().get// 3. 属性 getter→dispatch("Navigator_webdriver_get")// 4. 分发路由→ envFuncs.Navigator_webdriver_get()// 5. 环境函数→ jsdomNavigator.webdriver(这是个demo 不从jsdom取)// 6. 真实对象→returnundefined// 7. 返回结果

整个链路的核心是dispatch函数,它就像一个路由器,把所有 API 调用转发到对应的处理函数。

khBox.toolsFunc.dispatch=function(funcName,thisArg,args,defaultValue){constenvFunc=khBox.envFuncs[funcName];returnenvFunc?envFunc.apply(thisArg,args):defaultValue;};

toString 的坑

用 Proxy 代理对象后,toString会暴露问题:

// 真实浏览器Object.prototype.toString.call(navigator);// "[object Navigator]"// 被代理的对象constproxyNav=newProxy(navigator,{});Object.prototype.toString.call(proxyNav);// "[object Object]"

网站的反爬代码会这么检测:

consttoString=Object.prototype.toString;if(toString.call(navigator)!=='[object Navigator]'){console.log('检测到异常环境!');}

框架的解决办法是重写Symbol.toStringTag:

Object.defineProperty(Navigator.prototype,Symbol.toStringTag,{value:'Navigator',enumerable:false,configurable:true,writable:false});

这个操作要为每个类都做一遍,后面会看到这有多麻烦。

webdriver 属性的处理

navigator.webdriver是用来检测自动化工具的:

// Selenium/Puppeteer 环境navigator.webdriver===true// 真实浏览器navigator.webdriver===undefined// 网站检测if(navigator.webdriver){alert('检测到机器人!');}

框架的处理分三步:

// Step 1: 定义属性描述符Object.defineProperty(Navigator.prototype,'webdriver',{configurable:true,enumerable:true,get:function(){returnkhBox.toolsFunc.dispatch("Navigator_webdriver_get",this,arguments,undefined);}});// Step 2: 实现环境函数khBox.envFuncs.Navigator_webdriver_get=function(){console.log('{khBox|dispatch} -> Navigator_webdriver_get');returnkhBox.memory.jsdomNavigator.webdriver;};// Step 3: 用户访问时自动经过这两层console.log(navigator.webdriver);// undefined

二、现有框架的问题

问题1:暴力穷举所有API

以AnalyserNode.js这个文件为例:

// 定义 fftSize 属性khBox.toolsFunc.defineProperty(AnalyserNode.prototype,"fftSize",{configurable:true,enumerable:true,get:function(){returnkhBox.toolsFunc.dispatch("AnalyserNode_fftSize_get",this,arguments,undefined);},set:function(){returnkhBox.toolsFunc.dispatch("AnalyserNode_fftSize_set",this,arguments);}});// 定义 frequencyBinCount 属性khBox.toolsFunc.defineProperty(AnalyserNode.prototype,"frequencyBinCount",{configurable:true,enumerable:true,get:function(){returnkhBox.toolsFunc.dispatch("AnalyserNode_frequencyBinCount_get",this,arguments,undefined);}});// ... 还有 minDecibels、maxDecibels、smoothingTimeConstant// ... 还有 getByteFrequencyData、getFloatFrequencyData// ... 总共定义了十几个属性和方法

AnalyserNode只是 Web API 中的一个小角色,整个项目的规模:
目前的框架 ,加起来四万多行 。。。

关键是这些代码都是重复的模板:

// 模板 A:属性 getterget:function(){returnkhBox.toolsFunc.dispatch("ClassName_propName_get",this,arguments,undefined);}// 模板 B:属性 setterset:function(){returnkhBox.toolsFunc.dispatch("ClassName_propName_set",this,arguments);}// 模板 C:方法调用value:function(){returnkhBox.toolsFunc.dispatch("ClassName_methodName",this,arguments);}

三万行代码,就是这三件事的排列组合。

问题2:toString 保护的代价

为了让对象看起来像原生对象,需要做这些:

// 1. 设置 toStringTagObject.defineProperty(Navigator.prototype,Symbol.toStringTag,{value:'Navigator',enumerable:false,configurable:true,writable:false});// 2. 重写 toStringObject.defineProperty(Navigator.prototype,'toString',{value:function(){return'[object Navigator]';},writable:false,enumerable:false,configurable:false});// 3. 重写 valueOfObject.defineProperty(Navigator.prototype,'valueOf',{value:function(){returnthis;},writable:false,enumerable:false});// 4. 修正 constructorObject.defineProperty(Navigator.prototype,'constructor',{value:Navigator,writable:false,enumerable:false,configurable:false});//这里也能优化一点点 ,也就是一点点,不都直接写在具体的函数上。 重写defineProperty 。

每个类都要写一遍这些防御性代码。

问题3:性能损失

每次属性访问要经过六层:

用户代码 → Proxy.get (第1层) → defineProperty (第2层) → getter 函数 (第3层) → dispatch (第4层) → envFunc (第5层) → 真实对象 (第6层)

当然,还是有一些优化的点

  1. 四万多行的基础代码可以用模板去从浏览器取,但是又不能全取,有的node自带的,就没有必要去重写。写了还会导致原本node的方法失效。比如json,math,proxy等。
  2. 另外优化的点是 分发器套用domino ,或者jsdom ,能省一些代码,但是要做映射。不用自己全补。

参考 https://www.bilibili.com/video/BV19dSCBqEFe/

以及github https://github.com/xuxiaobo-bobo/boda_jsEnv

三、有没有优化的思路

有。ai给了一个思路,从v8开始改。将这些放c层。
实现路线图:
第一步:理解 V8 属性访问机制
V8 如何处理 obj.prop 这样的属性访问?涉及哪些 C++ 函数?如何在不破坏原有逻辑的情况下插入 Hook?
第二步:添加 Hook 注册接口
在 V8 isolate 中添加 Hook 函数的存储和调用机制。如何从 C++ 调用 JavaScript 函数?如何处理参数传递和返回值?
第三步:修改属性访问流程
在 Runtime_GetProperty、Runtime_SetProperty 等关键函数中插入 Hook 调用点。如何保证性能?如何处理异常?
第四步:暴露 Node.js API
在 Node.js 层面暴露 v8.registerPropertyHook() 接口,让 JavaScript 代码可以注册 Hook 函数。
第五步:实现 Native 对象标记
让 V8 自动识别 DOM/BOM 对象,为 toString、instanceof 等操作提供正确的行为。
第六步:优化与测试
性能测试、边界情况处理、与现有代码的兼容性测试。

emm

由于空缺比较大,会从c++基础开始,同时夹杂一些其他内容,先开一个坑,慢慢填

更多文章,敬请关注gzh:零基础爬虫第一天

http://www.cnnetsun.cn/news/74561.html

相关文章:

  • Vue-next-admin终极指南:打造现代化后台管理系统的完整解决方案
  • vgmstream终极指南:游戏音频转换与播放全攻略
  • vgmstream音频解码神器:游戏音频格式转换终极指南
  • 印象大红袍通过上市聆讯:上半年营收5588万 利润678万
  • 蔡东青创办的奥动冲刺港股:靠换电半年营收3.2亿同比降32% 亏1.57亿 蔚来资本是股东
  • 基于Next.js的LobeChat为何成为GitHub星标项目?技术架构全拆解
  • 广合科技冲刺港股:前9个月营收38亿净利7亿 刚斥资3.2亿买楼
  • Vue Signature Pad电子签名组件完整使用指南:快速集成步骤与实用配置技巧
  • Joy-Con Toolkit终极指南:如何轻松自定义你的游戏手柄
  • 暗黑破坏神II存档编辑器:多版本兼容的角色定制解决方案
  • [自动化办公] 【Python】【低代码】在明道云中实现无需 pandas 的数据聚合与 HTML 表格生成技巧
  • OpenFace完整指南:从零开始掌握面部行为分析终极工具
  • Python协同过滤算法 音乐推荐系统_731w447o 论坛 浏览历史
  • 17、图像深度处理技术:从理论到实践
  • GitHub项目Star暴涨:Stable Diffusion 3.5 FP8成AIGC开发者新宠
  • Windows显示器亮度控制终极方案:Twinkle Tray完整使用手册
  • VideoDownloadHelper 终极使用指南:轻松下载网页视频资源
  • 高效STL转STEP工具:3D格式转换的完整解决方案
  • throw new Exception 如何指定返回code
  • 如何利用Cangaroo开源工具高效解决CAN总线开发难题
  • 百度网盘秒传脚本完全指南:快速上手极速生成功能
  • 芯片价格战:成本才是王道
  • layerdivider:AI图像分层革命,让设计效率飙升10倍
  • 收到工资1002415.13元,爱你华为。
  • Windows 11精简终极教程:三步打造高性能轻量系统
  • 全面解锁Honey Select 2游戏潜能的200+插件整合方案
  • ComfyUI与ONNX Runtime集成:跨框架模型支持
  • WebSite-Downloader 网站下载器终极指南:5分钟快速上手完整教程
  • 什么是公有云
  • PHP 8.6错误码实战手册:从E_ERROR到自定义Throwable的全面掌控