Detecção de navegador cruzado do Javascript ES6

Como posso descobrir a versão do mecanismo Javascript do navegador e dar suporte ao ECMAScript 6?

Estou usando navigator.appVersion apenas para saber a versão do navegador, mas não a versão do mecanismo.

Detecção de resources

Eu sugiro que você use a detecção de resources em vez de detectar o mecanismo do navegador com methods heurísticos. Para fazer isso, você pode simplesmente include algum código dentro de uma instrução try {..} catch (e) {...} , ou usar algumas instruções if (...) .

Por exemplo:

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

Por que a detecção de resources é melhor do que a detecção de navegador / mecanismo?

Há vários motivos que fazem, na maioria dos casos, a detecção de resources a melhor opção:

  • Você não precisa depender da versão, do mecanismo ou das especificações do navegador, nem de detectá-los usando methods heurísticos que são difíceis de serem implementados.

  • Você não terá erros com relação à detecção de especificações do navegador / mecanismo.

  • Você não precisa se preocupar com resources específicos do navegador: por exemplo, os navegadores WebKit têm especificações diferentes das outras.

  • Você pode ter certeza de que, quando um recurso for detectado, você poderá usá-lo.

Estas são as principais razões pelas quais a IMHO faz a detecção de características a melhor abordagem.

Detecção de resources + fallback

Ao usar a detecção de resources , uma maneira bastante inteligente de trabalhar quando você não sabe quais resources você pode / não pode usar consiste em várias detecções de resources e consequentes fallbacks em methods mais básicos (ou mesmo na criação desses methods). você deseja usar não são suportados.

Um exemplo simples de detecção de recurso com fallback pode ser aplicado ao recurso window.requestAnimationFrame , que não é suportado por todos os navegadores e tem vários prefixos diferentes, dependendo do navegador em que você está trabalhando. Nesse caso, você pode facilmente detectar e fazer fallback assim:

 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) apresenta detecção

Agora, chegando ao verdadeiro problema : se você quiser detectar o suporte ao ES6, você não será capaz de se comportar como eu disse acima, porque um intervalo relevante de resources do ES6 é baseado em novas syntaxs e palavras privadas, e lançará um SyntaxError se usado em ES5 , o que significa que escrever um script que contenha ES5 e ES6 é impossível!

Aqui está um exemplo para demonstrar esse problema; o trecho abaixo não funcionará e será bloqueado antes da execução porque contém uma syntax ilegal.

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

Agora, desde que você não pode verificar e executar o ES6 condicionalmente no mesmo script, você terá que escrever dois scripts diferentes : um que use somente o ES5 e outro que inclua resources do ES6. Com dois scripts diferentes, você poderá importar o ES6 apenas se ele for suportado e sem causar o SyntaxErrors de SyntaxErrors .

Exemplo de detecção ES6 e execução condicional

Agora vamos fazer um exemplo mais relacionado, e digamos que você queira usar esses resources em seu script ES6:

  • Os novos objects Symbol
  • Classes construídas com a palavra-chave class
  • Seta ( (...)=>{...} ) funções

NOTA: a detecção de funcionalidades de novas syntaxs introduzidas (como as funções de seta) só pode ser feita usando a function eval() ou outros equivalentes (por exemplo, Function() ), porque a escrita de syntax inválida irá parar o script antes de sua execução. Esta também é a razão pela qual você não pode usar instruções if para detectar classs e funções de seta: esses resources estão relacionados a palavras-chave e syntax, portanto um eval(...) dentro de uma try {...} catch (e) {...} bloco irá funcionar bem.

Então, chegando ao código real:

  • Marcação HTML:

             
  • Código no seu script 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 :( } 
  • Código no seu 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! 

Detecção de navegador / mecanismo

Como eu disse acima, o navegador e a detecção do mecanismo não são as melhores práticas quando se programa algum script JavaScript. Vou te dar um pouco de conhecimento sobre esse assunto, só para não deixar minhas palavras como uma “opinião pessoal aleatória”.

Citando a documentação do MDN [ link ]:

Ao considerar o uso da cadeia do agente do usuário para detectar qual navegador está sendo usado, seu primeiro passo é tentar evitá-lo, se possível. Comece tentando identificar por que você quer fazer isso.

[…] Você está tentando verificar a existência de um recurso específico? Seu site precisa usar um recurso da Web específico que alguns navegadores ainda não suportam e você deseja enviar esses usuários para um site antigo com menos resources, mas que você sabe que funcionará. Esse é o pior motivo para usar a detecção do agente de usuário, porque as probabilidades acabarão sendo alcançadas por todos os outros navegadores. Você deve fazer o possível para evitar o uso do agente de detecção de sniffers nesse cenário e, em vez disso, fazer a detecção de resources .

Além disso, você está dizendo que usa navigator.appVersion , mas considere usar outra abordagem, porque essa, junto com muitas outras propriedades do navegador, é obsoleta e nem sempre se comporta como você pensa.

Então, citando a Documentação do MDN [ link ] novamente:

Descontinuado : esse recurso foi removido dos padrões da Web. Embora alguns navegadores ainda possam suportá-lo, ele está sendo descartado. Não use em projetos antigos ou novos. Páginas ou aplicativos da Web que o usam podem ser interrompidos a qualquer momento.

Nota: Não confie nessa propriedade para retornar a versão correta do navegador. Em navegadores baseados no Gecko (como o Firefox) e em navegadores baseados no WebKit (como Chrome e Safari), o valor retornado começa com “5.0” seguido por informações de plataforma. No Opera 10 e mais recente, a versão retornada também não corresponde à versão real do navegador.

Os fornecedores de navegadores que suportam os módulos ES6 agora fornecem uma maneira fácil de detectar resources:

 ...     ... 

O script com o atributo nomodule não será executado pelos navegadores que suportam

Você também pode injetar o script assim:

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

Como Marco Bonelli disse, a melhor maneira de detectar a syntax da linguagem ECMAScript 6 é usar eval (); . Se a chamada não lançar um erro, todos os outros resources são suportados, mas eu recomendo Function (); .

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

demo: https://jsfiddle.net/uma4Loq7/

  1. Detectar devicePixelRatio que é uma propriedade especial no WebKit .
  2. Detectar o implemento da function 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); } })() 

Por enquanto, não há uma maneira exata de detectar ES6, mas se você testar seus resources no navegador atual, poderá determinar se o mecanismo é ES6. Minha biblioteca esx detecta a versão do ECMAScript fazendo testes de syntax e verificação de methods. Para saber, ele pode detectar ECMAScript 3, 5, 6 e 7 (o ES7 não foi testado, mas deve funcionar), se nenhum teste ECMAScript corresponder, ele fornecerá null como resultado.

Exemplo usando minha biblioteca:

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

Coloque o código de syntax incompatível, como contendo funções de seta, no próprio bloco de script e preencha-o com um código de syntax compatível.