¿Qué hace el signo de exclamación antes de la función?

Respuestas:

2118

Sintaxis de JavaScript 101. Aquí hay una declaración de función :

function foo() {}

Tenga en cuenta que no hay punto y coma: esto es solo una declaración de función . Necesitaría una invocación,, foo()para ejecutar realmente la función.

Ahora, cuando agregamos el signo de exclamación aparentemente inocuo: !function foo() {}lo convierte en una expresión . Ahora es una expresión de función .

El !solo no invoca la función, por supuesto, pero ahora podemos poner ()al final: !function foo() {}()que tiene mayor precedencia que !e instantáneamente llama a la función.

Entonces, lo que está haciendo el autor es guardar un byte por expresión de función; una forma más legible de escribir sería esta:

(function(){})();

Por último, !hace que la expresión vuelva verdadera. Esto se debe a que, por defecto, todos los IIFE devuelven undefined, lo que nos deja con lo !undefinedque es true. No particularmente útil.

Neil
fuente
229
+1. Esta es realmente la mejor respuesta aquí, y lamentablemente, apenas votó. Obviamente, !devuelve boolean, todos lo sabemos, pero el gran punto que destaca es que también convierte la declaración de declaración de función en una expresión de función para que la función se pueda invocar inmediatamente sin ponerla entre paréntesis. No es obvio, y claramente la intención del codificador.
gilly3
64
+1 Esta es la única respuesta que realmente aborda POR QUÉ desea hacer esto, y por qué uno ve que se usa más de lo que la negación del resultado de devolución parece justificar. El operador unario! (también ~, - y +) desambigua de una declaración de función y permite que los padres al final () invoquen la función en el lugar. Esto a menudo se hace para crear un ámbito local / espacio de nombres para variables al escribir código modular.
Tom Auger
65
¡Otro beneficio es eso! provoca una inserción de punto y coma, por lo que es imposible que esta versión se concatene incorrectamente con un archivo que no termina con un;. Si tiene el formulario (), lo consideraría una llamada de función de lo que se definió en el archivo anterior. Punta del sombrero a un compañero de trabajo mío.
Jure Triglav
55
@Carnix var foo =rompe la ambigüedad de la declaración / expresión y simplemente puede escribir, var foo = function(bar){}("baz");etc.
Neil
66
Esto generalmente se realiza mediante scripts de minificación / uglificación, donde cada byte cuenta.
Dima Slivin
367

La función:

function () {}

no devuelve nada (o indefinido).

A veces queremos llamar a una función correctamente a medida que la creamos. Puede sentirse tentado a intentar esto:

function () {}()

pero resulta en a SyntaxError.

El uso del !operador antes de la función hace que se trate como una expresión, por lo que podemos llamarlo:

!function () {}()

Esto también devolverá el opuesto booleano del valor de retorno de la función, en este caso true, porque !undefinedes true. Si desea que el valor de retorno real sea el resultado de la llamada, intente hacerlo de esta manera:

(function () {})()
Michael Burr
fuente
28
¿Quién puede necesitar esto?
Andrey
13
Esta es la única respuesta que explica el caso en la pregunta, ¡bravo!
Andrey
14
Su segundo ejemplo de código no es JavaScript válido. El propósito de la !es convertir la declaración de función en una expresión de función, eso es todo.
Skilldrick
8
@Andrey El bootstrap twitter usa esto en todos sus archivos de plugin javascript (jQuery). Agregar este comentario en caso de que otros también tengan la misma pregunta.
Anmol Saraf
2
d3.js también usa la !functionsintaxis
Kristian
65

Hay un buen punto para usar la !invocación de funciones marcada en la guía JavaScript de airbnb

Generalmente es una idea para usar esta técnica en archivos separados (también conocidos como módulos) que luego se concatenan. La advertencia aquí es que los archivos deben ser concatenados por herramientas que colocan el nuevo archivo en la nueva línea (que de todos modos es un comportamiento común para la mayoría de las herramientas de concat). En ese caso, el uso !ayudará a evitar errores si el módulo concatenado anteriormente omitió el punto y coma final y, sin embargo, eso le dará la flexibilidad para colocarlos en cualquier orden sin preocupaciones.

!function abc(){}();
!function bca(){}();

Funcionará igual que

!function abc(){}();
(function bca(){})();

pero guarda un personaje y arbitrario se ve mejor.

Y, por cierto cualquiera de +, -, ~, voidlos operadores tienen el mismo efecto, en cuanto a la invocación de la función, a ciencia cierta si tiene que usar algo para volver de esa función que actuarían de manera diferente.

abcval = !function abc(){return true;}() // abcval equals false
bcaval = +function bca(){return true;}() // bcaval equals 1
zyxval = -function zyx(){return true;}() // zyxval equals -1
xyzval = ~function xyz(){return true;}() // your guess?

pero si usa patrones IIFE para una separación de código de módulo de un archivo y usa la herramienta concat para la optimización (que hace que una línea sea un trabajo de archivo), entonces construcción

!function abc(/*no returns*/) {}()
+function bca() {/*no returns*/}()

Hará una ejecución de código segura, igual que una primera muestra de código.

Este arrojará un error porque JavaScript ASI no podrá hacer su trabajo.

!function abc(/*no returns*/) {}()
(function bca() {/*no returns*/})()

Una nota con respecto a los operadores unarios, harían un trabajo similar, pero solo en caso de que no lo usaran en el primer módulo. Por lo tanto, no son tan seguros si no tiene control total sobre la orden de concatenación.

Esto funciona:

!function abc(/*no returns*/) {}()
^function bca() {/*no returns*/}()

Esto no:

^function abc(/*no returns*/) {}()
!function bca() {/*no returns*/}()
dmi3y
fuente
3
En realidad, esos otros símbolos no tienen el mismo efecto. Sí, le permiten llamar a una función como se describe, pero no son idénticas. Considere: var foo =! Function (bar) {console.debug (bar); }("murciélago"); No importa cuál de tus símbolos pongas delante, obtienes "murciélago" en tu consola. Ahora, agregue console.debug ("foo:", foo); - obtienes resultados muy diferentes según el símbolo que uses. ! fuerza un valor de retorno que no siempre es deseable. Prefiero la sintaxis ({}) () para mayor claridad y precisión.
Carnix
29

Devuelve si la declaración puede evaluar a falso. p.ej:

!false      // true
!true       // false
!isValid()  // is not valid

Puede usarlo dos veces para forzar un valor a booleano:

!!1    // true
!!0    // false

Entonces, para responder más directamente a su pregunta:

var myVar = !function(){ return false; }();  // myVar contains true

Editar: tiene el efecto secundario de cambiar la declaración de función a una expresión de función. Por ejemplo, el siguiente código no es válido porque se interpreta como una declaración de función a la que le falta el identificador requerido (o el nombre de la función ):

function () { return false; }();  // syntax error
gilly3
fuente
66
En aras de la claridad para los lectores que deseen utilizar una tarea con una función invocada de inmediato, su código de ejemplo var myVar = !function(){ return false; }()podría omitir el me !gusta var myVar = function(){ return false; }()y la función se ejecutará correctamente y el valor de retorno no se verá afectado.
Mark Fox
1
Para que quede claro, puede usarlo una vez para obligar a Boolean, porque es un operador lógico no . ! 0 = verdadero y! 1 = falso. Para fines de minificación de JavaScript, querrá reemplazar truecon !0y falsecon !1. Ahorra 2 o 3 caracteres.
Triynko
9

Es solo para guardar un byte de datos cuando hacemos la minificación de javascript.

considere la siguiente función anónima

function (){}

Para hacer lo anterior como función de auto invocación, generalmente cambiaremos el código anterior como

(function (){}())

Ahora agregamos dos caracteres adicionales (,)además de agregar ()al final de la función lo que es necesario para llamar a la función. En el proceso de minificación generalmente nos enfocamos en reducir el tamaño del archivo. Entonces también podemos escribir la función anterior como

!function (){}()

Aún así, ambas son funciones de invocación automática y también guardamos un byte. En lugar de 2 caracteres (,), solo usamos un personaje!

Varatharaj
fuente
1
Esto es útil porque a menudo lo verás en mins js
para el nombre del
5

! es un operador NOT lógico , es un operador booleano que invertirá algo en su opuesto.

Aunque puede omitir los paréntesis de la función invocada utilizando BANG (!) Antes de la función, todavía invertirá el retorno, que podría no ser lo que deseaba. Como en el caso de un IEFE, volvería indefinido , que cuando se invierte se convierte en el verdadero booleano.

En su lugar, use el paréntesis de cierre y el BANG ( ! ) Si es necesario.

// I'm going to leave the closing () in all examples as invoking the function with just ! and () takes away from what's happening.

(function(){ return false; }());
=> false

!(function(){ return false; }());
=> true

!!(function(){ return false; }());
=> false

!!!(function(){ return false; }());
=> true

Otros operadores que trabajan ...

+(function(){ return false; }());
=> 0

-(function(){ return false; }());
=> -0

~(function(){ return false; }());
=> -1

Operadores combinados ...

+!(function(){ return false; }());
=> 1

-!(function(){ return false; }());
=> -1

!+(function(){ return false; }());
=> true

!-(function(){ return false; }());
=> true

~!(function(){ return false; }());
=> -2

~!!(function(){ return false; }());
=> -1

+~(function(){ return false; }());
+> -1
SoEzPz
fuente
5

El signo de exclamación hace que cualquier función siempre devuelva un valor booleano.
El valor final es la negación del valor devuelto por la función.

!function bool() { return false; }() // true
!function bool() { return true; }() // false

Omitir !en los ejemplos anteriores sería un SyntaxError .

function bool() { return true; }() // SyntaxError

Sin embargo, una mejor manera de lograr esto sería:

(function bool() { return true; })() // true
oozzal
fuente
Esto es incorrecto. !cambia la forma en que el tiempo de ejecución analiza la función. Hace que el tiempo de ejecución trate la función como una expresión de función (y no una declaración). Hace esto para permitir que el desarrollador invoque inmediatamente la función utilizando la ()sintaxis. !también se aplicará (es decir, negación) al resultado de invocar la expresión de función.
Ben Aston el
3

Es otra forma de escribir IIFE (expresión de función invocada inmediatamente).

Su otra forma de escribir:

(function( args ) {})()

igual que

!function ( args ) {}();
kamal
fuente
Bueno, no es exactamente lo mismo; la segunda forma niega el resultado de la llamada a la función (y luego lo descarta, porque no hay asignación de valor). Prefiero estrictamente la (function (args) {...})()sintaxis más explícita y dejar ese !functionformulario a las herramientas de minificación y ofuscación.
Tobias
-1

! negará (opuesto) lo que sea que esté esperando como resultado, es decir, si tiene

var boy = true;
undefined
boy
true
!boy
false

cuando llame boy, su resultado será true, pero en el momento en que agregue el !al llamar boy, es decir !boy, su resultado será false. En otras palabras, te refieres a NotBoy , pero esta vez es básicamente un resultado booleano, ya sea trueo false.

Eso es lo mismo que le sucede a la !function () {}();expresión, la ejecución solo le function () {}();indicará un error, pero si se agrega !justo delante de su function () {}();expresión, es lo opuesto a lo function () {}();que debería devolverle true. El ejemplo se puede ver a continuación:

function () {}();
SyntaxError: function statement requires a name
!function () {}();
true
antzshrek
fuente