《JavaScript高级程序设计》Chapter 9 客户端检测

Chapter 9 客户端检测

能力检测

  1. 用于识别浏览器的能力,基本模式如下

    if (object.propertyInQuestion) {
        // use object.propertyInQuestion
    }
    
    function getElement(id) {
        if (document.getElementById) {
            return document.getElementById(id);
        } else if (document.all) {
            return document.all[id];
        } else {
            throw new Error('No way to retrieve element');
        }
    }
    
  2. 更可靠的能力检测

    • 确定一个对象是否支持排序(既要检测 sort 属性是否存在,也要检测 sort 属性是否是一个函数)
    function isSortable(object) {
        return typeof object.sort == 'function';
    }
    
    • 尽量使用 typeof 进行能力检测。但是在 IE8 之前的版本中,由于宿主对象是通过 COM 而非 JScript 实现的,所以 document.createElement() 之类的函数是一个 COM 对象,而非函数
    typeof document.createElement == 'function' // 在 IE8- 中为 false
    
    • IE 中类似的 typeof 的行为不标准的例子还有很多。
    • 在浏览器环境下测试任何对象的某个属性是否存在,要使用下面这个函数
    function isHostMethod(object, property) {
        var t = typeof object[property];
        return t == 'function' || (!!(t == 'object' && object[property])) || t == 'unknown;
    
    result = isHostMethod(xhr, 'open'); // true
    

    在 JavaScript 中, !! 操作符通常用于将表达式强制转换为 boolean 类型数据。

  3. 能力检测,不是浏览器检测

    • 检测某个或某几个特性并不能够确定浏览器。
    • 如果已知应用程序需要使用某些特定的浏览器特性,最好一次检测所有的相关特性,而不是分别检测。
    // 检测浏览器是否具有 DOM1 级规定的能力。
    var hasDOM1 = !! (document.getElementById && document.createElement && document.getElementByTagName);
    

怪癖检测

  1. 怪癖检测的目标是识别浏览器的特殊行为。通过怪癖检测,获知浏览器存在什么样的缺陷。

  2. Example: IE8- 中,如果某个实例属性与 [[Enumerable]] 标记为 false 的某个原型属性同名,那么该实例属性将不会出现在 for-in 循环当中。

    var hasDontEnumQuirk = function() {
        var o = {toString: function() {
            for(var prop in o) {
                if(prop == 'toString') {
                    return false;
                }
            }
        }
        return true;
    }();
    
  3. Example: Safari3- 中,会枚举被隐藏的属性。

var hasEnumShadowsQuirk() {
    var o = {toString: function() {
        var count = 0;
        for (var prop in o) {
            if (prop == 'toString') {
                count++;
            }
        }
        return (count > 1);
    }();
3. Example: Safari3- 中,会枚举被隐藏的属性。

```
var hasEnumShadowsQuirk() {
    var o = {toString: function() {
        var count = 0;
        for (var prop in o) {
            if (prop == 'toString') {
                count++;
            }
        }
        return (count > 1);
    }();
```

用户代理检测

  1. 用户代理检测通过检测用户代理字符串来确定实际使用的浏览器。在每一次 HTTP 请求过程中,用户代理字符串是作为相应首部发送的,而且该字符串可以通过 JavaScript 的 navigator.userAgent 属性访问。

    • 在服务器端,用户代理检测被广泛使用。
    • 在客户端,用户代理检测的优先级排在能力检测 / 怪癖检测之后。
  2. 电子欺骗:浏览器通过在自己的用户代理字符串加入一些错误或误导信息,来达到欺骗服务器的目的。

  3. 用户代理字符串

    • RFC2616 规定格式:标识符/产品版本号
    • IE4 ~ IE7:Mozilla/4.0 (平台; MSIE 版本号; 操作系统)
    • IE8:Mozilla/4.0 (compatible; MSIE 版本号; Trident/Trident 版本号)
      • Trident 记号是为了让开发人员知道 IE8 是不是在兼容模式下运行,如果是,则 MSIE 版本号会变成 7。
    • IE9:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
    • IE9 兼容模式:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0)
    • Gecko:Mozilla/Mozilla 版本号 (平台; 加密类型; 操作系统或CPU; 语言; 预先发型版本) Gecko/Gecko 版本号 应用程序或产品/引用程序或产品版本号
      • 具体查手册
    • Firefox4: Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox 4.0.1
    • Webkit:Mozilla/5.0 (平台; 加密类型; 操作系统或CPU; 语言) AppleWebkit/AppleWebkit 版本号 (KHTML, like Gecko) Safari/Safari 版本号
      • 基于 Webkit 的所有浏览器都将自己标识为 Mozilla 5.0
    • Safari3:Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebkit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5
    • Konqueror 略
    • Chrome7:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebkit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7
    • Opera8:Opera/版本号 (操作系统或CPU; 加密类型; 语言)
    • Opera9:Mozilla/5.0 (Windows NT 5.1; U; en-US; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50
      • Opera隐藏了自己的身份,伪装成 Firefox 或 IE,无法将 Opera 与其他浏览器区别开来。
    • Opera10:Opera/9.80 (操作系统或CPU; 加密类型; 语言) Presto/Presto 版本号 Version/版本号
    • iOS 和 Android:同桌面版
  4. 用户代理字符串检测技术

    • 识别呈现引擎

      • 检测五大引擎:IE、Gecko、WebKit、KHTML、Opera
      • 使用模块增强模式封装检测脚本
      var client = function() {
          var engine = {
              ie: 0,
              gecko: 0,
              webkit: 0,
              khtml: 0,
              opera: 0,
              
              ver: null
          };
          
          return {
              engine: engine
          };
      }();
      
      • 如果检测到了哪个呈现引擎,就以浮点数值形式将该引擎的版本号写入相应的属性。而呈现引擎的完整版本(是一个字符串),则被写入 ver 属性。
      if(client.engine.ie) {
          // 针对 IE 的代码
      } else if (client.engine.gecko > 1.5) {
          if (client.engine.ver == '1.8.1') {
              // 针对这个版本执行某些代码
          }
      }
      
      • 识别 Opera 必须检测 window.opera 对象。
      if (window.opera) {
          engine.var = window.opera.version();
          engine.opera = parseFloat(engine.ver);
      
      • 识别 WebKit 和 KHTML 见手册。
    • 识别浏览器(具体见文档)

    var browser = {
        ie: 0,
        firefox: 0,
        safari: 0,
        konq: 0,
        opera: 0,
        chrome: 0,
        ver: null
    };
    
    • 识别平台(具体见文档)
    var system = {
        win: false,
        mac: false,
        x11: false
    };
    var p = navigator.platform;
    system.win = p.indexOf('Win') == 0;
    system.mac = p.indexOf('Mac') == 0;
    system.x11 = (p.indexOf('X11') == 0) || (p.indexOf('Linux') == 0); 
    
    • 识别移动设备(具体见文档)
    var system = {
        win: false,
        mac: false,
        x11: false,
        
        iphone: false,
        ipod: false,
        ipad: false,
        ios: false,
        android: false,
        nokiaN: false,
        winMobile: false
    };
    var p = navigator.platform;
    system.win = p.indexOf('Win') == 0;
    system.mac = p.indexOf('Mac') == 0;
    system.x11 = (p.indexOf('X11') == 0) || (p.indexOf('Linux') == 0); 
    
    system.iphone = ua.indexOf('iPhone') > -1;
    system.ipod = ua.indexOf('iPod') > -1;
    system.ipad = ua.indexOf('iPad') > -1;
    system.nokiaN = ua.indexOf('NokiaN') > -1;
    system.winMobile = (system.win == 'CE');
    
    • 识别游戏系统(略)
  5. 完整的代码(看文档)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容