Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
803 views
in Technique[技术] by (71.8m points)

ecmascript 6 - Javascript ES6 cross-browser detection

How can I find out the browser's Javascript engine version and support to ECMAScript 6?

I'm using navigator.appVersion just to know the browser's version, but not the engine's version.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Feature detection

I suggest you to use feature detection instead 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.

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 WebKit browsers have different specifications than other ones.

  • You can be sure that, once a feature is detected, you'll be able to use it.

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 fallbacks to 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 fallback may be applied to the window.requestAnimationFrame feature, 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 fallback like this:

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

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 SyntaxError if used in ES5, which means that writing a script which contains both ES5 and ES6 is impossible!

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 SyntaxErrors to be thrown.

ES6 detection and conditional execution example

Now let's make a more relatable example, and let's say you want to use these features in your ES6 script:

  • The new Symbol objects
  • Classes built with the class keyword
  • Arrow ((...)=>{...}) functions

NOTE: feature detection of newly introduced syntaxes (like arrow functions) can only be done using the eval() function or 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 if statements 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.

So, coming to the real code:

  • HTML Markup:

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

    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!
    

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

Quoting from the MDN Documentation [link]:

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.

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.

So, quoting from the MDN Documentation [link] again:

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...