案例代码functionget_sign(){/*// 黑匣子省略很多代码 // (1) BOM和DOM正常的前端动作 window.addEventListener(test) // 搜索框 let kw document.getElementById(kw) let _class kw.getAttribute(class) // 创建画布 let canvas document.createElement(canvas) let ctx canvas.getContext(2d); ctx.fillRect(10, 10, 100, 100);*/// (2) 基于DOM和BOM进行环境校验/* if (navigator.toString() [object Navigator]) { let navLength navigator.userAgent.length return u82d1660a navLength // Yuan老师的微信想深入学逆向爬虫的联系我结一段善缘! } else { return false }*/}console.log(get_sign())上述代码let ctx canvas.getContext(“2d”);ctx.fillRect(10, 10, 100, 100);*/如果ctx没有后续操作是不用补的但是最终还是用的补充的js最终补环境答案代码window{addEventListener:function(){}}kw{getAttribute:function(){}}ctx{fillRect:function(){}}canvas{getContext:function(){returnctx}}document{getElementById:function(id){console.log(document getElementById:,id);// 看调用了什么标签名。以便缺啥补啥// 讲解if作用看下一步调用了什么标签以及这个标签有没有下一步调用。比如kw有下一步调用所以这里就要if。如果没有下一步调用这个if不加也没事if(idkw){//而且这个方法可能会被其他标签调用很多次一定要看具体缺什么标签然后if判断返回该标签// console.log(id,id)// console.log(typeof id)// console.log(typeof kw)returnkw// 这里就是返回kw标签object对象而id是string对象}},createElement:function(ele){console.log(document createElement:,ele)if(elecanvas){returncanvas}}}navigator{toString(){return[object Navigator];},userAgent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36}// 关键设置 Symbol.toStringTag让 Object.prototype.toString.call(navigator) 返回正确结果Object.defineProperty(navigator,Symbol.toStringTag,{value:Navigator,writable:false,enumerable:false,configurable:true});解析一Object.definePropertyObject.defineProperty(navigator,Symbol.toStringTag,{value:Navigator,writable:false,enumerable:false,configurable:true});A为什么需要如上代码的解释Q在node-js版本中18版本是不用这一段的。这段主要是因为node最新版本中具体查了AI之后才知道的就是我们补充navigator重写里面的toString方法return “[object Navigator]”。但是最新nodejs版本是这个navigator的toString是继承自Object对象的也就是AI所说的你当前的写法只重写了 navigator.toString() 方法但浏览器里的 Object.prototype.toString.call(navigator) 检测的是对象的 Symbol.toStringTag 内部标记不是直接调用你写的 toString()。这里不会详情查原型链知识点