2025-02-17 日报 Day100

2025-02-17 日报 Day100

Yuyang 前端小白🥬

今日的鸡汤

每一个优秀背后,都有一段默默扎根的时光;每一次成长背后,都有一段奋斗拼搏的日子。只要度过山重水复,岁月自会赠你柳暗花明。

今日学习内容

1、JS 红皮书 P393-400 第十三章:客户端检测

今日笔记

1、浏览器分析: 想要知道自己代码运行在什么浏览器上,大部分开发者会分析 window.navigator.userAgent 返回的字符串值。所有浏览器都会提供这个值,如果相信这些返回值并基于给定的一组浏览器检测这个字符串,最终会得到关于浏览器和操作系统的比较精确的结果。

  • 伪造用户代理: 通过检测用户代理来识别浏览器并不是完美的方式,毕竟这个字符串是可以造假的。只不过实现 window.navigator 对象的浏览器(即所有现代浏览器)都会提供 userAgent 这个只读属性。因此,简单地给这个属性设置其他值不会有效:
1
2
3
4
5
6
7
console.log(window.navigator.userAgent);
// Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/65.0.3325.181 Safari/537.36
window.navigator.userAgent = 'foobar';
console.log(window.navigator.userAgent);
// Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/65.0.3325.181 Safari/537.36

不过,通过简单的办法可以绕过这个限制。比如,有些浏览器提供伪私有的defineGetter方法,利用它可以篡改用户代理字符串:

1
2
3
4
5
6
console.log(window.navigator.userAgent);
// Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/65.0.3325.181 Safari/537.36
window.navigator.__defineGetter__('userAgent', () => 'foobar');
console.log(window.navigator.userAgent);
// foobar
  • 分析浏览器: 通过解析浏览器返回的用户代理字符串,可以极其准确地推断出下列相关的环境信息:
     浏览器
     浏览器版本
     浏览器渲染引擎
     设备类型(桌面/移动)
     设备生产商
     设备型号
     操作系统
     操作系统版本
    GitHub 上维护比较频繁的第三方用户代理解析程序:
     Bowser
     UAParser.js
     Platform.js
     CURRENT-DEVICE
     Google Closure
     Mootools
    2、软件与硬件检测: 现代浏览器提供了一组与页面执行环境相关的信息,包括浏览器、操作系统、硬件和周边设备信息。这些属性可以通过暴露在 window.navigator 上的一组 API 获得。不过,这些 API 的跨浏览器支持还不够好,远未达到标准化的程度。
    注意 强烈建议在使用这些 API 之前先检测它们是否存在,因为其中多数都不是强制性的,且很多浏览器没有支持。另外,本节介绍的特性有时候不一定可靠。
  • 识别浏览器与操作系统: 特性检测和用户代理字符串解析是当前常用的两种识别浏览器的方式。而 navigator 和 screen 对象也提供了关于页面所在软件环境的信息。
    • navigator.oscpu: navigator.oscpu 属性是一个字符串,通常对应用户代理字符串中操作系统/系统架构相关信息。根据 HTML 实时标准:oscpu 属性的获取方法必须返回空字符串或者表示浏览器所在平台的字符串,比如”Windows NT 10.0; Win64; x64”或”Linux x86_64”。
  • navigator.vendor: navigator.vendor 属性是一个字符串,通常包含浏览器开发商信息。返回这个字符串是浏览器 navigator 兼容模式的一个功能。根据 HTML 实时标准:navigator.vendor 返回一个空字符串,也可能返回字符串”Apple Computer, Inc.”或字符串”Google Inc.”。
    例如,Chrome 中的这个 navigator.vendor 属性返回下面的字符串:
    console.log(navigator.vendor); // “Google Inc.”
  • navigator.platform: navigator.platform 属性是一个字符串,通常表示浏览器所在的操作系统。根据 HTML 实时标准:navigator.platform 必须返回一个字符串或表示浏览器所在平台的字符串,例如”MacIntel”、”Win32”、”FreeBSD i386”或”WebTV OS”。例如,Windows 系统下 Chrome 中的这个 navigator.platform 属性返回下面的字符串:
    console.log(navigator.platform); // “Win32”
  • screen.colorDepth 和 screen.pixelDepth: screen.colorDepth 和 screen.pixelDepth 返回一样的值,即显示器每像素颜色的位深。根据 CSS 对象模型(CSSOM)规范:screen.colorDepth 和 screen.pixelDepth 属性应该返回输出设备中每像素用于显示颜色的位数,不包含 alpha 通道
  • screen.orientation: screen.orientation 属性返回一个 ScreenOrientation 对象,其中包含 Screen Orientation API 定义的屏幕信息。这里面最有意思的属性是 angle 和 type,前者返回相对于默认状态下屏幕的角度,后者返回以下 4 种枚举值之一:
     portrait-primary: 设备处于竖屏(纵向)模式,且这是设备默认的“主”方向(如手机竖着拿)
     portrait-secondary: 竖屏模式,但设备被“上下颠倒”了(相对于主方向)
     landscape-primary: 横屏(横向)模式,且是设备默认的主横屏方向(如部分平板或笔记本)
     landscape-secondary: 横屏模式,但设备被“反转”了(例如屏幕旋转了 180 度)
    根据规范,这些值的初始化取决于浏览器和设备状态。因此,不能假设 portrait-primary 和 0 始终是初始值。这两个值主要用于确定设备旋转后浏览器的朝向变化。
  • 浏览器元数据: navigator 对象暴露出一些 API,可以提供浏览器和操作系统的状态信息。
  • Geolocation API: navigator.geolocation 属性暴露了 Geolocation API,可以让浏览器脚本感知当前设备的地理位置。这个 API 只在安全执行环境(通过 HTTPS 获取的脚本)中可用。这个 API 可以查询宿主系统并尽可能精确地返回设备的位置信息。
    要获取浏览器当前的位置,可以使用 getCurrentPosition()方法。这个方法返回一个 Coordinates 对象,其中包含的信息不一定完全依赖宿主系统的能力:
    // getCurrentPosition()会以 position 对象为参数调用传入的回调函数
    navigator.geolocation.getCurrentPosition((position) => p = position);
    获取浏览器地理位置并不能保证成功。因此 getCurrentPosition()方法也接收失败回调函数作为第二个参数,这个函数会收到一个 PositionError 对象。在失败的情况下,PositionError 对象中会包含一个 code 属性和一个 message 属性,后者包含对错误的简短描述。code 属性是一个整数,表示以下 3 种错误。
    1. PERMISSION_DENIED: 用户拒绝了地理位置请求
    2. POSITION_UNAVAILABLE: 无法获取地理位置
    3. TIMEOUT: 获取地理位置超时
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      // 浏览器会弹出确认对话框请用户允许访问 Geolocation API
      // 这个例子显示了用户拒绝之后的结果
      navigator.geolocation.getCurrentPosition(
      () => {},
      (e) => {
      console.log(e.code); // 1
      console.log(e.message); // User denied Geolocation
      }
      );
      // 这个例子展示了在不安全的上下文中执行代码的结果
      navigator.geolocation.getCurrentPosition(
      () => {},
      (e) => {
      console.log(e.code); // 1
      console.log(e.message); // Only secure origins are allowed
      }
      );
  • Connection State 和 NetworkInformation API: 浏览器会跟踪网络连接状态并以两种方式暴露这些信息:连接事件和 navigator.onLine 属性。在设备连接到网络时,浏览器会记录这个事实并在 window 对象上触发 online 事件。断开网络连接后,浏览器会在 window 对象上触发 offline 事件。任何时候,都可以通过 navigator. onLine 属性来确定浏览器的联网状态。这个属性返回一个布尔值,表示浏览器是否联网。
1
2
3
4
5
6
7
const connectionStateChange = () => console.log(navigator.onLine);
window.addEventListener("online", connectionStateChange);
window.addEventListener("offline", connectionStateChange);
// 设备联网时:
// true
// 设备断网时:
// false

navigator 对象还暴露了 NetworkInformation API,可以通过 navigator.connection 属性使用。这个 API 提供了一些只读属性,并为连接属性变化事件处理程序定义了一个事件对象。以下是 NetworkInformation API 暴露的属性。
 downlink:整数,表示当前设备的带宽(以 Mbit/s 为单位),舍入到最接近的 25kbit/s。这个值可能会根据历史网络吞吐量计算,也可能根据连接技术的能力来计算。
 downlinkMax:整数,表示当前设备最大的下行带宽(以 Mbit/s 为单位),根据网络的第一跳来确定。因为第一跳不一定反映端到端的网络速度,所以这个值只能用作粗略的上限值。
 effectiveType:字符串枚举值,表示连接速度和质量。这些值对应不同的蜂窝数据网络连接技术,但也用于分类无线网络。这个值有以下 4 种可能。 1. slow-2g:2G 蜂窝网络 2. 2g:2G 蜂窝网络 3. 3g:3G 蜂窝网络 4. 4g:4G 蜂窝网络
 rtt:毫秒,表示当前网络实际的往返时间,舍入为最接近的 25 毫秒。这个值可能根据历史网络吞吐量计算,也可能根据连接技术的能力来计算。
 type:字符串枚举值,表示网络连接技术。这个值可能为下列值之一。
1、bluetooth: 蓝牙连接
2、cellular: 蜂窝网络连接
3、ethernet: 有线以太网连接
4、none: 没有网络连接.相当于 navigator.onLine === false。
5、mixed: 混合连接
6、other: 其他连接
7、unknown: 未知连接
8、wifi: 无线网络连接
9、wimax: WiMax 连接
 saveData:布尔值,表示用户设备是否启用了“节流”(reduced data)模式。
 onchange:事件处理程序,会在任何连接状态变化时激发一个 change 事件。可以通过 navigator. connection.addEventListener(‘change’,changeHandler)或 navigator.connection.onchange = changeHandler 等方式使用。

  • Battery Status API: 浏览器可以访问设备电池及充电状态的信息。navigator.getBattery()方法会返回一个期约实例,解决为一个 BatteryManager 对象。
    navigator.getBattery().then((b) => console.log(b));
    // BatteryManager { … }
    BatteryManager 包含 4 个只读属性,提供了设备电池的相关信息。
     charging:布尔值,表示设备当前是否正接入电源充电。如果设备没有电池,则返回 true。
     chargingTime:整数,表示预计离电池充满还有多少秒。如果电池已充满或设备没有电池,则返回 0。
     dischargingTime:整数,表示预计离电量耗尽还有多少秒。如果设备没有电池,则返回 Infinity。
     level:浮点数,表示电量百分比。电量完全耗尽返回 0.0,电池充满返回 1.0。如果设备没有电池,则返回 1.0。
    这个 API 还提供了 4 个事件属性,可用于设置在相应的电池事件发生时调用的回调函数。可以通过给 BatteryManager 添加事件监听器,也可以通过给事件属性赋值来使用这些属性。
     onchargingchange
     onchargingtimechange
     ondischargingtimechange
     onlevelchange
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
navigator.getBattery().then((battery) => {
// 添加充电状态变化时的处理程序
const chargingChangeHandler = () => console.log("chargingchange");
battery.onchargingchange = chargingChangeHandler;
// 或
battery.addEventListener("chargingchange", chargingChangeHandler);
// 添加充电时间变化时的处理程序
const chargingTimeChangeHandler = () => console.log("chargingtimechange");
battery.onchargingtimechange = chargingTimeChangeHandler;
// 或
battery.addEventListener("chargingtimechange", chargingTimeChangeHandler);
// 添加放电时间变化时的处理程序
const dischargingTimeChangeHandler = () =>
console.log("dischargingtimechange");
battery.ondischargingtimechange = dischargingTimeChangeHandler;
// 或
battery.addEventListener(
"dischargingtimechange",
dischargingTimeChangeHandler
);
// 添加电量百分比变化时的处理程序
const levelChangeHandler = () => console.log("levelchange");
battery.onlevelchange = levelChangeHandler;
// 或
battery.addEventListener("levelchange", levelChangeHandler);
});
  • 硬件: 浏览器检测硬件的能力相当有限。不过,navigator 对象还是通过一些属性提供了基本信息。
  • 处理器核心数: navigator.hardwareConcurrency 属性返回浏览器支持的逻辑处理器核心数量。关键在于,这个值表示浏览器可以并行执行的最大工作线程数量,不一定是实际的 CPU 核心数。
  • 设备内存大小: navigator.deviceMemory 属性返回设备的内存大小(以 GiB 为单位)。这个值是一个整数,表示设备的内存大小。这个值不是强制性的,可能会被浏览器忽略。
  • 最大触点数: navigator.maxTouchPoints 属性返回设备支持的最大触点数。这个值是一个整数,表示设备支持的最大触点数。这个值不是强制性的,可能会被浏览器忽略。
    2、小结: 客户端检测是 JavaScript 中争议最多的话题之一。因为不同浏览器之间存在差异,所以经常需要根据浏览器的能力来编写不同的代码。客户端检测有不少方式,但下面两种用得最多。
    能力检测,在使用之前先测试浏览器的特定能力。
    用户代理检测,通过用户代理字符串确定浏览器。
    在选择客户端检测方法时,首选是使用能力检测。特殊能力检测要放在次要位置,作为决定代码逻辑的参考。用户代理检测是最后一个选择,因为它过于依赖用户代理字符串
    浏览器也提供了一些软件和硬件相关的信息。这些信息通过 screen 和 navigator 对象暴露出来。利用这些 API,可以获取关于操作系统、浏览器、硬件、设备位置、电池状态等方面的准确信息。
此页目录
2025-02-17 日报 Day100