-
-
Save cowboy/1213629 to your computer and use it in GitHub Desktop.
| /*! | |
| * "IIFE" - v0.2 - 9/14/2011 | |
| * http://benalman.com/ | |
| * | |
| * Copyright (c) 2011 "Cowboy" Ben Alman | |
| * Dual licensed under the MIT and GPL licenses. | |
| * http://benalman.com/about/license/ | |
| */ | |
| // Usage: | |
| // | |
| // IIFE([value, ...,] function([arg, ...]) { | |
| // // your code goes here | |
| // }); | |
| function IIFE() { | |
| // Because `arguments` is mutable, we can pop its last item off, leaving | |
| // behind the remaining arguments to be passed into the passed function. | |
| // Because `arguments` isn't an array however, Array#pop must be "borrowed" | |
| // from Array.prototype, and executed using call invocation. | |
| // | |
| // To make a long story short: | |
| // | |
| // Invoke the last passed argument (function) in the same context as "IIFE," | |
| // passing in all preceding arguments, and returning its value. | |
| return Array.prototype.pop.call(arguments).apply(this, arguments); | |
| } |
| $(function() { | |
| IIFE(1, 2, function(a, b, c) { | |
| assert(a, 1, "should pass arguments correctly"); | |
| assert(b, 2, "should pass arguments correctly"); | |
| assert(c, undefined, "shouldn't pass the callback"); | |
| }); | |
| var obj = {} | |
| IIFE.call(obj, 1, 2, function(a, b, c) { | |
| assert(this, obj, "should set context arguments correctly"); | |
| assert(a, 1, "should pass arguments correctly"); | |
| assert(b, 2, "should pass arguments correctly"); | |
| assert(c, undefined, "shouldn't pass the callback"); | |
| }); | |
| }); | |
| // My "really simply unit testing framework" (note: requires jQuery) | |
| function assert(actual, expected, message) { | |
| var status = actual === expected | |
| ? "OK" | |
| : "ERROR (got " + actual + ", expected: " + expected + ") " + message; | |
| $("body").append(status + "<br/>"); | |
| } |
| // USAGE: EVERYDAY | |
| // Gross! Those passed values are impossibly far away from the function's | |
| // arguments list. In a large module, it would be very difficult to correlate $ | |
| // and global with jQuery and this, without lots of scrolling or some kind of | |
| // code folding or other magic. | |
| (function($, global) { | |
| console.log($.fn.jquery, global); | |
| }(jQuery, this)); | |
| // With "IIFE," passed values are right next to the function's arguments list, | |
| // which makes the module much easier to understand! Note that the above pattern | |
| // is called IIFE, so I've just made things all the more confusing. Of course, | |
| // since you can use IIFEs and "IIFE" interchangeably, it should be ok. FWIW, | |
| // this article describes the IIFE pattern, so read it if you're confused: | |
| // http://benalman.com/news/2010/11/immediately-invoked-function-expression/ | |
| IIFE(jQuery, this, function($, global) { | |
| console.log($.fn.jquery, global); | |
| }); | |
| // USAGE: YOU CARE ABOUT `THIS` | |
| var obj = {a: 123}; | |
| // Same thing, except we're setting `this` inside the IIFE explicitly. It's | |
| // even more gross than before! | |
| (function($, global) { | |
| console.log(this.a, $.fn.jquery, global); | |
| }.call(obj, jQuery, this)); | |
| // Same thing, except we're setting `this` inside the IIFE explicitly. See that | |
| // everything you care about is all in one place. Awesome! | |
| IIFE.call(obj, jQuery, this, function($, global) { | |
| console.log(this.a, $.fn.jquery, global); | |
| }); |
❤️. Incidentally, you can make this method even smaller, if you'd like.
function IIFE() {
return Array.prototype.pop.call(arguments).apply(this, arguments);
}
I thought of that, but I was afraid of possible cross-browser issues. Are there any?
I don't think so...the arguments object is mutable, even in ECMAScript 5. I'll test it more rigorously, though.
@kitcambridge, I created some basic unit tests which pass in every browser I cared to test (even back to IE6, Safari 3.2.1, Firefox 2.0, Opera 9.63) so I'm going to use your code. Thanks!
[].pop.call(arguments).apply(this, arguments); yum!
Very interesting! :)
Somehow I thought of AMD: group arguments to be passed into the function in an array (much like the moduleids in a require call).
function IIFE (args, func) {
return func.apply(this, args);
}
and then used:
IIFE([jQuery, this], function($, global) {
console.log($.fn.jquery, global);
});
There's a more logical grouping but the end results is not nearly as "scripty" though :D
Note: not to be confused with Immediately Invoked Function Expressions. Well, maybe to be confused with IIFEs. Ok, totally to be confused with IIFEs, because this is a little sugar to make them just a little bit easier to deal with.
Somebody please suggest a name that won't confuse the hell out of people.