Javascript ES6 cross-browser detection

Jak mogę znaleźć wersję silnika Javascript przeglądarki i wsparcie dla ECMAScript 6?

Używam navigator.appVersion tylko po to, aby poznać wersję przeglądarki, ale nie wersję silnika.

Author: Marco Bonelli, 2015-03-14

6 answers

Wykrywanie funkcji

Proponuję użyć wykrywanie funkcji zamiast wykrywać silnik przeglądarki za pomocą metod heurystycznych. Aby to zrobić, możesz po prostu zawinąć kod wewnątrz instrukcji try {..} catch (e) {...} lub użyć instrukcji if (...) .

Na przykład:

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

Dlaczego wykrywanie funkcji jest lepsze niż wykrywanie przeglądarki/silnika?

Istnieje wiele powodów, które sprawiają, że w większości przypadków wykrywanie cech jest najlepsze wariant:

  • Nie musisz polegać na wersji przeglądarki, silniku lub specyfikacjach, ani wykrywać ich za pomocą metod heurystycznych, które są trudne i dość przebiegłe do wdrożenia.

  • Nie wpadniesz w błędy dotyczące wykrywania specyfikacji przeglądarki/silnika.

  • Nie musisz się martwić o funkcje specyficzne dla przeglądarki: na przykład WebKit przeglądarki mają inne specyfikacje niż inne.

  • Możesz być upewnij się, że po wykryciu funkcji będziesz mógł z niej korzystać.

To główne powody, dla których IMHO wykrywanie funkcji jest najlepszym podejściem.

Wykrywanie funkcji + fallback

Podczas stosowania wykrywanie funkcji, dość inteligentny sposób pracy, gdy nie masz pewności, których funkcji możesz/nie możesz użyć, polega na kilku wykrywaniach funkcji i w konsekwencji wycofaniu się do bardziej podstawowych metod (lub nawet utworzeniu tych metod metody od podstaw) w przypadku, gdy funkcje, których chcesz użyć, nie są obsługiwane.

Prosty przykład wykrywania funkcji z funkcją awaryjną Może być zastosowany do funkcji window.requestAnimationFrame, która nie jest obsługiwana przez wszystkie przeglądarki i ma kilka różnych prefiksów w zależności od przeglądarki, nad którą pracujesz. W tym przypadku można łatwo wykryć i fallback w następujący sposób:

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) wykrywanie funkcji

Teraz, przychodząc do prawdziwego problem: jeśli chcesz wykryć obsługę ES6, nie będziesz w stanie zachowywać się tak, jak powiedziałem powyżej, ponieważ odpowiedni zakres funkcji ES6 opiera się na nowych składniach i prywatnych słowach, i rzuci SyntaxError jeśli zostanie użyty w ES5, co oznacza, że napisanie skryptu zawierającego zarówno ES5 jak i ES6 jest niemożliwe!

Oto przykład zademonstrowania tego problemu; poniższy fragment nie zadziała i zostanie zablokowany przed wykonaniem, ponieważ zawiera nielegalna składnia.

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

Teraz, ponieważ nie można warunkowo sprawdzić i uruchomić ES6 w tym samym skrypcie, będziesz musiał napisać dwa różne skrypty: jeden, który używa tylko ES5, a drugi, który zawiera funkcje ES6. Za pomocą dwóch różnych skryptów będziesz mógł zaimportować ES6 tylko wtedy, gdy jest on obsługiwany, i bez powodowania SyntaxErrors do wyrzucenia.

ES6 Detekcja i warunkowe wykonanie przykład

Teraz zróbmy bardziej relatable przykład i powiedzmy, że chcesz użyć tych funkcji w swoim skrypcie ES6:

  • nowe Symbol obiekty
  • klasy zbudowane ze słowa kluczowego class
  • arrow ((...)=>{...}) functions

Uwaga: wykrywanie funkcji nowo wprowadzonych składni (Jak funkcje strzałek) można wykonać tylko za pomocą funkcji eval() lub innych odpowiedników (np. Function()), ponieważ zapis nieprawidłowej składni zatrzyma skrypt przed jego uruchomieniem egzekucja. Jest to również powód, dla którego nie można używać instrukcji if do wykrywania klas i funkcji strzałek: te funkcje dotyczą słów kluczowych i składni, więc eval(...) zawinięty w try {...} catch (e) {...} blok będzie działał poprawnie.

Więc, przechodząc do prawdziwego kodu:

  • Znaczniki HTML:

    <html>
        <head>
            <script src="es5script.js"></script>
        </head>
        <body>
            <!-- ... -->
        </body>
    </html>
    
  • Kod w skrypcie 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 :(
    }
    
  • Kod w Twoim 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!
    

Wykrywanie przeglądarki/silnika

Jak Ja powiedziane powyżej, wykrywanie przeglądarki i silnika nie są najlepszymi praktykami podczas programowania jakiegoś skryptu JavaScript. Dam ci trochę informacji na ten temat, tylko nie zostawiaj moich słów jako "przypadkowej osobistej opinii".

Cytowanie z dokumentacji MDN [link]:

Rozważając użycie ciągu user agent do wykrywania używanej przeglądarki, pierwszym krokiem jest próba uniknięcia tego, jeśli to możliwe. Zacznij od określenia, dlaczego chcesz to zrobić to.

[...] próbujesz sprawdzić, czy istnieje konkretna cecha? Twoja witryna musi korzystać z określonej funkcji sieciowej, której niektóre przeglądarki jeszcze nie obsługują, i chcesz wysłać tych użytkowników do starszej witryny z mniejszą liczbą funkcji, ale o której wiesz, że będzie działać. Jest to najgorszy powód, aby użyć wykrywania agenta użytkownika, ponieważ szanse są ostatecznie wszystkie inne przeglądarki dogoni. Należy dołożyć wszelkich starań, aby uniknąć korzystania z User agent sniffing w tym scenariuszu, i zamiast tego wykonaj detekcję funkcji .

Mówisz także, że używasz navigator.appVersion, ale rozważ użycie innego podejścia, ponieważ to, wraz z wieloma innymi właściwościami nawigatora, jest przestarzałe i nie zawsze zachowuje się tak, jak myślisz.

Tak, cytując z dokumentacji MDN [link] ponownie:

Deprecated: ta funkcja została usunięta ze standardów sieciowych. Chociaż niektóre przeglądarki mogą nadal go obsługiwać, jest w trakcie zrzucania. Nie używaj go w starych lub nowych projektach. Strony lub aplikacje internetowe z niego korzystające mogą ulec awarii w dowolnym momencie.

Uwaga: nie polegaj na tej właściwości, aby zwrócić poprawną wersję przeglądarki. W przeglądarkach opartych na Gecko (takich jak Firefox) i WebKit (takich jak Chrome i Safari) zwracana wartość zaczyna się od "5.0", a następnie informacji o platformie. W Operze 10 i nowszej zwrócona Wersja również nie pasuje do rzeczywistej wersji przeglądarki.

 88
Author: Marco Bonelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-12 22:37:00

Dostawcy przeglądarek obsługujących Moduły ES6 zapewniają teraz łatwy sposób wykrywania funkcji:

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

Skrypt z atrybutem nomodule nie będzie usuwany przez przeglądarki obsługujące <script type="module" ...>

Możesz również wprowadzić skrypt w następujący sposób:

const script = document.createElement('script');
script.setAttribute('nomodule', '');
script.innerHTML = 'window.nomodules = true;';
document.head.insertBefore(script, document.head.firstChild);
script.remove();
 4
Author: Casey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-30 20:57:17

Jak powiedział Marco Bonelli, najlepszym sposobem na wykrycie składni języka ECMAScript 6 jest użycie eval (); . Jeśli wywołanie nie spowoduje błędu, "wszystkie inne" funkcje są obsługiwane, ale polecam Function (); .

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

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

 3
Author: Martin Wantke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-30 22:41:57
  1. Wykryj devicePixelRatio, które jest specjalną właściwością w WebKit .
  2. wykrywa implementację funkcji 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);
  }

})()
 3
Author: Andy Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 02:34:11

Na razie nie ma dokładnego sposobu na wykrycie ES6, ale jeśli przetestujesz jego funkcje w bieżącej przeglądarce, możesz określić, czy silnik to ES6. Moja biblioteka esx wykrywa wersję ECMAScript, wykonując testy składni i sprawdzając metody. Dla know it can detect ECMAScript 3, 5, 6 and 7 (ES7 not tested, but should work), if no ECMAScript test matched, it gives null as result.

Przykład użycia mojej biblioteki:

if (esx.detectVersion() >= 6) {
    /* We're in ES6 or above */
}
 1
Author: hydroper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-23 14:42:03

Umieść niezgodny kod składni, np. zawierający funkcje strzałek, we własnym bloku skryptów i uzupełnij go zgodnym kodem składni.

<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>
 -1
Author: NOYB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-29 10:27:49