如何找出浏览器的 Javascript 引擎版本和对 ECMAScript 6 的支持?
我navigator.appVersion
只是想知道浏览器的版本,而不是引擎的版本。
如何找出浏览器的 Javascript 引擎版本和对 ECMAScript 6 的支持?
我navigator.appVersion
只是想知道浏览器的版本,而不是引擎的版本。
我建议您使用特征检测而不是使用启发式方法检测浏览器的引擎。为此,您可以简单地将一些代码包装在一个try {..} catch (e) {...}
语句中,或者使用一些if (...)
statements。
例如:
function check() {
if (typeof SpecialObject == "undefined") return false;
try { specialFunction(); }
catch (e) { return false; }
return true;
}
if (check()) {
// Use SpecialObject and specialFunction
} else {
// You cannot use them :(
}
在大多数情况下,有多种原因使特征检测成为最佳选择:
您不必依赖浏览器的版本、引擎或细节,也不必使用难以实现且非常狡猾的启发式方法来检测它们。
您不会陷入有关浏览器/引擎规格检测的错误中。
您不必担心特定于浏览器的功能:例如,WebKit浏览器的规范与其他浏览器不同。
您可以确定,一旦检测到某个功能,您就可以使用它。
这些是恕我直言使特征检测成为最佳方法的主要原因。
使用特征检测时,当您不确定可以/不能使用哪些特征时,一种非常聪明的工作方式包括多个特征检测和随后对更基本方法的回退(甚至从头开始创建这些方法),以防万一不支持您要使用。
一个带有回退的特征检测的简单示例可能适用于该window.requestAnimationFrame
特征,并非所有浏览器都支持该特征,并且根据您使用的浏览器有几个不同的前缀。在这种情况下,您可以像这样轻松检测和回退:
requestAnimationFrame =
window.requestAnimationFrame // Standard name
|| window.webkitRequestAnimationFrame // Fallback to webkit- (old versions of Chrome or Safari)
|| window.mozRequestAnimationFrame // Fallback to moz- (Mozilla Firefox)
|| false; // Feature not supported :(
// Same goes for cancelAnimationFrame
cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || false;
if (!requestAnimationFrame) {
// Not supported? Build it by yourself!
requestAnimationFrame = function(callback) {
return setTimeout(callback, 0);
}
// No requestAnim. means no cancelAnim. Built that too.
cancelAnimationFrame = function(id) {
clearTimeout(id);
}
}
// Now you can use requestAnimationFrame
// No matter which browser you're running
var animationID = requestAnimationFrame(myBeautifulFunction);
现在,真正的问题来了:如果你想检测对 ES6 的支持,你将无法像我上面说的那样表现,因为ES6 的相关特性范围是基于新的语法和私有词,并且会抛出a SyntaxError
if 在 ES5 中使用,这意味着编写包含 ES5 和 ES6 的脚本是不可能的!
这是一个演示此问题的示例;下面的代码段不起作用,它会在执行前被阻止,因为包含非法语法。
function check() {
"use strict";
try { eval("var foo = (x)=>x+1"); }
catch (e) { return false; }
return true;
}
if (check()) {
var bar = (arg) => { return arg; }
// THIS LINE will always throw a SyntaxError in ES5
// even before checking for ES6
// because it contains illegal syntax.
} else {
var bar = function(arg) { return arg; }
}
现在,由于您不能在同一个脚本中同时有条件地检查和执行 ES6,您必须编写两个不同的脚本:一个只使用 ES5,另一个包含 ES6 功能。使用两种不同的脚本,您将能够导入 ES6 中的一种,前提是它受支持,并且不会导致SyntaxErrors
抛出。
现在让我们做一个更相关的例子,假设你想在你的 ES6 脚本中使用这些特性:
Symbol
对象class
关键字构建的类(...)=>{...}
) 函数注意: 新引入的语法(如箭头函数)的特征检测只能使用该eval()
函数或其他等效项(例如Function()
)来完成,因为编写无效的语法会在脚本执行之前停止脚本。这也是你不能使用if
语句来检测类和箭头函数的原因:这些特性是关于关键字和语法的,所以eval(...)
包裹在try {...} catch (e) {...}
块中会很好用。
所以,来到真正的代码:
HTML 标记:
<html>
<head>
<script src="es5script.js"></script>
</head>
<body>
<!-- ... -->
</body>
</html>
es5script.js
脚本中的代码:
function check() {
"use strict";
if (typeof Symbol == "undefined") return false;
try {
eval("class Foo {}");
eval("var bar = (x) => x+1");
} catch (e) { return false; }
return true;
}
if (check()) {
// The engine supports ES6 features you want to use
var s = document.createElement('script');
s.src = "es6script.js";
document.head.appendChild(s);
} else {
// The engine doesn't support those ES6 features
// Use the boring ES5 :(
}
在您的代码es6script.js
:
// Just for example...
"use strict";
class Car { // yay!
constructor(speed) {
this.speed = speed;
}
}
var foo = Symbol('foo'); // wohoo!
var bar = new Car(320); // blaze it!
var baz = (name) => { alert('Hello ' + name + '!'); }; // so cool!
就像我上面说的,浏览器和引擎检测在编写 JavaScript 脚本时并不是最佳实践。我会给你一些关于这个主题的背景知识,只是不要把我的话当作“随机的个人意见”。
引用 MDN 文档 [链接]:
在考虑使用用户代理字符串来检测正在使用的浏览器时,您的第一步是尽可能避免使用它。首先尝试确定您为什么要这样做。
[...]您是否正在尝试检查特定功能的存在? 您的站点需要使用某些浏览器尚不支持的特定 Web 功能,并且您希望将这些用户发送到具有较少功能但您知道可以使用的旧网站。这是使用用户代理检测的最糟糕的原因,因为很可能最终所有其他浏览器都会迎头赶上。在这种情况下,您应该尽量避免使用用户代理嗅探,而是进行特征检测。
此外,您是说您使用navigator.appVersion
,但请考虑使用另一种方法,因为该方法与许多其他导航器属性一起已被弃用,并且其行为并不总是像您想象的那样。
因此,再次引用 MDN 文档 [链接]:
已弃用:此功能已从 Web 标准中删除。虽然一些浏览器可能仍然支持它,但它正在被删除。不要在旧项目或新项目中使用它。使用它的页面或 Web 应用程序可能随时中断。
注意:不要依赖此属性来返回正确的浏览器版本。在基于 Gecko 的浏览器(如 Firefox)和基于 WebKit 的浏览器(如 Chrome 和 Safari)中,返回值以“5.0”开头,后跟平台信息。在 Opera 10 和更新版本中,返回的版本也不匹配实际的浏览器版本。
支持 ES6 module的浏览器供应商现在提供了一种简单的方法来进行特征检测:
...
<head>
<script nomodule>window.nomodules = true;</script>
<script>console.log(window.nomodules)</script>
</head>
...
具有该nomodule
属性的脚本不会被支持的浏览器执行<script type="module" ...>
您还可以像这样注入脚本:
const script = document.createElement('script');
script.setAttribute('nomodule', '');
script.innerHTML = 'window.nomodules = true;';
document.head.insertBefore(script, document.head.firstChild);
script.remove();
正如 Marco Bonelli 所说,检测 ECMAScript 6 语言语法的最佳方法是使用eval(); . 如果调用没有抛出错误,则支持“所有其他”功能,但我推荐Function(); .
function isES6()
{
try
{
Function("() => {};"); return true;
}
catch(exception)
{
return false;
}
}
(function() {
var v8string = 'function%20javaEnabled%28%29%20%7B%20%5Bnative%20code%5D%20%7D';
var es6string = 'function%20javaEnabled%28%29%20%7B%0A%20%20%20%20%5Bnative%20code%5D%0A%7D';
if (window.devicePixelRatio) //If WebKit browser
{
var s = escape(navigator.javaEnabled.toString());
if (s === v8string) {
alert('V099787 detected');
} else if (s === es6string) {
alert('ES6 detected')
} else {
alert('JSC detected');
}
} else {
display("Not a WebKit browser");
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
})()
如果您因任何原因不能使用 eval,您可以通过在其自己的脚本块中插入脚本代码来全局检测这一点(并且要么测试块中变量赋值的值,要么使用 window.onerror)。
<script>
window.isES6 = false;
</script>
<script>
// Arrow functions support
() => { };
// Class support
class __ES6FeatureDetectionTest { };
// Object initializer property and method shorthands
let a = true;
let b = {
a,
c() { return true; },
d: [1,2,3],
};
// Object destructuring
let { c, d } = b;
// Spread operator
let e = [...d, 4];
window.isES6 = true;
</script>
<script>
document.body.innerHTML += 'isES6: ' + window.isES6;
</script>
https://jsfiddle.net/s5tqow91/2/
请注意,ES6 的特性有很多,只检查一项并不能保证你会被覆盖。(上面的代码也没有涵盖所有内容,这只是我认为我最常使用的功能)。
还有一个相当流行的 npm 库,它似乎涵盖了所有这些内容:https : //www.npmjs.com/package/feature-detect-es6