Javascript ES6跨浏览器检测

Kno*_*ang 60 javascript ecmascript-6

如何找到浏览器的Javascript引擎版本并支持ECMAScript 6?

navigator.appVersion只是想知道浏览器的版本,而不是引擎的版本.

Mar*_*lli 105

特征检测

我建议您使用特征检测,而不是使用启发式方法检测浏览器的引擎.要做到这一点,您可以简单地将一些代码包装在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 :(
}
Run Code Online (Sandbox Code Playgroud)

为什么功能检测比浏览器/引擎检测更好?

在大多数情况下,有多种原因可以使功能检测成为最佳选择:

  • 您不必依赖浏览器的版本,引擎或细节,也不必使用难以实现且非常狡猾的启发式方法来检测它们.

  • 您不会遇到有关浏览器/引擎规格检测的错误.

  • 您不必担心特定于浏览器的功能:例如,WebKit浏览器的规格与其他浏览器不同.

  • 您可以确定,一旦检测到某个功能,您就可以使用它.

这些是IMHO使特征检测成为最佳方法的主要原因.

特征检测+后备

当使用特征检测时,当您不确定哪些功能可以/不可以使用时,一种非常聪明的工作方式包括多个功能检测以及随后的更多基本方法的后备(甚至从头开始创建这些方法)以防功能你想要使用不受支持.

使用回退功能检测的简单示例可以应用于该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);
Run Code Online (Sandbox Code Playgroud)

ECMAScript 6(Harmony)具有检测功能

现在,遇到真正的问题:如果你想检测对ES6的支持,你将无法像我上面说的那样表现,因为相关的ES6功能范围是基于新的语法和私有词,并且会抛出一SyntaxError,如果用在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 contains illegal syntax
} else {
    var bar = function(arg) { return arg; }
}
Run Code Online (Sandbox Code Playgroud)

现在,由于您无法在同一脚本中有条件地检查和执行ES6,因此您必须编写两个不同的脚本:一个仅使用ES5,另一个包含ES6功能.使用两个不同的脚本,只有在支持的情况下才能导入ES6,并且不会导致SyntaxErrors抛出.

ES6检测和条件执行示例

现在让我们做一个更相关的例子,假设你想在ES6脚本中使用这些功能:

  • Symbol对象
  • 使用class关键字构建的类
  • 箭头((...)=>{...})函数

注意: 新引入的语法(如箭头函数)的特征检测只能使用eval()函数或其他等价物(例如Function())来完成,因为编写无效语法将在脚本执行之前停止.这也是您无法使用if语句来检测类和箭头函数的原因:这些功能与关键字和语法有关,因此eval(...)封装在try {...} catch (e) {...}块内部可以正常工作.

所以,来到真正的代码:

  • HTML标记:

    <html>
        <head>
            <script src="es5script.js"></script>
        </head>
        <body>
            <!-- ... -->
        </body>
    </html>
    
    Run Code Online (Sandbox Code Playgroud)
  • 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 :(
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 您的代码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!
    
    Run Code Online (Sandbox Code Playgroud)

浏览器/引擎检测

就像我上面所说的,在编写一些JavaScript脚本时,浏览器和引擎检测不是最佳实践.我将给你一些关于这个主题的背景知识,只是不要将我的话作为"随意的个人意见".

引用MDN文档[ link ]:

在考虑使用用户代理字符串来检测正在使用哪个浏览器时,您的第一步是尝试尽可能避免使用它.首先尝试确定您要执行此操作的原因.

[...] 您是否正在尝试检查特定功能的存在? 您的站点需要使用某些浏览器尚不支持的特定Web功能,并且您希望将这些用户发送到功能较少但您知道可以使用的旧网站.这是使用用户代理检测的最坏理由,因为最终所有其他浏览器都会遇到赔率.您应该尽力避免在此方案中使用用户代理嗅探,而是进行功能检测.

此外,您说您正在使用navigator.appVersion,但考虑使用其他方法,因为该方法与许多其他导航器属性一起被弃用,并且并不总是像您想象的那样.

所以,再次引用MDN文档[ link ]:

不推荐使用:此功能已从Web标准中删除.虽然有些浏览器可能仍然支持它,但它正在被删除.不要在旧项目或新项目中使用它.使用它的页面或Web应用程序可能随时中断.

注意:不要依赖此属性来返回正确的浏览器版本.在基于Gecko的浏览器(如Firefox)和基于WebKit的浏览器(如Chrome和Safari)中,返回值以"5.0"开头,后跟平台信息.在Opera 10及更新版本中,返回的版本也与实际的浏览器版本不匹配.

  • 就像一个注释:有一个表格显示ES6支持https://kangax.github.io/compat-table/es6/ (2认同)
  • "功能检测"无法正常工作,因为您无法尝试/抓住像胖箭头,_let_和_class_这样的语法错误.它可以检查像""".startsWith`这样的东西,但这总是很容易.要检测基于语法的功能,您需要使用eval()来隐藏ES5解析器中的无效代码. (2认同)

Cas*_*sey 8

支持ES6模块的浏览器供应商现在提供了一种简单的功能检测方法:

...
<head>
  <script nomodule>window.nomodules = true;</script>
  <script>console.log(window.nomodules)</script>
</head>
...
Run Code Online (Sandbox Code Playgroud)

具有该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();
Run Code Online (Sandbox Code Playgroud)


Mac*_*zyk 7

没有 eval ES6 功能检测

您可以在不使用 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>
Run Code Online (Sandbox Code Playgroud)

https://jsfiddle.net/s5tqow91/2/

请注意,ES6 功能有很多,仅检查一项并不能保证您已涵盖其中。(上面的代码也没有涵盖所有内容,这只是我认为我最常使用的功能)。

为什么没有评估?

主要原因是安全性,并不是说调用 eval 进行特征检测本身就是不安全的。理想情况下,您应该禁止使用内容安全策略进行评估,因此根本无法使用它 - 这大大减少了攻击面。但如果你自己的代码使用了eval,你就不能这样做。


Mar*_*tke 6

正如Marco Bonelli所说的,检测ECMAScript 6语言语法的最佳方法是使用eval()。。如果调用没有引发错误,则支持“所有其他”功能,但我建议使用Function();。

function isES6()
{
    try
    {
        Function("() => {};"); return true;
    }
    catch(exception)
    {
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

演示: https : //jsfiddle.net/uma4Loq7/

  • 使用eval()或Function()与未指定“ unsafe-eval”的内容安全策略不兼容。 (2认同)