IE8环境下用JavaScript检测空值/非对象避免报错的简单方法
网上很多流传的兼容性检测代码,其实默认了一个只在现代浏览器成立的前提:读取不存在的对象属性只会返回undefined,不会触发能直接终止脚本运行的硬错误。
一碰到IE8的环境,这个前提根本站不住——只要是涉及window、document这类宿主对象的属性读取,IE8在不同文档模式、安全权限设置下,很容易直接抛null or not an object的运行时错误。
其实根本不需要到处堆try/catch把代码搞得臃肿,也不需要靠不靠谱的条件注释凑兼容,用几个原生JS的特性就能写出不崩的检测逻辑。
核心检测方法
1. 用in操作符检测全局对象的属性存在性
in操作符的作用是判断指定属性是否存在于目标对象(包括原型链上的属性)的属性名录里,整个检测过程只查属性列表、不会触发属性值的读取校验,自然不会抛硬错。
- 之前直接取值会报错的
XMLHttpRequest检测,错误写法:
改成安全写法:if (!window.XMLHttpRequest) // IE8下特定场景直接抛错if (!('XMLHttpRequest' in window)) // 只查属性是否存在,不读值,不会报错 - 之前的视口宽高获取逻辑,改成先做属性存在性校验再取值:
var w = 'innerWidth' in window ? window.innerWidth : document.documentElement.clientWidth || document.body.clientWidth; var h = 'innerHeight' in window ? window.innerHeight : document.documentElement.clientHeight || document.body.clientHeight;
2. 用typeof运算符做前置类型校验
typeof是所有属性读取运算里,对IE8宿主对象兼容性最好的操作——哪怕属性值是null、是受浏览器安全限制的API,typeof也只会返回对应类型的字符串,不会触发硬报错。
最典型的坑就是window.event的检测:这个全局对象只在事件触发的调用上下文中才有效,在全局作用域直接读值很容易触发报错。
错误写法:
if (!window.event) // 全局作用域读这个值,IE8直接抛错
安全写法:
// 先通过typeof确认值的类型,只有确定不是undefined/null的时候再读值 if (typeof window.event === 'undefined' || window.event === null) { // 执行备用兼容逻辑 }
3. 轻量通用安全取值函数
针对嵌套属性的检测,不需要写一长串&&判空链,也不需要到处套try/catch,写个几行的通用函数就能覆盖大部分场景,比零散的错误处理逻辑代码体积小、运行效率高:
function safeGet(root, propPath) { var props = propPath.split('.'); var current = root; for (var i = 0; i < props.length; i++) { // 每一层都用typeof做拦截,避免读值触发报错 if (typeof current[props[i]] === 'undefined' || current[props[i]] === null) { return undefined; } current = current[props[i]]; } return current; }
使用示例:
// 安全获取innerWidth,不存在就返回undefined,不会报错 var w = safeGet(window, 'innerWidth') || document.documentElement.clientWidth || document.body.clientWidth;
为什么不推荐依赖条件注释做检测
条件注释本质是靠浏览器版本猜能力,根本覆盖不了实际场景的所有问题:比如同样是IE8,用户切换到IE5怪癖文档模式、调高浏览器安全级别禁用了部分API、甚至跨域场景下的属性访问限制,都会导致本应存在的对象实际不可用。上面提到的原生检测方法是直接在当前运行环境下验证对象的实际可用性,比版本猜测的可靠性高得多。
补充踩坑说明:很多Stack Overflow上的高赞兼容答案,写的时候已经默认跳过了IE8这类老浏览器的特殊错误场景,直接拿现代浏览器的运行逻辑当通用标准,才会出现代码看起来逻辑通顺、实际在老浏览器里直接崩的问题。
内容的提问来源于stack exchange,提问作者Randy

