2025-02-16 日报 Day99

2025-02-16 日报 Day99

Yuyang 前端小白🥬

今日的鸡汤

该养精蓄锐时,别着急出人头地;该奋斗拼搏时,别企图一鸣惊人。因为通往成功最快的路,不是加速,而是脚踏实地地走好每一天。

今日学习内容

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

今日笔记

1、客户端检测:

  • 能力检测: 能力检测(又称特性检测)即在 JavaScript 运行时中使用一套简单的检测逻辑,测试浏览器是否支持某种特性。这种方式不要求事先知道特定浏览器的信息,只需检测自己关心的能力是否存在即可。能力检测的基本模式如下:
1
2
3
if (object.propertyInQuestion) {
// 使用 object.propertyInQuestion
}

比如,IE5 之前的版本中没有 document.getElementById()这个 DOM 方法,但可以通过 document.all 属性实现同样的功能。为此,可以进行如下能力检测:

1
2
3
4
5
6
7
8
9
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!");
}
}
  • 安全能力检测:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 不要这样做!错误的能力检测,只能检测到能力是否存在
function isSortable(object) {
return !!object.sort;
}

let result = isSortable({ sort: true });

// 好一些,检测 sort 是不是函数
function isSortable(object) {
return typeof object.sort == "function";
}

// 不适用于 IE8 及更低版本
function hasCreateElement() {
return typeof document.createElement == "function";
}
  • 基于能力检测进行浏览器分析: 使用能力检测而非用户代理检测的优点在于,伪造用户代理字符串很简单,而伪造能够欺骗能力检测的浏览器特性却很难。
  • 检测特性: 可以按照能力将浏览器归类。如果你的应用程序需要使用特定的浏览器能力,那么最好集中检测所有能力,而不是等到用的时候再重复检测。比如:
1
2
3
4
5
6
7
8
// 检测浏览器是否支持 Netscape 式的插件
let hasNSPlugins = !!(navigator.plugins && navigator.plugins.length);
// 检测浏览器是否具有 DOM Level 1 能力
let hasDOM1 = !!(
document.getElementById &&
document.createElement &&
document.getElementsByTagName
);
  • 检测浏览器: 可以根据对浏览器特性的检测并与已知特性对比,确认用户使用的是什么浏览器。这样可以获得比用户代码嗅探(稍后讨论)更准确的结果。
    下面来看一个例子,根据不同浏览器独有的行为推断出浏览器的身份。这里故意没有使用 navigator. userAgent 属性,后面会讨论它:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class BrowserDetector {
constructor() {
// 测试条件编译
// IE6~10 支持
this.isIE_Gte6Lte10 = /*@cc_on!@*/ false;
// 测试 documentMode
// IE7~11 支持
this.isIE_Gte7Lte11 = !!document.documentMode;
// 测试 StyleMedia 构造函数
// Edge 20 及以上版本支持
this.isEdge_Gte20 = !!window.StyleMedia;
// 测试 Firefox 专有扩展安装 API
// 所有版本的 Firefox 都支持
this.isFirefox_Gte1 = typeof InstallTrigger !== "undefined";
// 测试 chrome 对象及其 webstore 属性
// Opera 的某些版本有 window.chrome,但没有 window.chrome.webstore
// 所有版本的 Chrome 都支持
this.isChrome_Gte1 = !!window.chrome && !!window.chrome.webstore;
// Safari 早期版本会给构造函数的标签符追加"Constructor"字样,如:
// window.Element.toString(); // [object ElementConstructor]
// Safari 3~9.1 支持
this.isSafari_Gte3Lte9_1 = /constructor/i.test(window.Element);
// 推送通知 API 暴露在 window 对象上
// 使用默认参数值以避免对 undefined 调用 toString()
// Safari 7.1 及以上版本支持
this.isSafari_Gte7_1 = (({ pushNotification = {} } = {}) =>
pushNotification.toString() == "[object SafariRemoteNotification]")(
window.safari
);
// 测试 addons 属性
// Opera 20 及以上版本支持
this.isOpera_Gte20 = !!window.opr && !!window.opr.addons;
}
isIE() {
return this.isIE_Gte6Lte10 || this.isIE_Gte7Lte11;
}
isEdge() {
return this.isEdge_Gte20 && !this.isIE();
}
isFirefox() {
return this.isFirefox_Gte1;
}
isChrome() {
return this.isChrome_Gte1;
}
isSafari() {
return this.isSafari_Gte3Lte9_1 || this.isSafari_Gte7_1;
}
isOpera() {
return this.isOpera_Gte20;
}
}
  • 能力检测的局限: 通过检测一种或一组能力,并不总能确定使用的是哪种浏览器。

2、用户代理检测: 用户代理检测通过浏览器的用户代理字符串确定使用的是什么浏览器。用户代理字符串包含在每个HTTP 请求的头部,在 JavaScript 中可以通过 navigator.userAgent 访问。在服务器端,常见的做法是根据接收到的用户代理字符串确定浏览器并执行相应操作。而在客户端,用户代理检测被认为是不可靠的,只应该在没有其他选项时再考虑。
用户代理字符串最受争议的地方就是,在很长一段时间里,浏览器都通过在用户代理字符串包含错误或误导性信息来欺骗服务器。要理解背后的原因,必须回顾一下自 Web 出现之后用户代理字符串的历史。

  • 用户代理的历史: HTTP 规范(1.0 和 1.1)要求浏览器应该向服务器发送包含浏览器名称和版本信息的简短字符串。
    iOS 和 Android 移动操作系统上默认的浏览器都是基于 WebKit 的,因此具有与相应桌面浏览器一样的用户代理字符串。iOS 设备遵循以下基本格式:
此页目录
2025-02-16 日报 Day99