Alguém pode explicar esse truque “duplo negativo”?

Não sou de forma alguma um especialista em Javascript, mas tenho lido a página da Web “Dive into HTML5” de Mark Pilgrim e ele mencionou algo que eu gostaria de ter uma melhor compreensão.

Ele afirma:

Finalmente, você usa o truque duplo negativo para forçar o resultado a um valor booleano (verdadeiro ou falso).

function supports_canvas() { return !!document.createElement('canvas').getContext; } 

Se alguém puder explicar isso um pouco melhor eu agradeceria!

Um operador NÃO lógico ! converte um valor em um booleano que é o oposto de seu valor lógico.

O segundo ! converte o resultado booleano anterior de volta para a representação booleana de seu valor lógico original.

Destes docs para o operador Logical NOT:

Retorna false se seu único operando puder ser convertido para true; Caso contrário, retorna verdadeiro.

Então, se getContext lhe der um valor “falsey”, o !! fará com que retorne o valor booleano false . Caso contrário, retornará true .

Os valores “falsey” são:

  • false
  • NaN
  • undefined
  • null
  • "" (string vazia)
  • 0

O Javascript tem um conjunto confuso de regras para o que é considerado “verdadeiro” e “falso” quando colocado em um contexto onde um booleano é esperado. Mas o operador lógico-NÃO ! , sempre produz um valor booleano adequado (uma das constantes true e false ). Ao encadear dois deles, a expressão idiomática produz um booleano adequado com a mesma autenticidade da expressão original.

Por que você se incomodaria? Porque faz funções como a que você mostra mais previsível. Se não tivesse o duplo negativo, poderia retornar undefined , um object Function ou algo não totalmente diferente de um object Function . Se o chamador dessa function fizer algo estranho com o valor de retorno, o código geral poderá se comportar mal (“estranho” aqui significa “qualquer coisa, exceto uma operação que imponha o contexto booleano”). O idioma negativo duplo impede isso.

Em javascript, usar o operador “bang” (!) Retornará true se o valor fornecido for true, 1, not null, etc. Ele retornará false se o valor for undefined, null, 0 ou uma string vazia.

Assim, o operador bang sempre retornará um valor booleano, mas representará o valor oposto do que você começou. Se você pegar o resultado dessa operação e “bater” de novo, você pode inverter novamente, mas ainda assim acabar com um booleano (e não indefinido, nulo, etc).

Usar o estrondo duas vezes levará um valor que poderia ter sido indefinido, nulo, etc e torná-lo simplesmente false . Vai demorar um valor que poderia ter sido 1, “true”, etc e torná-lo simplesmente true .

O código poderia ter sido escrito:

 var context = document.createElement('canvas').getContext; var contextDoesNotExist = !context; var contextExists = !contextDoesNotExist; return contextExists; 

Usando !! variável dá-lhe uma garantia de typecast para booleano.

Para dar um exemplo simples:

 "" == false (is true) "" === false (is false) !!"" == false (is true) !!"" === false (is true) 

Mas não faz sentido usar se você está fazendo algo como:

 var a = ""; // or a = null; or a = undefined ... if(!!a){ ... 

O if irá convertê-lo em booleano, então não há necessidade de fazer o double negativo negativo implícito.

! lança “alguma coisa” / “qualquer coisa” para um boolean .

!! retorna o valor booleano original (e garante que a expressão seja booleana agora, independentemente do que foi antes)

O primeiro ! coage a variável para um tipo booleano e a inverte. O segundo ! inverte-o novamente (dando-lhe o valor booleano original (correto) para o que você está verificando).

Para maior clareza, seria melhor usar

 return Boolean(....); 

document.createElement('canvas').getContext pode ser avaliado como undefined ou como uma referência de object. !undefined rende true ![some_object] resulta em false . Isso é quase o que precisamos, apenas invertido. Então !! serve para converter undefined para false e uma referência de object para true .

Tem a ver com a digitação fraca do JavaScript. document.createElement('canvas').getContext é um object de function. Ao prefixar um single ! ele avalia como uma expressão booleana e inverte a resposta. Ao preceder outro ! , inverte a resposta. O resultado final é que a function a avalia como uma expressão booleana, mas retorna um resultado booleano real em vez do próprio object da function. Preendendo !! é uma maneira rápida e suja de converter uma expressão em um tipo booleano.

Se document.createElement('canvas').getContext não for undefined ou null , retornará true . Caso contrário, retornará false .