脚本 isMobile.js(移动设备检测库)的核心实现

偶然看到一段代码,原来是isMobile.js(移动设备检测库)的核心实现

javascript 复制代码
 !function (a) { var b = /iPhone/i, c = /iPod/i, d = /iPad/i, e = /(?=.*\bAndroid\b)(?=.*\bMobile\b)/i, f = /Android/i, g = /(?=.*\bAndroid\b)(?=.*\bSD4930UR\b)/i, h = /(?=.*\bAndroid\b)(?=.*\b(?:KFOT|KFTT|KFJWI|KFJWA|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|KFARWI|KFASWI|KFSAWI|KFSAWA)\b)/i, i = /Windows Phone/i, j = /(?=.*\bWindows\b)(?=.*\bARM\b)/i, k = /BlackBerry/i, l = /BB10/i, m = /Opera Mini/i, n = /(CriOS|Chrome)(?=.*\bMobile\b)/i, o = /(?=.*\bFirefox\b)(?=.*\bMobile\b)/i, p = new RegExp("(?:Nexus 7|BNTV250|Kindle Fire|Silk|GT-P1000)", "i"), q = function (a, b) { return a.test(b); }, r = function (a) { var r = a || navigator.userAgent, s = r.split("[FBAN"); if ("undefined" != typeof s[1] && (r = s[0]), s = r.split("Twitter"), "undefined" != typeof s[1] && (r = s[0]), this.apple = { phone: q(b, r), ipod: q(c, r), tablet: !q(b, r) && q(d, r), device: q(b, r) || q(c, r) || q(d, r) }, this.amazon = { phone: q(g, r), tablet: !q(g, r) && q(h, r), device: q(g, r) || q(h, r) }, this.android = { phone: q(g, r) || q(e, r), tablet: !q(g, r) && !q(e, r) && (q(h, r) || q(f, r)), device: q(g, r) || q(h, r) || q(e, r) || q(f, r) }, this.windows = { phone: q(i, r), tablet: q(j, r), device: q(i, r) || q(j, r) }, this.other = { blackberry: q(k, r), blackberry10: q(l, r), opera: q(m, r), firefox: q(o, r), chrome: q(n, r), device: q(k, r) || q(l, r) || q(m, r) || q(o, r) || q(n, r) }, this.seven_inch = q(p, r), this.any = this.apple.device || this.android.device || this.windows.device || this.other.device || this.seven_inch, this.phone = this.apple.phone || this.android.phone || this.windows.phone, this.tablet = this.apple.tablet || this.android.tablet || this.windows.tablet, "undefined" == typeof window) return this; }, s = function () { var a = new r; return a.Class = r, a; }; "undefined" != typeof module && module.exports && "undefined" == typeof window ? module.exports = r : "undefined" != typeof module && module.exports && "undefined" != typeof window ? module.exports = s() : "function" == typeof define && define.amd ? define("isMobile", [], a.isMobile = s()) : a.isMobile = s(); }(this);

MiniMax的说明

这段代码的作用

这段脚本是 isMobile.js (移动设备检测库)的核心实现。

整体思路是:

  1. 用一组正则表达式(Regular Expression)把 navigator.userAgent(或用户自己传入的字符串)里能体现「是什么设备」的特征匹配出来。
  2. 把这些特征组装成一个对象,返回给调用者。调用者只要读取 isMobile.phoneisMobile.tabletisMobile.any 等属性,就能知道当前页面是否运行在手机、平板或其它移动设备上。

下面逐段、逐行解释代码的细节。


1. 外层 IIFE(立即执行函数表达式)

复制代码
!function (a) {
    // ......
}(this);
  • !function (a){ ... } 等价于 (function(a){ ... }),是一种常见的"把代码包装成模块"写法。
  • 传入的实参 this 在浏览器环境下是 window,在 Node 环境里是 global(尽管后面会判断 window 是否存在)。
  • 这样可以把所有变量 封闭 在函数内部,防止污染全局命名空间,只把最终需要的 isMobile 暴露出去。

2. 正则表达式(device signatures)

下面是一组用来匹配 User‑Agent (UA)字符串的正则,全部使用 i(不区分大小写)标志。

变量 正则 用途
b /iPhone/i iPhone
c /iPod/i iPod
d /iPad/i iPad
e /(?=.*\bAndroid\b)(?=.*\bMobile\b)/i Android 手机(同时出现 Android 与 Mobile)
f /Android/i 任意 Android 设备(包括平板)
g /(?=.*\bAndroid\b)(?=.*\b(?:SD4930UR\b))/i Amazon 手机(Kindle Fire 的手机版)
h `/(?=.\bAndroid\b)(?=.\b(?:KFOT KFTT
i /Windows Phone/i Windows Phone
j /(?=.*\bWindows\b)(?=.*\bARM\b)/i Windows on ARM(例:Surface RT)
k /BlackBerry/i BlackBerry 老手机
l /BB10/i BlackBerry 10
m /Opera Mini/i Opera Mini 浏览器
n `/(CriOS Chrome)(?=.*\bMobile\b)/i`
o /(?=.*\bFirefox\b)(?=.*\bMobile\b)/i Firefox 移动版
p `new RegExp("(?:Nexus 7 BNTV250

说明

  • (?= ... )先行断言 (look‑ahead),用来要求同时满足多个条件但不消耗字符。
  • \b 表示单词边界,防止把 "Android" 匹配到 "AndroidX" 之类。
  • i 标志让匹配不区分大小写,因为 UA 字符串往往大小写混杂。

3. 辅助函数 q

复制代码
var q = function (a, b) {
    return a.test(b);
};
  • a:一个正则(RegExp 实例)
  • b:待检测的 UA 字符串
  • 返回true if 正则匹配成功,false otherwise。

这个函数把「正则 → 测试 → 布尔」这一步封装起来,后面的检测代码可以直接写成 q(b, r) 而不必每次写 b.test(r)


4. 核心构造函数 r

复制代码
var r = function (a) {
    var r = a || navigator.userAgent,
        s = r.split("[FBAN");
    if ("undefined" != typeof s[1] && (r = s[0]), s = r.split("Twitter"),
        "undefined" != typeof s[1] && (r = s[0]), this.apple = {...}, ...)

这里的 步骤

  1. 取 UA :如果调用者显式传入 a(如 new r("...")),就使用它;否则使用 navigator.userAgent
  2. 去除 Facebook / Twitter 包装
    • r.split("[FBAN") 把 UA 按 "[FBAN" 切开,通常 UA 里会出现 [FBAN...] 的部分(Facebook App 注入的),后面的 [0] 就是原始 UA。
    • 同理处理 Twitter。这波清洗是为了让后面的正则更加可靠。
  3. 构造检测结果对象 this(即 new r() 的实例),属性如下:
类别 属性 检测逻辑
apple phone 匹配 iPhone(b
ipod 匹配 iPod(c
tablet 不是 iPhone 匹配 iPad(!q(b,r) && q(d,r)
device 是 phone、ipod 或tablet 任一
amazon phone 匹配 Amazon 手机(g
tablet 不是 phone 匹配 Amazon 平板(!q(g,r) && q(h,r)
device 是 phone 或 tablet
android phone Amazon phone (g) 通用 Android 手机 (e)
tablet 不是 phone 匹配 Amazon 平板 (h) 通用 Android (f)
device phone、tablet、ghef 任一
windows phone 匹配 Windows Phone(i
tablet 匹配 Windows ARM(j
device phone 或 tablet
other blackberry BlackBerry 老机 (k)
blackberry10 BB10 (l)
opera Opera Mini (m)
firefox Firefox 移动版 (o)
chrome Chrome 移动版 (n)
device 任意上面的 true
seven_inch - 匹配 p(Nexus 7、Kindle Fire 等)
any - `apple.device
phone - `apple.phone
tablet - `apple.tablet

android 判定里,g(Amazon phone)与 e(Android Mobile)都会标记为 phone ,而 h(Amazon 平板) 与 f(普通 Android)则归为 tablet。这样即使在同一个"Android"体系里也能把平板与手机区分出来。


5. 输出方式(模块化)

复制代码
var s = function () {
    var a = new r;
    a.Class = r;
    return a;
};
  • s() 创建一个 实例 (把检测结果放进对象里),并把原始构造函数 r 挂在这个实例的 Class 属性上,方便后续继承或二次包装。

5.1 Node.js(CommonJS)

复制代码
"undefined" != typeof module && module.exports && "undefined" == typeof window
    ? module.exports = r
  • module.exports 存在且没有 window(Node 环境),直接把构造函数 r 导出。这样使用方可以 var isMobile = require('isMobile');new isMobile()

5.2 浏览器全局 + AMD

复制代码
"undefined" != typeof module && module.exports && "undefined" != typeof window
    ? module.exports = s()
    : "function" == typeof define && define.amd
        ? define("isMobile", [], a.isMobile = s())
        : a.isMobile = s();
  • AMD :如果检测到 define(RequireJS),则用 define 定义为模块 isMobile
  • 普通浏览器 :直接把 s()(即 new r() 的实例)赋值给全局对象 a.isMobilea 正是传入的 this,通常等价于 window)。

这里的四处判断确保了 同构 :无论在 Node、浏览器还是 RequireJS 环境,都只需要一次引用 isMobile 就能得到统一的检测对象。


6. 使用示例

复制代码
// 假设在浏览器中执行,isMobile 已经挂在 window 上
if (isMobile.phone) {
    alert("你在手机上");
}
if (isMobile.tablet) {
    alert("你在平板上");
}
if (isMobile.android.device) {
    console.log("设备是 Android 家族");
}
if (isMobile.any) {
    // 任意移动设备(包括 7 寸平板)都会进来
    console.log("移动设备访问");
}

Node/ RequireJS 用法同理:

复制代码
var isMobile = require('isMobile');
isMobile.phone      // => true/false
isMobile.tablet
isMobile.any

7. 小结

  • 正则 把 UA 字符串拆分成若干「特征」;
  • 构造函数 r 把这些特征聚合成布尔属性(phonetabletdevice 等);
  • 外层包装 根据当前运行平台(Node / 浏览器 / AMD)选择合适的导出方式,使得库在所有主流环境都能即插即用。

这段代码的核心价值在于:只依赖 UA ,不需要浏览器特性检测(window.orientationtouch 事件等),从而在 服务端渲染静态页面WebView 等各种场景都能快速判断「是不是手机」或「是不是平板」。


可能的局限性(顺便提及)

  1. UA 可被伪造 :恶意脚本可以随意修改 userAgent,导致误判。
  2. 新设备 выпуска:如果出现新的型号(如新 iPhone、新 Android 机型)不在正则里,检测会失效,需要手动更新正则。
  3. Windows 10 Mobile 已停产 :相关正则(如 Windows Phone)在今天已基本不再产生流量。

希望这段拆解能帮助你彻底弄清楚代码的意图和实现细节!如果还有其它细节想了解(比如如何扩展正则、或者怎样在项目中更好维护),随时告诉我。祝你开发顺利 🚀.

相关推荐
i_am_a_div_日积月累_7 小时前
3.contextBridge桥梁
前端·javascript·vue.js·electron
jieyucx8 小时前
从基础语法到面向对象:Go语言如何实现封装、继承与多态?
开发语言·后端·golang
SuperArc19998 小时前
jar包文件修改(java编译与反编译)
java·开发语言·后端·jar·反编译
计算机安禾8 小时前
【c++面向对象编程】第49篇:面向对象的单元测试:用GoogleTest测试类
开发语言·c++·单元测试
lly2024068 小时前
Python3 条件控制
开发语言
biter down8 小时前
3:GUI⾃动化简单⽰例
开发语言
坚定学代码8 小时前
如何在c++中使用MySQL
开发语言·c++·mysql
小牛蛋8 小时前
vcpkg 管理 PCL + VTK + Qt 开发三维点云可视化软件
开发语言·qt
阿正的梦工坊8 小时前
【Typescript】04-数组元组枚举与字面量类型
javascript·ubuntu·typescript