要让属性检查器在旧浏览器中可靠运行,核心是避开现代api,仅用最保守的dom方法和特性检测:使用var、for...in+hasownproperty组合,手动过滤原型属性,严格区分原生与宿主对象,拦截危险属性,try...catch包裹访问,输出采用制表符分隔的纯文本格式。

要让属性检查器在所有旧浏览器(比如 IE6–IE8、Firefox 2–3、Chrome 早期版本)中可靠运行,核心不是“兼容所有”,而是避开现代 API,用最保守的 DOM 方法 + 特性检测代替语法糖和假设。旧浏览器不支持 Object.getOwnPropertyDescriptor、Reflect.has、in 操作符对原型链的精确控制,甚至对 hasOwnProperty 的实现也有差异。
只用原生、无依赖的 DOM 基础方法
避免任何 ES5+ 语法(如 let、箭头函数、解构)、不调用 Object.keys() 或 for...of。全部用 var、for...in + hasOwnProperty 组合,并手动过滤原型属性:
- 用
typeof obj === 'object' && obj !== null判断可遍历对象,避开 null 和原始值 - 遍历前先用
obj.constructor === Object或{}.toString.call(obj) === '[object Object]'区分普通对象和宿主对象(如document、XMLHttpRequest),后者属性行为不可靠 - 对每个键,用
obj.hasOwnProperty(prop)判定是否为自有属性;若返回false,再用prop in obj确认是否来自原型链——这是 IE6–8 中最稳妥的组合
绕过不一致的 getter/setter 支持
IE8 及更早版本完全不支持 accessor 属性(get/set),且 Object.prototype.propertyIsEnumerable 在某些宿主对象上会报错。因此:
- 绝不尝试读取或调用疑似 getter 的属性(如
offsetWidth、scrollTop),避免触发意外副作用或脚本错误 - 对已知危险属性(如
innerHTML、style、className)做白名单拦截,仅列出名称,不尝试获取值 - 用
try...catch包裹每次属性访问,捕获TypeError或Permission denied(常见于跨域 iframe)
区分宿主对象与原生对象
旧浏览器中,document、window、location 等宿主对象的属性枚举行为极不稳定(例如 IE6 中 for...in window 会漏掉很多属性)。所以:
- 预先定义常见宿主对象的“安全属性表”,比如
{ window: ['location', 'document', 'navigator', 'screen'], document: ['body', 'title', 'URL'] } - 对非 Object 实例(如
NodeList、HTMLCollection)直接跳过枚举,只返回length和索引项(用0 in obj检查是否存在) - 遇到
function类型对象时,不展开其内部属性(旧版 Opera 和 Safari 对Function.prototype枚举结果混乱)
输出格式保持简单、可降级
不依赖 JSON.stringify(IE7 不支持)、不使用 innerHTML 插入结构化 HTML(旧浏览器解析易出错)。采用纯文本表格模拟:
- 每行用
\n分隔,字段用制表符\t对齐(比空格更稳定) - 属性名统一转为字符串(
String(prop)),避免undefined或null导致拼接失败 - 值统一用
'' + val转字符串,不调用val.toString()(防止自定义toString报错)
不复杂但容易忽略:真正的兼容性不在“写得多”,而在“不敢写什么”。删掉所有看似方便但旧环境未定义的行为,靠最小交集守住底线。











