我如何查找浏览器的Javascript引擎版本,并支持ECMAScript 6?
我正在使用navigator.appVersion
来了解浏览器的版本,但不是引擎的版本。
我如何查找浏览器的Javascript引擎版本,并支持ECMAScript 6?
我正在使用navigator.appVersion
来了解浏览器的版本,但不是引擎的版本。
我建议您使用特性检测而不是使用启发式方法来检测浏览器引擎。您可以简单地在try {..} catch (e) {...}
语句中包装一些代码,或者使用一些if (...)
语句来实现这一点。
例如:
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特性基于新的语法和私有词汇,并且在ES5中使用会抛出SyntaxError
,这意味着编写同时包含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; }
}
SyntaxErrors
。
现在让我们举一个更具体的例子,假设您要在ES6脚本中使用以下功能:
Symbol
对象class
关键字构建的类(...)=>{...}
)函数注意:只能使用eval()
函数或其他等效函数(例如Function()
)对新引入的语法进行特性检测(如箭头函数),因为编写无效的语法将在执行之前停止脚本。这也是为什么您不能使用if
语句检测类和箭头函数的原因:这些功能涉及关键字和语法,因此在try {...} catch (e) {...}
块中包装的eval(...)
将正常工作。
因此,来看真正的代码:
HTML Markup:
<html>
<head>
<script src="es5script.js"></script>
</head>
<body>
<!-- ... -->
</body>
</html>
Code in your es5script.js
script:
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 :(
}
Code in your 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
,但请考虑使用另一种方法,因为那一个以及许多其他navigator属性已被废弃,并且不总是像您想象的那样运行。
所以,再次引用MDN文档 [链接]:
已弃用:此特性已从Web标准中删除。虽然某些浏览器仍可能支持它,但正在逐步停止使用。不要在旧或新项目中使用它。使用它的页面或Web应用程序可能随时会崩溃。
注意:不要依赖该属性返回正确的浏览器版本。在基于Gecko的浏览器(如Firefox)和基于WebKit的浏览器(如Chrome和Safari)中,返回的值以“5.0”开头,后面跟有平台信息。在Opera 10及更高版本中,返回的版本号也与实际的浏览器版本不匹配。
"".startsWith
之类的东西,但这一直很容易。要检测基于语法的功能,您需要使用eval()将无效代码隐藏在ES5解析器中。 - dandavisengineVersion: es5
或engineVersion: es6
来显示它是否支持,如果打开/关闭了flags
,也应该起作用,这样您就不必依赖js script
标签来加载资源。服务器应该能够提供正确的文件以供使用或遗留。但这仍然是一种观点 :) - Val现在支持 ES6 模块的浏览器厂商提供了一种简单的功能检测方法:
Browser vendors that support ES6 modules now provide an easy way to do feature detection:...
<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;
}
}
eval()
或 Function()
与未指定 'unsafe-eval'
的内容安全策略不兼容。 - Damian Yerrick你可以通过在自己的脚本块中插入检测代码并在结尾进行全局变量赋值来实现,而无需使用eval。如果脚本块中发生任何错误,变量赋值将不会运行。
<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有许多功能,仅检查一个功能并不能保证你已经涵盖了所有功能。(上面的代码也没有覆盖所有内容,只是我认为我最常使用的功能)。undefined
是一个假值。因此,在第一个 script
中您不需要初始化。 - cevingboolean
类型,而不是 true | undefined
。 - Maciej Krawczyk(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);
}
})()
目前还没有确切的方法来检测ES6,但是如果在当前浏览器中测试其功能,则可以确定引擎是否为ES6。我的 esx 库通过进行语法测试和方法检查来检测ECMAScript版本。它可以检测ECMAScript 3、5、6和7(未经测试的ES7应该可以工作),如果没有匹配到ECMAScript测试,则返回null
作为结果。
使用我的库的示例:
if (esx.detectVersion() >= 6) {
/* We're in ES6 or above */
}
esdetect.js
文件第 46 行的 eval()
使用与未指定 'unsafe-eval'
的内容安全策略不兼容。 - Damian Yerrick<script>
// This script block should not compile on incompatible browsers,
// leaving the function name undefined.
// It can then be polyfilled with a function containing compatible syntax code.
function fame() {
/* incompatible syntax code such as arrow functions */
}
</script>
<script>
if (typeof fame !== "function") {
// alert("polyfill: fame");
function fame() {
/* compatible syntax code */
}
}
</script>
<script>
// main code
fame();
</script>
此函数在 Chrome 98.0.4758.80 和 Firefox 97.0.2 中返回 true(已测试)。它可能无法在其他浏览器和早期版本的 Chrome/Firefox 上工作(会出现错误的负面结果)
function hasAsyncSupport () {
return Object.getPrototypeOf(async function() {}).constructor.toString().includes('Async')
}
我认为最好的方法是将所有ES6脚本都编写为模块,并对任何回退使用nomodule脚本标记。尝试编写内联检测是不必要的。
当然,这引出了一个问题,如何检测来自ES6后版本的新语法,例如"??"和"?."运算符。
使用UAParser.js检测浏览器是否支持ECMAScript 6。
function supportsES6(engine) {
if (typeof engine.name === 'undefined' || typeof engine.version === 'undefined') {
return false;
}
if (engine.name in MinimumForES6) {
return parseInt(engine.version) >= MinimumForES6[engine.name];
}
return false;
}
/*
ref:
- https://caniuse.com/es6
- https://www.lambdatest.com/web-technologies/es6
*/
var MinimumForES6 = {
"Blink": 51,
"EdgeHTML": 15,
"Gecko": 54,
"WebKit": 602
};
window.addEventListener('DOMContentLoaded', function() {
var ua_parser = new UAParser();
var ua_result = ua_parser.getResult();
window['isES6'] = supportsES6(ua_result.engine);
console.log('isEs6: '+ window.isES6);
});
<script src="https://cdn.jsdelivr.net/npm/ua-parser-js@1.0.33/src/ua-parser.min.js"></script>