Browserübergreifende Erkennung von Javascript ES6

73

Wie kann ich die Javascript-Engine-Version des Browsers und die Unterstützung für ECMAScript 6 herausfinden?

Ich verwende navigator.appVersionnur, um die Version des Browsers zu kennen, aber nicht die Version der Engine.

Schlag Yang
quelle
Warum willst du das wissen?
Es gibt keine Standardeigenschaft, die Sie einfach lesen können, um diese Informationen zu erhalten. Sie können ein Wörterbuch oder bekannte Plattformen erstellen oder Funktionstests verwenden.
Dandavis
2
Wenn Sie eine neue Syntax wünschen, verwenden Sie einen Pre-Compiler, der Javascript ausgibt, das mit älteren Versionen kompatibel ist. Wenn Sie neue Funktionen wünschen, verwenden Sie die Funktionserkennung, wie von Marco Bonelli empfohlen.
CodesInChaos
1
Sie können die Feature-Detect-Es6- Bibliothek verwenden.
Lloyd

Antworten:

119

Funktionserkennung

Ich empfehle Ihnen, die Feature-Erkennung zu verwenden, anstatt die Engine des Browsers mit heuristischen Methoden zu erkennen. Dazu können Sie einfach Code in eine try {..} catch (e) {...}Anweisung einschließen oder einige if (...)Anweisungen verwenden .

Zum Beispiel:

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 :(
}

Warum ist die Funktionserkennung besser als die Browser- / Engine-Erkennung?

Es gibt mehrere Gründe, die in den meisten Fällen die Feature-Erkennung zur besten Option machen:

  • Sie müssen sich nicht auf die Version, die Engine oder die Besonderheiten des Browsers verlassen oder diese mit heuristischen Methoden erkennen, die schwer und recht geschickt zu implementieren sind.

  • Bei der Erkennung von Browser- / Engine-Spezifikationen treten keine Fehler auf.

  • Sie müssen sich nicht um browserspezifische Funktionen kümmern: Beispielsweise haben WebKit- Browser andere Spezifikationen als andere.

  • Sie können sicher sein, dass Sie eine Funktion verwenden können, sobald sie erkannt wurde.

Dies sind die Hauptgründe, warum IMHO die Feature-Erkennung zum besten Ansatz macht.

Funktionserkennung + Fallback

Wenn Sie die Feature-Erkennung verwenden , besteht eine ziemlich clevere Arbeitsweise, wenn Sie nicht sicher sind, welche Features Sie verwenden können / nicht, in mehreren Feature-Erkennungen und den daraus resultierenden Rückgriffen auf grundlegendere Methoden (oder sogar die Erstellung dieser Methoden von Grund auf neu), falls die Features vorhanden sind Sie möchten verwenden, werden nicht unterstützt.

Ein einfaches Beispiel für die Feature-Erkennung mit Fallback kann auf das window.requestAnimationFrameFeature angewendet werden , das nicht von allen Browsern unterstützt wird und je nach dem Browser, an dem Sie arbeiten, verschiedene Präfixe hat. In diesem Fall können Sie Folgendes leicht erkennen und zurückgreifen :

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

Erkennung von ECMAScript 6 (Harmony) -Funktionen

Kommen wir nun zum eigentlichen Problem : Wenn Sie die Unterstützung für ES6 erkennen möchten, können Sie sich nicht wie oben beschrieben verhalten, da ein relevanter Bereich von ES6-Funktionen auf neuen Syntaxen und privaten Wörtern basiert und ausgelöst wird a SyntaxErrorWenn in ES5 verwendet , bedeutet dies, dass das Schreiben eines Skripts, das sowohl ES5 als auch ES6 enthält, unmöglich ist!

Hier ist ein Beispiel, um dieses Problem zu demonstrieren. Das folgende Snippet funktioniert nicht und wird vor der Ausführung blockiert, da es eine unzulässige Syntax enthält.

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

Da Sie ES6 nicht im selben Skript prüfen und bedingt ausführen können, müssen Sie zwei verschiedene Skripte schreiben : eines, das nur ES5 verwendet, und eines, das ES6-Funktionen enthält. Mit zwei verschiedenen Skripten können Sie das ES6-Skript nur dann importieren, wenn es unterstützt wird und ohne dass SyntaxErrorses ausgelöst wird.

Beispiel für ES6-Erkennung und bedingte Ausführung

Lassen Sie uns nun ein aussagekräftigeres Beispiel erstellen und diese Funktionen in Ihrem ES6-Skript verwenden:

  • Die neuen SymbolObjekte
  • Mit dem classSchlüsselwort erstellte Klassen
  • Arrow ( (...)=>{...}) -Funktionen

HINWEIS: Die Funktionserkennung neu eingeführter Syntaxen (wie Pfeilfunktionen) kann nur mit der eval()Funktion oder anderen Entsprechungen (z. B. Function()) durchgeführt werden, da das Schreiben einer ungültigen Syntax das Skript vor seiner Ausführung stoppt. Dies ist auch der Grund, warum Sie keine ifAnweisungen zum Erkennen von Klassen und Pfeilfunktionen verwenden können: Diese Funktionen beziehen sich auf Schlüsselwörter und Syntax, sodass ein eval(...)in einen try {...} catch (e) {...}Block eingeschlossenes Element einwandfrei funktioniert.

Kommen wir also zum eigentlichen Code:

  • HTML-Markup:

    <html>
        <head>
            <script src="es5script.js"></script>
        </head>
        <body>
            <!-- ... -->
        </body>
    </html>
    
  • Code in Ihrem es5script.jsSkript:

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

Wie oben erwähnt, sind die Browser- und Engine-Erkennung nicht die besten Methoden beim Programmieren eines JavaScript-Skripts. Ich werde Ihnen einige Hintergrundinformationen zu diesem Thema geben, nur um meine Worte nicht als "zufällige persönliche Meinung" zu hinterlassen.

Zitat aus der MDN-Dokumentation [ Link ]:

Wenn Sie die Benutzeragentenzeichenfolge verwenden möchten, um festzustellen, welcher Browser verwendet wird, versuchen Sie zunächst, dies nach Möglichkeit zu vermeiden. Versuchen Sie zunächst herauszufinden, warum Sie dies tun möchten.

[...] Versuchen Sie, das Vorhandensein einer bestimmten Funktion zu überprüfen? Ihre Website muss eine bestimmte Webfunktion verwenden, die einige Browser noch nicht unterstützen. Sie möchten diese Benutzer an eine ältere Website mit weniger Funktionen senden, von der Sie jedoch wissen, dass sie funktioniert. Dies ist der schlechteste Grund für die Verwendung der Benutzeragentenerkennung, da die Wahrscheinlichkeit besteht, dass alle anderen Browser aufholen. Sie sollten Ihr Bestes tun, um in diesem Szenario das Sniffing von Benutzeragenten zu vermeiden, und stattdessen die Funktionserkennung durchführen .

Sie sagen auch, dass Sie verwenden navigator.appVersion, erwägen jedoch einen anderen Ansatz, da dieser zusammen mit vielen anderen Navigatoreigenschaften veraltet ist und sich nicht immer so verhält, wie Sie denken.

Also noch einmal aus der MDN-Dokumentation [ Link ] zitieren :

Veraltet : Diese Funktion wurde aus den Webstandards entfernt. Obwohl einige Browser dies möglicherweise noch unterstützen, wird es gerade gelöscht. Verwenden Sie es nicht in alten oder neuen Projekten. Seiten oder Web-Apps, die es verwenden, können jederzeit beschädigt werden.

Hinweis: Verlassen Sie sich nicht auf diese Eigenschaft, um die richtige Browserversion zurückzugeben. In Gecko-basierten Browsern (wie Firefox) und WebKit-basierten Browsern (wie Chrome und Safari) beginnt der zurückgegebene Wert mit "5.0", gefolgt von Plattforminformationen. In Opera 10 und höher stimmt die zurückgegebene Version auch nicht mit der tatsächlichen Browserversion überein.

Marco Bonelli
quelle
Hmm, wirklich nur Tests zur Unterstützung von Symbol. Ich glaube nicht, dass eine Engine alle von ES6 vorgeschlagenen Funktionen unterstützt. Sie müssen nach allen ES6-Funktionen suchen, die Sie verwenden möchten.
HBP
2
Nur als Hinweis: Es gibt eine Tabelle mit der ES6-Unterstützung kangax.github.io/compat-table/es6
zaynetro
2
Diese "Funktionserkennung" funktioniert nicht, da Sie nicht versuchen können, einen Syntaxfehler wie fette Pfeile, let und class zu umgehen . es kann nach Dingen wie suchen "".startsWith, aber das war schon immer einfach. Um syntaxbasierte Funktionen zu erkennen, müssen Sie eval () verwenden, um den ungültigen Code vor dem ES5-Parser auszublenden.
Dandavis
1
@ user3412159 Ich habe meine Antwort bearbeitet! Bitte schauen Sie sich das neue an, da das vorherige völlig falsch war! Vielen Dank.
Marco Bonelli
1
hmm. Mit den oben genannten Methoden ist es ein Hack oder eine Umgehung. Ich bin der Meinung, dass ein Browser dem Server mitteilen sollte, welche Datei bereitgestellt werden soll, oder die Option dazu geben sollte. Es sollte also einen Anforderungsheader wie diesen geben engineVersion: es5oder um engineVersion: es6zu zeigen, dass er dies unterstützt. Wenn dies auch funktionieren sollte, wenn flagses auf on/offdiese Weise gedreht wird, müssen Sie sich nicht auf ein js- scriptTag verlassen, um Ihre Assets zu laden. Der Server sollte in der Lage sein, die richtige Datei oder das richtige Legacy bereitzustellen. aber das ist immer noch eine Meinung :)
Val
13

Browser-Anbieter, die ES6-Module unterstützen, bieten jetzt eine einfache Möglichkeit zur Funktionserkennung:

...
<head>
  <script nomodule>window.nomodules = true;</script>
  <script>console.log(window.nomodules)</script>
</head>
...

Das Skript mit dem nomoduleAttribut wird von unterstützenden Browsern nicht ausgeführt<script type="module" ...>

Sie können das Skript auch folgendermaßen einfügen:

const script = document.createElement('script');
script.setAttribute('nomodule', '');
script.innerHTML = 'window.nomodules = true;';
document.head.insertBefore(script, document.head.firstChild);
script.remove();
Casey
quelle
7

Wie Marco Bonelli sagte, ist die Verwendung von eval () der beste Weg, um die Sprachsyntax von ECMAScript 6 zu erkennen . . Wenn der Aufruf keinen Fehler auslöst, werden "alle anderen" Funktionen unterstützt, aber ich empfehle Function (); .

function isES6()
{
    try
    {
        Function("() => {};"); return true;
    }
    catch(exception)
    {
        return false;
    }
}

Demo: https://jsfiddle.net/uma4Loq7/

Martin Wantke
quelle
3
Die Verwendung von eval()oder Function()ist nicht kompatibel mit einer Inhaltssicherheitsrichtlinie, die nicht angegeben ist 'unsafe-eval'.
Damian Yerrick
Wenn Sie Zugriff auf index.html haben, können Sie den Ausdruck einfach mit einem <script> -Tag auswerten. Siehe meine Antwort unten.
Maciej Krawczyk
Das Erkennen der Unterstützung von Pfeilfunktionen deckt nicht alle ES6-Funktionen ab. caniuse.com/es6-class,arrow-functions
Maciej Krawczyk
3
  1. Erkennen Sie devicePixelRatio, eine spezielle Eigenschaft in WebKit .
  2. Erkennen Sie das Gerät der Funktion 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);
  }

})()

Singen
quelle
1
Das ist schön, aber ich bin gespannt, wo ich die Engine-Version auf Firefox direkt sehen kann.
RoliCo
5
if (window.devicePixelRatio) //If WebKit browser<= Das ist schlecht. Lesen Sie die Antwort von Marco Bonelli, warum Sie nicht die Browsererkennung verwenden sollten, sondern die Funktionserkennung.
Arturo Torres Sánchez
1

Derzeit gibt es keine genaue Methode zum Erkennen von ES6. Wenn Sie jedoch die Funktionen im aktuellen Browser testen, können Sie feststellen, ob es sich bei der Engine um ES6 handelt. Meine esx- Bibliothek erkennt die ECMAScript-Version durch Syntaxtests und Methodenprüfungen. Wissen Sie, dass es ECMAScript 3, 5, 6 und 7 erkennen kann (ES7 nicht getestet, sollte aber funktionieren), wenn kein ECMAScript-Test übereinstimmt, gibt es nullals Ergebnis.

Beispiel mit meiner Bibliothek:

if (esx.detectVersion() >= 6) {
    /* We're in ES6 or above */
}
Hydroper
quelle
2
Die Verwendung von eval()Zeile 46 in esdetect.jsdiesem Repository ist nicht mit einer Inhaltssicherheitsrichtlinie kompatibel, die nicht angegeben ist 'unsafe-eval'.
Damian Yerrick
0

Wie Damian Yerrick erwähnt hat, ist die Verwendung von eval () oder Function () nicht mit einer Richtlinie zur Inhaltssicherheit kompatibel, in der nicht "unsicher-eval" angegeben ist.

Wenn der Browser Worker unterstützt, können Sie die Unterstützung für jede ES6-Syntax erkennen, indem Sie diese Syntax in einem Worker implementieren und auf Fehler oder Erfolge prüfen, z. B. um die Unterstützung für Pfeilfunktionen zu erkennen:

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)
    }
}
Grundgestein
quelle
0

Wenn Sie eval aus irgendeinem Grund nicht verwenden können, können Sie dies global erkennen, indem Sie den Skriptcode in einen eigenen Skriptblock einfügen (und entweder den Wert einer Variablenzuweisung im Block testen oder window.onerror verwenden).

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

Bitte beachten Sie, dass es viele ES6-Funktionen gibt und die Überprüfung nur einer nicht garantiert, dass Sie abgedeckt sind. (Der obige Code deckt auch nicht alles ab, ich denke, es sind genau die Funktionen, die ich am häufigsten benutze).

Außerdem gibt es eine ziemlich beliebte npm-Bibliothek, die all diese Dinge zu behandeln scheint: https://www.npmjs.com/package/feature-detect-es6

Maciej Krawczyk
quelle
-1

Fügen Sie den inkompatiblen Syntaxcode, z. B. mit Pfeilfunktionen, in einen eigenen Skriptblock ein und füllen Sie ihn mit kompatiblem Syntaxcode.

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