Javascript ES6 跨浏览器检测
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29046635/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Javascript ES6 cross-browser detection
提问by Knock Yang
How can I find out the browser's Javascript engine version and support to ECMAScript 6?
如何找出浏览器的 Javascript 引擎版本和对 ECMAScript 6 的支持?
I'm using navigator.appVersionjust to know the browser's version, but not the engine's version.
我navigator.appVersion只是想知道浏览器的版本,而不是引擎的版本。
回答by Marco Bonelli
Feature detection
特征检测
I suggest you to use feature detectioninstead of detecting the browser's engine with heuristic methods. To do this you can simply wrap some code inside a try {..} catch (e) {...}statement, or use some if (...)statements.
我建议您使用特征检测而不是使用启发式方法检测浏览器的引擎。为此,您可以简单地将一些代码包装在一个try {..} catch (e) {...}语句中,或者使用一些if (...)statements。
For example:
例如:
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 :(
}
Why is feature detection better than browser/engine detection?
为什么特征检测比浏览器/引擎检测更好?
There are multiple reasons that make, in most of the cases, feature detection the best option:
在大多数情况下,有多种原因使特征检测成为最佳选择:
You don't have to rely on browser's version, engine or specifics, nor detect them using heuristic methods which are hard and pretty crafty to implement.
You will not fall into errors regarding browser/engine specifications detection.
You don't have to worry about browser-specific features: for example WebKitbrowsers have different specifications than other ones.
You can be sure that, once a feature is detected, you'll be able to use it.
您不必依赖浏览器的版本、引擎或细节,也不必使用难以实现且非常狡猾的启发式方法来检测它们。
您不会陷入有关浏览器/引擎规格检测的错误中。
您不必担心特定于浏览器的功能:例如,WebKit浏览器的规范与其他浏览器不同。
您可以确定,一旦检测到某个功能,您就可以使用它。
These are the main reasons that IMHO make feature detection the best approach.
这些是恕我直言使特征检测成为最佳方法的主要原因。
Feature detection + fallback
特征检测+回退
When using feature detection, a pretty smart way to work when you aren't sure which features you can/cannot use consists in several feature detections and consequent fallbacksto more basic methods(or even creation of these methods from scratch) in case the features you want to use are not supported.
使用特征检测时,当您不确定可以/不能使用哪些特征时,一种非常聪明的工作方式包括多个特征检测和随后对更基本方法的回退(甚至从头开始创建这些方法),以防万一不支持您要使用。
A simple example of feature detection with fallbackmay be applied to the window.requestAnimationFramefeature, which is not supported by all the browsers, and has several different prefixes depending on the browser you're working on. In this case, you can easily detect and fallbacklike this:
具有回退功能的特征检测的简单示例可能适用于该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);
ECMAScript 6 (Harmony) features detection
ECMAScript 6 (Harmony) 特征检测
Now, coming to the real problem: if you want to detect the support to ES6, you'll not be able to behave like I said above, because a relevant range of ES6 features is based on new syntaxes and private words, and will throw a SyntaxErrorif used in ES5, which means that writing a script which contains both ES5 and ES6 is impossible!
现在,真正的问题来了:如果你想检测对 ES6 的支持,你将无法像我上面说的那样表现,因为ES6 的一系列相关特性是基于新的语法和私有词,并且会抛出a SyntaxErrorif 在 ES5 中使用,这意味着编写包含 ES5 和 ES6 的脚本是不可能的!
Here is an example to demonstrate this issue; the below snippet won't work, and it will be blocked before execution because contains illegal syntax.
这是一个演示此问题的示例;下面的代码段不起作用,它会在执行前被阻止,因为包含非法语法。
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; }
}
Now, since that you cannot both check and execute ES6 conditionally in the same script, you'll have to write two different scripts: one which only uses ES5, and another one which includes ES6 features. With two different scripts you'll be able to import the ES6 one only if it is supported, and without causing SyntaxErrorsto be thrown.
现在,由于您不能在同一个脚本中同时有条件地检查和执行 ES6,您必须编写两个不同的脚本:一个只使用 ES5,另一个包含 ES6 功能。使用两种不同的脚本,您将能够导入 ES6 中的一种,前提是它受支持,而不会导致SyntaxErrors抛出。
ES6 detection and conditional execution example
ES6 检测和条件执行示例
Now let's make a more relatable example, and let's say you want to use these features in your ES6 script:
现在让我们做一个更相关的例子,假设你想在你的 ES6 脚本中使用这些特性:
- The new
Symbolobjects - Classes built with the
classkeyword - Arrow (
(...)=>{...}) functions
- 新
Symbol对象 - 使用
class关键字构建的类 - 箭头 (
(...)=>{...}) 函数
NOTE:feature detectionof newly introduced syntaxes(like arrow functions) can only be done using the eval()functionor other equivalents (e.g. Function()), because writing invalid syntax will stop the script before its execution. This is also the reason why you cannot use ifstatements to detect classes and arrow functions: these features are regarding keywords and syntax, so an eval(...)wrapped inside a try {...} catch (e) {...}block will work fine.
注意:新引入的语法(如箭头函数)的特征检测只能使用该eval()函数或其他等效项(例如Function())来完成,因为编写无效的语法会在脚本执行之前停止脚本。这也是你不能使用if语句来检测类和箭头函数的原因:这些特性是关于关键字和语法的,所以eval(...)包裹在try {...} catch (e) {...}块中可以正常工作。
So, coming to the real code:
所以,来到真正的代码:
HTML Markup:
<html> <head> <script src="es5script.js"></script> </head> <body> <!-- ... --> </body> </html>Code in your
es5script.jsscript: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!
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!
Browser/engine detection
浏览器/引擎检测
Like I said above, browser and engine detection are not the best practices when programming some JavaScript script. I'm gonna give you some background on this topic, just not to leave my words as a "random personal opinion".
就像我上面说的,浏览器和引擎检测在编写 JavaScript 脚本时并不是最佳实践。我会给你一些关于这个话题的背景,只是不要把我的话当作“随机的个人意见”。
Quoting from the MDN Documentation [link]:
引用 MDN 文档 [链接]:
When considering using the user agent string to detect which browser is being used, your first step is to try to avoid it if possible. Start by trying to identify why you want to do it.
[...] Are you trying to check for the existence of a specific feature?Your site needs to use a specific Web feature that some browsers don't yet support, and you want to send those users to an older Web site with fewer features but that you know will work. This is the worst reason to use user agent detection, because odds are eventually all the other browsers will catch up. You should do your best to avoid using user agent sniffing in this scenario, and do feature detection instead.
在考虑使用用户代理字符串来检测正在使用的浏览器时,您的第一步是尽可能避免使用它。首先尝试确定您为什么要这样做。
[...]您是否正在尝试检查特定功能的存在?您的站点需要使用某些浏览器尚不支持的特定 Web 功能,并且您希望将这些用户发送到具有较少功能但您知道可以使用的旧网站。这是使用用户代理检测的最糟糕的原因,因为很可能最终所有其他浏览器都会迎头赶上。在这种情况下,您应该尽量避免使用用户代理嗅探,而是进行特征检测。
Also, you're saying you use navigator.appVersion, but consider using another approach, because that one, together with many other navigator properties, is deprecated, and doesn't always behave like you think.
此外,您是说您使用navigator.appVersion,但请考虑使用另一种方法,因为该方法与许多其他导航器属性一起已被弃用,并且其行为并不总是像您想象的那样。
So, quoting from the MDN Documentation [link] again:
因此,再次引用 MDN 文档 [链接]:
Deprecated: this feature has been removed from the Web standards. Though some browsers may still support it, it is in the process of being dropped. Do not use it in old or new projects. Pages or Web apps using it may break at any time.
Note:Do not rely on this property to return the correct browser version. In Gecko-based browsers (like Firefox) and WebKit-based browsers (like Chrome and Safari) the returned value starts with "5.0" followed by platform information. In Opera 10 and newer the returned version does not match the actual browser version, either.
已弃用:此功能已从 Web 标准中删除。虽然一些浏览器可能仍然支持它,但它正在被删除。不要在旧项目或新项目中使用它。使用它的页面或 Web 应用程序可能随时中断。
注意:不要依赖此属性来返回正确的浏览器版本。在基于 Gecko 的浏览器(如 Firefox)和基于 WebKit 的浏览器(如 Chrome 和 Safari)中,返回值以“5.0”开头,后跟平台信息。在 Opera 10 和更新版本中,返回的版本与实际的浏览器版本也不匹配。
回答by Casey
Browser vendors that support ES6 modules now provide an easy way to do feature detection:
支持 ES6 模块的浏览器供应商现在提供了一种简单的方法来进行特征检测:
...
<head>
<script nomodule>window.nomodules = true;</script>
<script>console.log(window.nomodules)</script>
</head>
...
The script with the nomoduleattribute will not be excuted by browsers which support <script type="module" ...>
具有该nomodule属性的脚本不会被支持的浏览器执行<script type="module" ...>
You can also inject the script like this:
您还可以像这样注入脚本:
const script = document.createElement('script');
script.setAttribute('nomodule', '');
script.innerHTML = 'window.nomodules = true;';
document.head.insertBefore(script, document.head.firstChild);
script.remove();
回答by Martin Wantke
As Marco Bonelli said, the best way to detect ECMAScript 6 language syntax is to use eval();. If the call does not throw an error, "all other" features are supported, but I recommend Function();.
正如 Marco Bonelli 所说,检测 ECMAScript 6 语言语法的最佳方法是使用eval(); . 如果调用没有抛出错误,则支持“所有其他”功能,但我推荐Function(); .
function isES6()
{
try
{
Function("() => {};"); return true;
}
catch(exception)
{
return false;
}
}
回答by Sing
- Detect devicePixelRatio which is a special property in WebKit.
- Detect javaEnabled function's implement.
- 检测 devicePixelRatio 这是WebKit 中的一个特殊属性。
- 检测 javaEnabled 函数的实现。
(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);
}
})()
回答by Hydroper
For now there's not a exact way to detect ES6, but if you test its features in the current browser, you can determine if the engine is ES6. My esxlibrary detects the ECMAScript version by doing syntax tests and methods check. For know it can detect ECMAScript 3, 5, 6 and 7 (ES7 not tested, but should work), if no ECMAScript test matched, it gives nullas result.
目前还没有一个准确的方法来检测 ES6,但是如果你在当前浏览器中测试它的特性,你可以确定引擎是否是 ES6。我的esx库通过进行语法测试和方法检查来检测 ECMAScript 版本。要知道它可以检测 ECMAScript 3、5、6 和 7(ES7 未测试,但应该可以工作),如果没有匹配的 ECMAScript 测试,它会给出null结果。
Example using my library:
使用我的库的示例:
if (esx.detectVersion() >= 6) {
/* We're in ES6 or above */
}
回答by Bedrock
As Damian Yerrick has mentioned, the use of eval() or Function() is incompatible with a Content Security Policy that does not specify 'unsafe-eval'.
正如 Damian Yerrick 所提到的,使用 eval() 或 Function() 与未指定“unsafe-eval”的内容安全策略不兼容。
If the browser supports Worker then you can detect support for any ES6 syntax by implementing that syntax in a worker and checking for error or successs eg to detect support for arrow functions:
如果浏览器支持 Worker,那么您可以通过在 Worker 中实现该语法并检查错误或成功来检测对任何 ES6 语法的支持,例如检测对箭头函数的支持:
worker.js
工人.js
// If ES6 arrow functions are supported then the worker listener will receive true, otherwise it will receive an error message
(() => {
postMessage(true);
})();
index.js
索引.js
if (typeof (Worker) !== "undefined") {
var myWorker = new Worker('worker.js');
myWorker.onmessage = function (e) {
// arrow functions must be supported since we received message from the worker arrow function
}
myWorker.onerror = function (e) {
// the worker triggered an error so arrow function not supported (could explicitly check message for syntax error)
}
}
回答by NOYB
Put the incompatible syntax code, such as containing arrow functions, in it's own script block and polyfill it with compatible syntax code.
将不兼容的语法代码(例如包含箭头函数)放入其自己的脚本块中,并使用兼容的语法代码对其进行 polyfill。
<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>

