-
-
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.