脚本 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)在今天已基本不再产生流量。

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

相关推荐
马优晨5 分钟前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
窝子面2 小时前
手搓最简前后端协作-node
javascript·数据库
人邮异步社区2 小时前
怎么把C语言学到精通?
c语言·开发语言
kyriewen2 小时前
我让AI给前端项目做了一次完整的Code Review——它和人类的差距,比我想的大得多
前端·javascript·ai编程
心平气和量大福大2 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai3 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜3 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa3 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio
一次旅行3 小时前
Python+大模型端到端自动化日报系统
开发语言·python·自动化
sugar__salt4 小时前
Vue3 表单双向绑定与响应式核心 API 技术详解
前端·javascript·vue.js