JavaScript Bang "!" Functions vs Leading Semi-Colon ";" IIFEs
Asked Answered
D

3

4

Airbnd suggests I do this:

!function() {
  // ...
}();

Because:

This ensures that if a malformed module forgets to include a final semicolon there aren't errors in production when the scripts get concatenated.

The bang allows me to work around the language's grammar rules:

// Evaluated in Chromium 34 console.
function(){}(); // => SyntaxError: Unexpected token (
!function(){}(); // => true

And when concatenating other modules the bang seems to do the trick:

!function(){}();function(){}(); // => SyntaxError: Unexpected token (
!function(){}();!function(){}(); // => true
(function(){}());!function(){}(); // => true

However it doesn't seem to actually be "safe," because if someone else doesn't have a semi-colon at the end of his script:

!function(){}()!function(){}(); // => SyntaxError: Unexpected token !
(function(){}())!function(){}(); // => SyntaxError: Unexpected token !

It would appear that a leading semi-colon IIFE is better.

;(function() {
  // ...
}());

!function(){}();(function(){}()); // => undefined
(function(){}());(function(){}()); // => undefined
!function(){}();;(function(){}()); // => undefined
(function(){}());;(function(){}()); // => undefined

Am I missing something? Is it actually acceptable to use bang "!" functions or are leading semi-colon ";" IIFEs truly superior because of the way they concatenate?

Darnley answered 5/6, 2014 at 18:29 Comment(0)
M
8

You always have IEFEs there. Whether you wrap them in parenthesis or prefix them with a ! is your choice, and doesn't make a difference. You need either one to force the function to be parsed as an expression. See javascript function leading bang ! syntax for details.

Whether you prefix that whole construct with a ; to prevent errors from concatenation with poor-written scripts (What does the leading semicolon in JavaScript libraries do?) is totally unrelated. You can mix the patterns as you want:

 !function(){…}() // not safe for arbitrary concatenation
 (function(){…}()) // not safe for arbitrary concatenation either
;!function(){…}()
;(function(){…}())

However, there is one case of concatenation where () vs ! does make a difference: if two scripts are concatenated so that there is a newline in between, and the former does not end in a semicolon. This does allow automatic semicolon insertion to jump in - when the next line does begin with a bang!

1 + 2             // script A
!function(){…}(); // script B
// works!

1 + 2              // script A
(function(){…}()); // script B
// runtime error: "2 is not a function" (or whatever the previous line ends in)

We learn: Always end your script with a semicolon. Use intelligent concatenation. Start your script with a semicolon if you need to be safe against dumb concatenation.

Monopolize answered 5/6, 2014 at 19:11 Comment(0)
A
4

The ! has actually nothing to with protecting against missing semicolons when concatenating files. It's often used to enforce that the following function definition is evaluated as expression, not as declaration.

A leading semicolon on the other hand ends any "open" expression statement that might come before it.

But if you follow the explanation, it seems like ! would suffice if it's the first character in the line. Apparently JS would add a semicolon to the previous due to ASI. However, that seems to be a more fragile approach, since you might not be in total control over how the modules are concatenated. Using a semicolon is definitely "safer".

Axolotl answered 5/6, 2014 at 18:38 Comment(0)
D
-2

Actually this works too:

;!function() {
  // ...
}();

!function(){}()!function(){}(); // => SyntaxError: Unexpected token !
!function(){}();!function(){}(); // => true
(function(){}());!function(){}(); // => true
!function(){}();;!function(){}(); // => true
(function(){}());;!function(){}(); // => true

So IIFEs are no better than bang functions, as I absentmindedly-assumed in the last paragraph of my question. Regardless of whether you use a bang function or an IIFE, a leading semi-colon before them is a good defensive technique.

Darnley answered 5/6, 2014 at 18:49 Comment(2)
IIFE's are "no better than bang functions..." Huh? IIFE's and "bang functions" aren't really related. IIFE's are a construct that prevents polluting the global scope. You can place a ! in front of a function declaration to make it get evaluated as a function expression... Also what the heck does "a leading semi colon is the dealbreaker" mean?Egwin
1. In the last paragraph of my question I made the erroneous assumption that one could only prepend an IIFE with a semi-colon. However I realized afterward that I could just as easily prepend a bang function with one. 2. In this case either a bang function OR an IIFE could be used to define a module. 3. Without a leading semi-colon you would get a syntax error. I will update this post to clarify that.Darnley

© 2022 - 2024 — McMap. All rights reserved.