JavaScript补环境报错navigator is not defined几种解决方案
作者:周小董
这篇文章主要介绍了JavaScript补环境报错navigator is not defined的几种解决方案,文中通过代码介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
js补环境报错navigator is not defined
遇到 navigator is not defined 错误,通常是因为在 Node.js 环境中运行了浏览器端的代码。以下是几种解决方案:
1.使用 JSDOM(推荐)
const { JSDOM } = require('jsdom');
// 创建虚拟的浏览器环境
const dom = new JSDOM(`<!DOCTYPE html><html><body></body></html>`, {
url: 'https://example.com',
pretendToBeVisual: true,
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
});
// 将全局对象挂载到 global
global.window = dom.window;
global.document = window.document;
global.navigator = window.navigator;
global.location = window.location;
// 现在可以使用 navigator 了
console.log(navigator.userAgent);
console.log(navigator.platform);
2.简单的模拟补环境
如果只需要基本功能:
// 在代码开头添加这些全局变量
if (typeof global !== 'undefined') {
// Node.js 环境
global.navigator = {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
platform: 'Win32',
language: 'zh-CN',
languages: ['zh-CN', 'zh'],
cookieEnabled: true,
webdriver: false,
appVersion: '5.0 (Windows)',
vendor: '',
vendorSub: '',
product: 'Gecko',
productSub: '20030107',
hardwareConcurrency: 8,
// 常用属性
plugins: [],
mimeTypes: [],
// 常用方法
javaEnabled: () => false,
sendBeacon: () => true
};
}
3.使用 puppeteer-extra 插件补环境
安装相关包:
npm install puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// 设置 userAgent
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
// 执行代码
const result = await page.evaluate(() => {
// 这里可以安全使用 navigator
return {
userAgent: navigator.userAgent,
platform: navigator.platform
};
});
console.log(result);
await browser.close();
})();
4.完整的环境模拟类
创建一个环境模拟类:
class BrowserEnv {
constructor() {
this.setupGlobals();
}
setupGlobals() {
if (typeof global !== 'undefined') {
// 模拟 navigator
global.navigator = {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
platform: 'Win32',
language: 'zh-CN',
languages: ['zh-CN', 'zh'],
cookieEnabled: true,
webdriver: false,
appVersion: '5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
vendor: 'Google Inc.',
vendorSub: '',
product: 'Gecko',
productSub: '20030107',
hardwareConcurrency: 12,
maxTouchPoints: 0,
onLine: true,
// 模拟插件
plugins: {
length: 3,
[0]: { name: 'Chrome PDF Plugin' },
[1]: { name: 'Chrome PDF Viewer' },
[2]: { name: 'Native Client' }
},
// 方法
javaEnabled: function() { return false; },
sendBeacon: function() { return true; }
};
// 模拟 window
global.window = {
navigator: global.navigator,
document: {},
location: {
href: 'https://www.example.com',
protocol: 'https:',
host: 'www.example.com',
hostname: 'www.example.com',
port: '',
pathname: '/',
search: '',
hash: '',
origin: 'https://www.example.com'
},
screen: {
width: 1920,
height: 1080,
availWidth: 1920,
availHeight: 1040,
colorDepth: 24,
pixelDepth: 24
}
};
// 模拟 document
global.document = {
characterSet: 'UTF-8',
contentType: 'text/html',
documentElement: {
clientWidth: 1903,
clientHeight: 969
}
};
}
}
// 更新特定属性
setUserAgent(ua) {
if (global.navigator) {
global.navigator.userAgent = ua;
}
}
}
// 使用
new BrowserEnv();
console.log(navigator.userAgent);
5.使用 vm2 沙箱环境
const { VM } = require('vm2');
const vm = new VM({
sandbox: {
navigator: {
userAgent: 'Mozilla/5.0...',
platform: 'Win32'
},
window: {
navigator: this.navigator
}
}
});
// 在沙箱中运行代码
vm.run(`
console.log(navigator.userAgent);
// 你的代码
`);
使用建议
根据需求选择方案:
- 简单测试:用方案2
- 网页爬虫/自动化:用方案3
- 复杂环境模拟:用方案1或4
常见的需要补的属性:
// 这些属性常被网站检测 navigator.webdriver // 通常设为 false navigator.plugins.length // 插件数量 navigator.languages // 语言设置
调试技巧:
// 检查缺少的属性 try { // 你的代码 } catch (e) { console.log('缺少属性:', e.message); // 根据错误信息补充对应属性 }
选择哪种方案取决于你的具体需求。如果是做爬虫或逆向工程,建议使用 JSDOM 或 puppeteer 方案。
JS中怎么全局化一个函数
在 JavaScript 中,有多种方式可以全局化函数。以下是具体的方法:
1.在浏览器环境中
方法一:直接挂载到window对象
// 定义全局函数
window.myGlobalFunction = function() {
console.log('这是全局函数');
return 'Hello World';
};
// 直接调用(无需 window. 前缀)
myGlobalFunction();
方法二:使用var声明(旧方式)
// 在最外层使用 var 声明(不推荐在现代代码中使用)
var myGlobalFunction = function() {
console.log('全局函数');
};
方法三:隐式全局(不推荐)
// 没有使用 var/let/const 声明(严格模式下会报错)
function myGlobalFunction() {
console.log('隐式全局');
}
2.在 Node.js 环境中
方法一:挂载到global对象
// Node.js 环境中
global.myGlobalFunction = function() {
console.log('Node.js 全局函数');
return 'Node Global';
};
// 直接调用
myGlobalFunction();
方法二:使用globalThis(ES2020+)
// 跨环境兼容方案(浏览器和Node.js都适用)
globalThis.myGlobalFunction = function() {
console.log('使用 globalThis');
return 'Works everywhere';
};
// 调用
myGlobalFunction();
3.跨环境兼容的封装方法
方法一:检测环境并挂载
// 封装一个跨平台的全局函数设置器
function makeGlobal(funcName, func) {
if (typeof window !== 'undefined') {
// 浏览器环境
window[funcName] = func;
} else if (typeof global !== 'undefined') {
// Node.js 环境
global[funcName] = func;
} else if (typeof globalThis !== 'undefined') {
// 现代环境
globalThis[funcName] = func;
} else {
// 其他环境
this[funcName] = func;
}
}
// 使用
makeGlobal('sayHello', function(name) {
console.log(`Hello, ${name || 'World'}!`);
});
// 调用
sayHello('Alice');
方法二:自动检测并挂载
// 自动挂载到全局对象
(function(globalObject) {
// 定义你的函数
function calculateSum(a, b) {
return a + b;
}
function formatDate(date) {
return date.toISOString().split('T')[0];
}
// 挂载到全局
globalObject.calculateSum = calculateSum;
globalObject.formatDate = formatDate;
// 如果需要,也可以挂载多个
Object.assign(globalObject, {
utils: {
calculateSum,
formatDate,
multiply: (a, b) => a * b
}
});
})(
typeof global !== 'undefined' ? global :
typeof window !== 'undefined' ? window :
typeof globalThis !== 'undefined' ? globalThis :
this
);
// 调用
console.log(calculateSum(5, 3)); // 8
console.log(utils.multiply(4, 5)); // 20
4.模块化环境下的全局化
在 ES6 模块中:
// global-functions.js
const myFunctions = {
greet: function(name) {
return `Hello, ${name}!`;
},
add: function(a, b) {
return a + b;
}
};
// 挂载到全局
if (typeof window !== 'undefined') {
window.appFunctions = myFunctions;
} else if (typeof global !== 'undefined') {
global.appFunctions = myFunctions;
}
export default myFunctions;
5.创建全局命名空间(推荐)
为了避免污染全局命名空间,建议使用单一全局变量:
// 创建或使用现有的全局命名空间
if (!window.MyApp) {
window.MyApp = {};
}
// 或者使用 globalThis
if (!globalThis.MyApp) {
globalThis.MyApp = {};
}
// 向命名空间添加函数
MyApp.utils = {
formatCurrency: function(amount) {
return `$${amount.toFixed(2)}`;
},
generateId: function() {
return 'id-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
},
validateEmail: function(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
};
// 使用
console.log(MyApp.utils.formatCurrency(19.99));
console.log(MyApp.utils.validateEmail('test@example.com'));
6.动态加载全局函数
// 动态加载函数到全局
function loadGlobalFunctions(functions) {
const globalObj = globalThis || window || global;
Object.keys(functions).forEach(key => {
// 避免覆盖已有函数
if (!globalObj[key]) {
globalObj[key] = functions[key];
} else {
console.warn(`函数 ${key} 已存在,跳过`);
}
});
}
// 批量加载
loadGlobalFunctions({
debounce: function(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
},
throttle: function(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
});
// 使用
const search = debounce(function(query) {
console.log('搜索:', query);
}, 300);
最佳实践建议
- 避免污染全局命名空间:尽量使用命名空间模式
- 使用
globalThis:兼容性最好的方式 - 检查是否存在:避免覆盖已有函数
- 考虑使用模块系统:现代前端开发推荐使用 ES6 模块
// 最推荐的实践
(function() {
// 定义你的函数库
const myLib = {
// 各种工具函数
};
// 挂载到全局(如果需要)
if (typeof globalThis !== 'undefined') {
globalThis.myLib = myLib;
}
})();
JS 补环境报错TypeError: Cannot read properties of undefined (reading ‘back’)
这个错误通常发生在JavaScript逆向工程中,当你尝试补环境时,缺少了必要的对象或属性。'back' 属性通常是某些特定API的一部分。以下是常见的原因和解决方案:
常见原因及解决方案
1.缺失 window.history.back() 方法
// 添加 history.back 方法
if (!window.history.back) {
window.history.back = function() {
console.log('history.back() called');
// 可以返回一个Promise或具体值
return Promise.resolve();
};
}
2.缺失自定义对象或API的 back 方法
// 例如,对于某些SDK或API
if (window.someSDK && !window.someSDK.back) {
window.someSDK.back = function() {
console.log('someSDK.back() called');
// 根据实际需求实现逻辑
return {};
};
}
3.更完整的 history 对象补环境
// 完整的 history 对象补全
window.history = window.history || {};
window.history.back = window.history.back || function() {
console.log('[补环境] history.back() 被调用');
return undefined;
};
window.history.forward = window.history.forward || function() {
console.log('[补环境] history.forward() 被调用');
return undefined;
};
window.history.go = window.history.go || function(num) {
console.log(`[补环境] history.go(${num}) 被调用`);
return undefined;
};
// 其他 history 属性
Object.defineProperties(window.history, {
length: {
get: function() {
return 1; // 返回一个合理的值
},
configurable: true
},
state: {
get: function() {
return null;
},
configurable: true
}
});
4.使用 Proxy 拦截属性访问
// 全局拦截 undefined 属性的访问
window = new Proxy(window, {
get: function(target, property) {
console.log(`[补环境] 访问 window.${property.toString()}`);
// 如果属性不存在,返回一个函数或对象
if (!(property in target)) {
// 对于方法调用
if (property === 'back' || property.endsWith('back')) {
return function() {
console.log(`[补环境] ${property}() 被调用`);
return {};
};
}
// 返回一个空对象,避免后续访问出错
return {};
}
return target[property];
}
});
// 对特定对象使用 Proxy
window.someObject = new Proxy({}, {
get: function(target, property) {
console.log(`[补环境] 访问 someObject.${property.toString()}`);
if (!(property in target)) {
if (property === 'back') {
return function() {
console.log('[补环境] someObject.back() 被调用');
return {};
};
}
// 自动创建嵌套对象
target[property] = new Proxy({}, this);
return target[property];
}
return target[property];
}
});
5.完整的补环境框架示例
// 创建一个补环境配置对象
const envConfig = {
// 需要补的 API 列表
apis: {
'history.back': {
type: 'function',
return: undefined,
log: true
},
'someAPI.back': {
type: 'function',
return: { success: true },
log: true
},
// 可以添加更多需要补的 API
},
// 自动补环境的方法
patch: function() {
for (const [path, config] of Object.entries(this.apis)) {
const parts = path.split('.');
let obj = window;
// 创建嵌套对象结构
for (let i = 0; i < parts.length - 1; i++) {
if (!obj[parts[i]]) {
obj[parts[i]] = {};
}
obj = obj[parts[i]];
}
// 设置最终属性
const lastPart = parts[parts.length - 1];
if (!obj[lastPart]) {
if (config.type === 'function') {
obj[lastPart] = function() {
if (config.log) {
console.log(`[补环境] ${path}() 被调用`);
}
return config.return;
};
} else {
obj[lastPart] = config.value;
}
}
}
}
};
// 执行补环境
envConfig.patch();
6.调试技巧
// 启用详细调试
window.__debugEnv = true;
// 监控特定的属性访问
Object.defineProperty(window, 'someObject', {
get: function() {
console.trace('[补环境] window.someObject 被访问');
return {
back: function() {
console.log('[补环境] someObject.back() 被调用');
return {};
}
};
},
configurable: true
});
// 捕获所有错误
window.addEventListener('error', function(e) {
console.log('[补环境] 捕获错误:', e.message);
console.log('[补环境] 堆栈:', e.error.stack);
// 可以在这里动态补环境
});
实际案例分析
案例1:某网站的 history.back
// 网站实际调用了 history.back()
if (!window.history) window.history = {};
if (!window.history.back) {
window.history.back = function() {
// 记录调用,便于调试
console.log('[补环境] history.back() called at:', new Date().toISOString());
// 阻止页面实际后退,只模拟行为
return false;
};
}
案例2:某SDK的 back 方法
// 假设 SDK 对象是 window.sdk
if (!window.sdk) window.sdk = {};
if (!window.sdk.back) {
window.sdk.back = function(options) {
console.log('[补环境] sdk.back() called with:', options);
// 返回模拟的响应
return {
code: 0,
message: 'success',
data: {}
};
};
}
建议的调试步骤
先找出具体的调用栈:
// 在代码开头添加 Error.stackTraceLimit = 100; // 或者使用 try-catch 包裹可疑代码 try { // 你的代码 } catch (e) { console.log('错误堆栈:', e.stack); }逐步补环境:
- 先补 window 对象
- 再补 document
- 最后补具体的 API
使用现有的补环境框架:
- JSDOM:模拟完整的浏览器环境
- Puppeteer:使用真实浏览器环境
- vm2:安全的沙箱环境
JS补环境报错TypeError: Cannot read properties of undefined (reading ‘webdriver’)
这个错误是因为JavaScript代码检测 navigator.webdriver 属性时,该属性不存在。这是常见的反爬虫检测手段,很多网站通过这个属性来检测是否使用了自动化工具(如Selenium、Puppeteer)。
解决方案
1.最简方案 - 直接设置为 undefined 或 false
// 方案1:设置为 false(最常用)
Object.defineProperty(navigator, 'webdriver', {
get: () => false,
configurable: true
});
// 方案2:设置为 undefined(有些网站需要这样)
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined,
configurable: true
});
// 方案3:直接删除属性
delete navigator.webdriver;
2.完整的环境伪装方案
// 完整的 navigator 对象伪装
const originalNavigator = Object.getOwnPropertyDescriptor(window, 'navigator');
if (originalNavigator && originalNavigator.configurable) {
Object.defineProperty(window, 'navigator', {
value: new Proxy(navigator, {
get: function(target, property) {
// 拦截 webdriver 属性
if (property === 'webdriver') {
return false; // 或 undefined
}
// 拦截其他可能被检测的属性
if (property === 'languages') {
return ['zh-CN', 'zh', 'en'];
}
if (property === 'plugins') {
return {
length: 5,
[Symbol.iterator]: function*() {
yield { name: 'Chrome PDF Plugin' };
yield { name: 'Chrome PDF Viewer' };
yield { name: 'Native Client' };
}
};
}
// 返回原始属性
return target[property];
}
}),
configurable: true,
writable: true
});
}
3.针对不同场景的解决方案
场景1:普通网站
// 大部分网站只需要设置 webdriver 为 false
Object.defineProperty(navigator, 'webdriver', {
get: () => false
});
// 同时需要处理一些其他常见检测点
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
});
Object.defineProperty(navigator, 'languages', {
get: () => ['zh-CN', 'zh', 'en', 'en-US']
});
场景2:高级反爬网站(如某宝、某东)
// 更复杂的伪装方案
const fakeNavigator = {};
// 复制原始 navigator 的所有属性
for (const key in navigator) {
if (navigator.hasOwnProperty(key)) {
fakeNavigator[key] = navigator[key];
}
}
// 重写关键属性
fakeNavigator.webdriver = false;
fakeNavigator.plugins = [
{ name: 'Chrome PDF Plugin' },
{ name: 'Chrome PDF Viewer' },
{ name: 'Native Client' }
];
fakeNavigator.languages = ['zh-CN', 'zh', 'en'];
// 冻结对象,防止被修改检测
Object.defineProperty(window, 'navigator', {
value: Object.freeze(fakeNavigator),
configurable: false,
writable: false
});
4.使用 Proxy 拦截所有访问
// 创建代理 navigator
const navigatorProxy = new Proxy(navigator, {
get(target, property) {
// 处理 webdriver
if (property === 'webdriver') {
return false;
}
// 处理其他可能被检测的属性
const detectionProperties = [
'webdriver',
'plugins',
'languages',
'userAgent',
'platform',
'vendor',
'maxTouchPoints',
'hardwareConcurrency',
'deviceMemory'
];
if (detectionProperties.includes(property)) {
console.log(`[补环境] navigator.${property} 被访问`);
}
// 返回原始值
const value = target[property];
return typeof value === 'function' ? value.bind(target) : value;
}
});
// 替换 navigator
Object.defineProperty(window, 'navigator', {
value: navigatorProxy,
configurable: true
});
5.完整的反检测套件
class AntiDetection {
constructor() {
this.init();
}
init() {
this.patchNavigator();
this.patchWindow();
this.patchDocument();
}
patchNavigator() {
// webdriver 伪装
Object.defineProperty(navigator, 'webdriver', {
get: () => false
});
// Chrome 特征伪装
if (navigator.userAgent.includes('Chrome')) {
Object.defineProperties(navigator, {
plugins: {
get: () => {
return {
length: 3,
0: { name: 'Chrome PDF Plugin' },
1: { name: 'Chrome PDF Viewer' },
2: { name: 'Native Client' }
};
}
},
languages: {
get: () => ['zh-CN', 'zh', 'en']
},
hardwareConcurrency: {
get: () => 8 // 常见的CPU核心数
},
deviceMemory: {
get: () => 8 // 8GB内存
}
});
}
}
patchWindow() {
// 处理 window 上的检测
if (window.chrome) {
// 伪装 chrome 对象
window.chrome = {
runtime: {},
loadTimes: function() {},
csi: function() {},
app: {}
};
}
}
patchDocument() {
// 处理 document 上的检测
Object.defineProperty(document, 'hidden', { value: false });
Object.defineProperty(document, 'visibilityState', { value: 'visible' });
}
}
// 使用
new AntiDetection();
6.动态调试和检测
// 启用调试模式
const DEBUG = true;
// 监控所有 navigator 属性访问
const originalGet = Object.getOwnPropertyDescriptor;
Object.getOwnPropertyDescriptor = function(obj, prop) {
if (obj === navigator && DEBUG) {
console.log(`[检测] navigator.${prop} 被检测`);
}
return originalGet.call(this, obj, prop);
};
// 或者使用更加侵入式的方法
Object.defineProperty(navigator, 'webdriver', {
configurable: true,
get: function() {
if (DEBUG) {
console.trace('[检测] navigator.webdriver 被访问');
}
return false;
}
});
7.针对特定框架的解决方案
Puppeteer 用户:
// 在启动 puppeteer 时添加参数
const browser = await puppeteer.launch({
args: [
'--disable-blink-features=AutomationControlled'
]
});
// 然后在页面中执行
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => false
});
});
Selenium 用户:
// ChromeOptions 设置
ChromeOptions options = new ChromeOptions();
options.addArguments("--disable-blink-features=AutomationControlled");
// 或者使用 CDP
Map<String, Object> params = new HashMap<>();
params.put("source", "Object.defineProperty(navigator, 'webdriver', { get: () => false })");
driver.executeCdpCommand("Page.addScriptToEvaluateOnNewDocument", params);
8.检查是否还有其他检测点
// 运行这个脚本来检查常见的检测点
const detectionTests = [
'navigator.webdriver',
'window.chrome',
'navigator.plugins.length',
'navigator.languages',
'document.__driver_evaluate',
'document.__webdriver_evaluate',
'document.$cdc_asdjflasutopfhvcZLmcfl_',
'window.callPhantom',
'window._phantom',
'window.phantom'
];
detectionTests.forEach(test => {
try {
const result = eval(test);
console.log(`${test}:`, result);
} catch (e) {
console.log(`${test}: Error - ${e.message}`);
}
});
注意事项
- 执行时机很重要:必须在网站脚本执行之前就设置好这些属性
- 兼容性:不同浏览器可能需要不同的伪装策略
- 不要过度伪装:过于完美的伪装反而会被检测
- 动态更新:有些网站会多次检测,需要确保属性始终正确
总结
到此这篇关于JavaScript补环境报错navigator is not defined几种解决方案的文章就介绍到这了,更多相关js补环境报错navigator is not defined内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
