应分层验证属性可用性而非仅检查存在性:先确认类型为函数且非null/undefined,再判断是否为原生构造函数,最后可选轻量实例测试;需封装复用函数并配合降级策略保障体验。

直接检查属性是否存在,关键不是“有没有”,而是“能不能用”——尤其在跨环境、多框架、老旧浏览器或沙箱运行时,typeof obj.prop === 'undefined' 这类简单判断容易误判。
一、属性存在性 ≠ 可用性
很多场景下,属性看似存在,实则不可调用或被污染:
- 测试环境或某些 WebView 中,
window.EventSource可能被设为null或{}占位对象 - 某些安全策略(如 CSP)会屏蔽原生 API,但不删除属性名
- TypeScript 编译后或打包工具注入的 polyfill 可能覆盖原生构造函数,导致行为不一致
所以不能只查 in 或 !== undefined,必须验证其类型 + 可调用性 + 基本行为一致性。
二、分层验证:从静态到轻量运行时
建议按需组合以下三步,避免过度检测影响首屏性能:
-
✅ 基础层(必做):确认是函数且非 null/undefined
if (typeof window.EventSource !== 'function') return false;
-
✅ 增强层(推荐):检查是否为原生构造函数(排除 mock 或 wrapper)
const isNative = EventSource.toString().startsWith('function EventSource'); -
✅ 可选层(谨慎使用):创建一个最小化实例并立即关闭
try { const src = new EventSource('data:text/plain,'); // 使用 data URL 避免真实请求 src.close(); return true; } catch { return false; }
⚠️ 注意:不要用
new EventSource('')或无效 URL 触发错误事件,可能干扰监控或触发重试逻辑;data:协议更安全可控。
三、封装成可复用的检查函数
function supportsProperty(obj, key, type = 'function') {
const value = obj[key];
if (value == null) return false;
if (typeof value !== type) return false;
if (type === 'function' && !value.prototype) return false; // 排除箭头函数等伪构造器
return true;
}
// 使用示例
if (supportsProperty(window, 'EventSource')) { /* 初始化 SSE */ }
if (supportsProperty(navigator, 'clipboard', 'object')) { /* 启用复制功能 */ }这种写法把“检查逻辑”和“业务逻辑”解耦,也便于单元测试模拟不同环境。
四、配合降级策略才真正健壮
光知道“不支持”没用,要立刻切换路径:
- 不支持
EventSource→ 回退轮询(带指数退避) - 不支持
navigator.clipboard→ 降级为document.execCommand('copy')(兼容 IE) - 不支持
ResizeObserver→ 监听window.resize+ 节流
检查本身只是起点,后续的 fallback 才决定体验是否断裂。
不复杂但容易忽略。

















