I've been studying some JS libraries lately that were written by people who really knew what they were doing, and I keep seeing this pattern, and I can't find information about it. I read the docs on the .call() method, but it didn't really make sense to me. I'm hoping to get one of those good old classic in-depth SO explanations with examples.
(function(undefined){ /*(insert entire library here)*/ }).call(this);
What is this about? Why is this a good way to write a library?
Note, sometimes the undefined
is omitted, though I have no idea what difference it makes to put it there or not. I don't even know where the arguments are coming from, or who the caller is.
Let's disassemble this piece of code.
First off there is an anonymous function with immediate invocation. It's similar to this:
(function () {/**/}).call(); (new Date()).getTime(); // timestamp since 1970 jan 1 in milliseconds
We don't assign new Date()
to a variable, instead we use it immediately.
Now why use .call
instead of just ()
?
.call
is a method all Functions
have. The first argument is what this
will be bound to, subsequent arguments will be passed as arguments to the function. So:
(function () { console.log(this.foo); // bar }).call({ "foo": "bar" });
This works in conjunction with undefined
(see below).
.call
is the same as .apply
with one minor difference. .apply
only takes 2 arguments, where the 2nd is an array of arguments. This would be similar:
(function () {}).call(this, "foo", "bar"); (function () {}).apply(this, [ "foo", "bar" ]);
A common use of apply is in conjunction with the magic variable arguments
.
(function () { console.log(Array.prototype.slice.call(arguments, 1)); // [ "bar" ] })([ "foo", "bar" ]);
Array.prototype.slice.call(arguments, 1)
may look scary but really it's just arguments.slice(1)
, but arguments
isn't an Array
so it doesn't have a slice
function. We borrow Array
s slice
function and use .call
to set the this
to arguments
. Array.prototype.slice(arguments, 1??)
is incorrect.
Now why is there this
in .call(this)
? this
always points to the context you're in. If you're in an instance of a class it will point to the instance and if you're in the global scope it will point to that. In a browser environment it is also window
.
Why undefined
? Since we did a .call(this)
with no second argument, all arguments to our anonymous function are undefined
. I'm not really sure why you need to make an explicit variable named undefined
there. Maybe this is support for some browsers or some lint tool that likes to see undefined
defined.
Thanks to @TedHopp. undefined
is fickle.
var undefined = "foo"; console.log(undefined); // undefined (function (undefined) { console.log(undefined); // "foo" })("foo");
You can just as easily have:
(function () { /* code here */ }());
This is completely valid and works just the same. There might be some performance or linting benefits to use the form you posted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With