网站开发电脑,东莞轨道公司,html 网站地图,专门做会议的网站补环境框架的核心问题与优化方案
最近在研究补环境框架的实现#xff0c;发现了一些有意思的东西。现有的框架虽然能用#xff0c;但代码量大得离谱。本文会深入分析现有方案的工作原理和致命缺陷#xff0c;最后提出一个基于V8魔改的优化思路。
一、现有框架怎么工作的
…补环境框架的核心问题与优化方案最近在研究补环境框架的实现发现了一些有意思的东西。现有的框架虽然能用但代码量大得离谱。本文会深入分析现有方案的工作原理和致命缺陷最后提出一个基于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.dispatchfunction(funcName,thisArg,args,defaultValue){constenvFunckhBox.envFuncs[funcName];returnenvFunc?envFunc.apply(thisArg,args):defaultValue;};toString 的坑用 Proxy 代理对象后toString会暴露问题// 真实浏览器Object.prototype.toString.call(navigator);// [object Navigator]// 被代理的对象constproxyNavnewProxy(navigator,{});Object.prototype.toString.call(proxyNav);// [object Object]网站的反爬代码会这么检测consttoStringObject.prototype.toString;if(toString.call(navigator)![object Navigator]){console.log(检测到异常环境);}框架的解决办法是重写Symbol.toStringTagObject.defineProperty(Navigator.prototype,Symbol.toStringTag,{value:Navigator,enumerable:false,configurable:true,writable:false});这个操作要为每个类都做一遍后面会看到这有多麻烦。webdriver 属性的处理navigator.webdriver是用来检测自动化工具的// Selenium/Puppeteer 环境navigator.webdrivertrue// 真实浏览器navigator.webdriverundefined// 网站检测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_getfunction(){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);}三万行代码就是这三件事的排列组合。问题2toString 保护的代价为了让对象看起来像原生对象需要做这些// 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层)当然还是有一些优化的点四万多行的基础代码可以用模板去从浏览器取但是又不能全取有的node自带的就没有必要去重写。写了还会导致原本node的方法失效。比如json,math,proxy等。另外优化的点是 分发器套用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零基础爬虫第一天