Javascript ES6跨浏览器检测

时间:2015-03-14 07:39:12

标签: javascript ecmascript-6

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

我只是使用navigator.appVersion来了解浏览器的版本,而不是引擎的版本。

8 个答案:

答案 0 :(得分:102)

特征检测

我建议您使用 功能检测 ,而不是使用启发式方法检测浏览器的引擎。要执行此操作,您只需将一些代码包装在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功能,所有浏览器都不支持该功能,并且根据您的浏览器具有多个不同的前缀&# 39;重新开始工作。在这种情况下,您可以像这样轻松检测并后备

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)具有检测功能

现在,来到真正的问题:如果你想检测对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 contains illegal syntax
} else {
    var bar = function(arg) { return arg; }
}

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

ES6检测和条件执行示例

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

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

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

所以,来到真正的代码:

  • HTML标记:

    try {...} catch (e) {...}
  • <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

浏览器/引擎检测

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

引自MDN文档[link]:

  

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

     

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

另外,您是说您使用// 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! ,但考虑使用其他方法,因为该方法与许多其他导航器属性一起已弃用,并且不会使用; t总是表现得像你想的那样。

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

  

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

     

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

答案 1 :(得分:7)

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

...
<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();

答案 2 :(得分:5)

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

print(response.json())

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

答案 3 :(得分:3)

  1. 检测devicePixelRatio,它是WebKit中的特殊属性。
  2. 检测javaEnabled函数的工具。
  3. (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);
      }
    
    })()

答案 4 :(得分:1)

目前还没有一种确切的方法来检测ES6,但是如果你在当前浏览器中测试它的功能,你可以确定引擎是否是ES6。我的esx库通过语法测试和方法检查来检测ECMAScript版本。要知道它可以检测ECMAScript 3,5,6和7(ES7未测试,但应该有效),如果没有ECMAScript测试匹配,则结果为null

使用我的库的示例:

if (esx.detectVersion() >= 6) {
    /* We're in ES6 or above */
}

答案 5 :(得分:1)

如果由于某种原因不能使用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

答案 6 :(得分:0)

如Damian Yerrick所述,eval()或Function()的使用与未指定“ unsafe-eval”的内容安全策略不兼容。

如果浏览器支持Worker,那么您可以通过在worker中实现该语法并检查错误或成功来检测对任何ES6语法的支持,例如,检测对箭头功能的支持:

worker.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

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)
    }
}

答案 7 :(得分:-1)

将不兼容的语法代码(例如包含箭头函数)放在其自己的脚本块中,并使用兼容的语法代码对其进行填充。

<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>