2011-01-13 26 views
178

No soy de ninguna manera un experto en Javascript, pero he estado leyendo la página web de Mark Pilgrim "Dive into HTML5" y mencionó algo que me gustaría conocer mejor.¿Alguien puede explicar este truco 'doble negativo'?

Él afirma:

Por último, se utiliza el truco de doble negativo para forzar el resultado a un valor booleano (verdadero o falso).

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

Si alguien puede explicar esto un poco mejor lo agradecería!

Respuesta

232

Un operador lógico NOT ! convierte un valor a un valor booleano que es lo contrario de su valor lógico.

El segundo ! convierte el resultado booleano anterior a la representación booleana de su valor lógico original.

From these docs para el operador lógico NO:

devuelve false si su único operando se puede convertir en verdad; de lo contrario, devuelve verdadero.

Así que si getContext le da un valor "Falsey-", el !! hará que sea devuelve el valor booleano false. De lo contrario, devolverá true.

Los valores "Falsey" son:

  • false
  • NaN
  • undefined
  • null
  • "" (cadena vacía)
  • 0
+0

¡Ja! Inteligente ... ¡No tenía idea! –

+19

+1 para enumerar todos los valores de "falsey". – Grinn

+3

Qué lenguaje cojo: (... Gracias por la explicación. – Den

12

En javascript, usar el operador "bang" (!) Devolverá verdadero si el valor dado es verdadero, 1, no nulo, etc. Devolverá falso si el valor es indefinido, nulo, 0 o un valor vacío cuerda.

Por lo tanto, el operador de bang siempre devolverá un valor booleano, pero representará el valor opuesto al que comenzó. Si toma el resultado de esa operación y la "golpea" de nuevo, puede revertirla nuevamente, pero aún así terminar con un booleano (y no indefinido, nulo, etc.).

Al usar la explosión dos veces tomará un valor que podría haber sido indefinido, nulo, etc., y lo hará simplemente false. Tomará un valor que podría haber sido 1, "verdadero", etc. y simplemente lo escriba true.

El código podría haber sido escrita:

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

+1 para el operador 'bang' – qwertymk

2

Si no es document.createElement('canvas').getContextundefined o null, volverá true. De lo contrario, devolverá false.

4

La primera ! coacciona la variable a un tipo booleano y la invierte. El segundo ! lo invierte de nuevo (proporcionándole el valor booleano original (correcto) para lo que esté comprobando).

Para mayor claridad que sería mejor usar

return Boolean(....); 
+5

'Boolean()' crea un booleano encuadrado, que no se comporta del mismo modo que los booleanos primitivos creados por '!!' (por ejemplo, 'typeof' informará' 'object'). Por lo tanto, se prefiere '!!'. – zwol

+1

Para hacer que regrese primitivo booleano: 'return (new Boolean (...)). ValueOf()' – serg

+3

@Zack No es para mí. Solo funciona cuando se combina con 'nuevo'. – alex

6

! arroja "algo"/"nada" a un boolean.

!! da el valor booleano original de nuevo (y garantiza la expresión es un valor lógico ahora, independientemente de lo que era antes)

3

Tiene que ver con la tipificación débil de JavaScript. document.createElement('canvas').getContext es un objeto de función. Al anteponer un solo ! lo evalúa como una expresión booleana y voltea la respuesta. Al anteponer otro !, voltea la respuesta. El resultado final es que la función lo evalúa como una expresión booleana, pero devuelve un resultado booleano real en lugar del objeto función mismo. El antecedente !! es una forma rápida y sucia de convertir una expresión a un tipo booleano.

26

Javascript tiene un conjunto confuso de reglas para lo que se considera "verdadero" y "falso" cuando se coloca en un contexto donde se espera un booleano. Pero el operador lógico-NOT, !, siempre produce un valor booleano adecuado (una de las constantes true y false). Al encadenar dos de ellos, la expresión !!expression produce un booleano adecuado con la misma veracidad que la expresión original.

¿Por qué te molestaría? Porque hace que funciones como la que demuestre sean más predecibles. Si no tenía el doble negativo allí, podría devolver undefined, un objeto Function, o algo no muy diferente de un objeto Function. Si la persona que llama de esta función hace algo extraño con el valor de retorno, el código general podría portarse mal ("raro" aquí significa "cualquier cosa menos una operación que aplica el contexto booleano"). El idioma doblemente negativo lo impide.

+3

No es un conjunto de reglas "confusas". – Chris

+7

@ Abody97 La lista (amablemente mostrada arriba) no es tan corta como sea posible ('false'; cualquier otra cosa requiere un operador de comparación explícito), ni el mayor tiempo posible (agregue' {} 'y' [] ', al menos) . Entonces debes memorizar la lista en lugar de una regla. Eso es lo que llamo una característica de lenguaje confuso. – zwol

+0

Ciertamente: es una lista, no una regla. Creo que es altamente subjetivo si es confuso o no. Personalmente, me resulta muy intuitivo saber qué es "falsey" y qué es "verídico" cuando se lo envía a un booleano. – Chris

4

document.createElement('canvas').getContext pueden evaluarse como undefined o una referencia de objeto. !undefined produce true, ![some_object] produce false. Esto es casi lo que necesitamos, solo invertido. Entonces, !! sirve para convertir undefined en false y una referencia de objeto en true.

8

Usando !! variable le ofrece la garantía de typecast a boolean.

Para dar un ejemplo sencillo:

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

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

Pero no tiene sentido utilizar si se está haciendo algo como:

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

El caso se echó a booleano por lo que hay no es necesario hacer el reparto doble negativo implícito.

Cuestiones relacionadas