能力检测最有效的场景是检测能力是否存在的同时,验证其是否能够展现出预期的行为。前一节中 的例子依赖将测试对象的成员转换类型,然后再确定它是否存在。虽然这样能够确定检测的对象成员存 8 在,但不能确定它就是你想要的。来看下面的例子,这个函数尝试检测某个对象是否可以排序:
// 不要这样做!错误的能力检测,只能检测到能力是否存在 function isSortable(object) {
return !!object.sort;
}
这个函数尝试通过检测对象上是否有 sort()方法来确定它是否支持排序。问题在于,即使这个对 象有一个 sort 属性,这个函数也会返回 true:
let result = isSortable({ sort: true }); 简单地测试到一个属性存在并不代表这个对象就可以排序。更好的方式是检测 sort 是不是函数:
// 好一些,检测 sort 是不是函数 function isSortable(object) {
}
上面的代码中使用的 typeof 操作符可以确定 sort 是不是函数,从而确认是否可以调用它对数据 进行排序。
return typeof object.sort == "function";
进行能力检测时应该尽量使用 typeof 操作符,但光有它还不够。尤其是某些宿主对象并不保证对 typeof 测试返回合理的值。最有名的例子就是 Internet Explorer(IE)。在多数浏览器中,下面的代码都 会在 document.createElement()存在时返回 true:
// 不适用于 IE8 及更低版本 function hasCreateElement() {
return typeof document.createElement == "function";
}
但在 IE8 及更低版本中,这个函数会返回 false。这是因为 typeof document.createElement 返回"object"而非"function"。前面提到过,DOM 对象是宿主对象,而宿主对象在 IE8 及更低版本 中是通过 COM 而非 JScript 实现的。因此,document.createElement()函数被实现为 COM 对象, typeof 返回"object"。IE9 对 DOM 方法会返回"function"。
基于能力检测进行浏览器分析
虽然可能有人觉得能力检测类似于黑科技,但恰当地使用能力检测可以精准地分析运行代码的浏览 器。使用能力检测而非用户代理检测的优点在于,伪造用户代理字符串很简单,而伪造能够欺骗能力检 测的浏览器特性却很难。
1. 检测特性
可以按照能力将浏览器归类。如果你的应用程序需要使用特定的浏览器能力,那么最好集中检测所 有能力,而不是等到用的时候再重复检测。比如:
// 检测浏览器是否支持 Netscape 式的插件
let hasNSPlugins = !!(navigator.plugins && navigator.plugins.length);
// 检测浏览器是否具有 DOM Level 1 能力
let hasDOM1 = !!(document.getElementById && document.createElement &&
document.getElementsByTagName);
这个例子完成了两项检测:一项是确定浏览器是否支持 Netscape 式的插件,另一项是检测浏览器 是否具有 DOM Level 1 能力。保存在变量中的布尔值可以用在后面的条件语句中,这样比重复检测省 事多了。