Created
January 24, 2013 22:56
-
-
Save cemo/4629138 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If you don't care about the return value of the IIFE, it could also be any of the following: | |
!function(){}(); // => true | |
~function(){}(); // => -1 | |
+function(){}(); // => NaN | |
-function(){}(); // => NaN | |
Let's explore this a bit more. | |
// module1.js | |
(function() { | |
console.log('module1'); | |
})(); | |
// module2.js | |
(function() { | |
console.log('module2'); | |
})(); | |
// production.min.js | |
(function(){console.log('module1');})();(function(){console.log('module2');})(); | |
// => module1 | |
// => module2 | |
// crockford production.min.js | |
(function(){console.log('module1');}());(function(){console.log('module2');}()); | |
// => module1 | |
// => module2 | |
Both work the same. It starts to get interesting when one of the modules is missing a trailing semicolon: | |
// problem | |
(function(){console.log('module1');})()(function(){console.log('module2');})(); | |
// => module1 | |
// => TypeError: undefined is not a function | |
// crockford problem | |
(function(){console.log('module1');}())(function(){console.log('module2');}()); | |
// => module1 | |
// => module2 | |
// => TypeError: undefined is not a function | |
With a missing semicolon, each set of parens is trying to immediately-invoke the preceding expression. That would be the return value of the preceding IIFE. | |
(function(){ return undefined; })()(); | |
// => TypeError: undefined is not a function | |
// equivalent | |
(undefined)(); | |
(function(){ return undefined; }())(); // crockford | |
// => TypeError: undefined is not a function | |
// equivalent | |
(undefined()); | |
So the difference is when the TypeError happens. Let's check out what the arguments are up to. Note that console.log() returns undefined: | |
// new module1.js | |
(function() { | |
console.log('module1'); | |
// return a function that logs it's | |
// arguments when invoked | |
return function(){ | |
console.log(arguments); | |
}; | |
})() | |
// new production.js | |
// unminified problem for readability | |
(function() { | |
console.log('module1'); | |
// return a function that logs it's | |
// arguments when invoked | |
return function(){ | |
console.log(arguments); | |
}; | |
})()(function() { | |
console.log('module2'); | |
})(); | |
// => module1 | |
// => [function() { | |
// console.log('module2'); | |
// }] | |
// => TypeError: undefined is not a function | |
// the first step is to invoke the first module | |
(function(){console.log('module1');return function(){console.log(arguments)};})() | |
// => module1 | |
// then it invokes the return value | |
// with module2 as arguments | |
(function(){ console.log(arguments) })(function() {console.log('module2');}); | |
// => [function() { | |
// console.log('module2'); | |
// }] | |
// then it tries to invoke the return value. | |
// console.log returns undefined so: | |
(undefined)(); | |
// => TypeError: undefined is not a function | |
Now let's do that same example with the crockford way: | |
// new crockford module1.js | |
(function(){ | |
console.log('module1'); | |
// return a function that logs it's | |
// arguments when invoked | |
return function(){ | |
console.log(arguments); | |
}; | |
}()) | |
// new production.js | |
// unminified problem for readability | |
(function(){ | |
console.log('module1'); | |
// return a function that logs it's | |
// arguments when invoked | |
return function(){ | |
console.log(arguments); | |
}; | |
}())(function(){ | |
console.log('module2'); | |
}()); | |
// => module1 | |
// => module2 | |
// => [undefined] | |
But wait, there's no TypeError here... | |
// it invokes module1 | |
(function(){ console.log('module1'); return function(){ console.log(arguments); };}()) | |
// => module1 | |
//it invokes module2 | |
(function(){ console.log('module2'); }()); | |
// => module2 | |
// equivalent | |
(function(){ console.log(arguments); })(undefined) | |
// => [undefined] | |
There's no TypeError because of the returned function. The returned function that logs the arguments is then getting invoked with the return value of module2, which is undefined. | |
With that understanding, let's go back to the original example, where there was a TypeError: | |
// crockford problem | |
(function(){console.log('module1');}())(function(){console.log('module2');}()); | |
// => module1 | |
// => module2 | |
// => TypeError: undefined is not a function | |
// it invokes module1 | |
(function(){ console.log('module1'); }()) | |
// => module1 | |
//it invokes module2 | |
(function(){ console.log('module2'); }()); | |
// => module2 | |
// equivalent | |
(undefined(undefined)) | |
// => TypeError: undefined is not a function | |
Conclusion | |
The (function{})(); and (function(){}()); IIFEs can act differently in the missing semicolon situation. | |
Use linter or a tool to make sure modules aren't missing trailing semicolons when working on modules. | |
To be extra safe add a leading semicolon to the IIFE: | |
// module1.js | |
;(function() { | |
console.log('module1'); | |
})() | |
// crockford module1.js | |
;(function(){ | |
console.log('module1'); | |
}()) | |
// module2.js | |
;(function() { | |
console.log('module2'); | |
})(); | |
// crockford module2.js | |
;(function() { | |
console.log('module2'); | |
}()); | |
// production.min.js | |
;(function(){ console.log('module1'); })();(function(){ console.log('module2'); })(); | |
// => module1 | |
// => module2 | |
// crockford production.min.js | |
;(function(){ console.log('module1'); }());(function(){ console.log('module2'); }()); | |
// => module1 | |
// => module2 | |
Hope that helps! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment